1 | /* Get file-specific information about a file. Linux version. |
2 | Copyright (C) 1991-2024 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <errno.h> |
20 | #include <mntent.h> |
21 | #include <stdio_ext.h> |
22 | #include <string.h> |
23 | #include <unistd.h> |
24 | #include <sys/sysmacros.h> |
25 | |
26 | #include "pathconf.h" |
27 | #include "linux_fsinfo.h" |
28 | #include <not-cancel.h> |
29 | |
30 | static long int posix_pathconf (const char *file, int name); |
31 | |
32 | /* Define this first, so it can be inlined. */ |
33 | #define __pathconf static posix_pathconf |
34 | #include <sysdeps/posix/pathconf.c> |
35 | |
36 | |
37 | /* Get file-specific information about FILE. */ |
38 | long int |
39 | __pathconf (const char *file, int name) |
40 | { |
41 | struct statfs fsbuf; |
42 | |
43 | switch (name) |
44 | { |
45 | case _PC_LINK_MAX: |
46 | return __statfs_link_max (result: __statfs (file, &fsbuf), fsbuf: &fsbuf, file, fd: -1); |
47 | |
48 | case _PC_FILESIZEBITS: |
49 | return __statfs_filesize_max (result: __statfs (file, &fsbuf), fsbuf: &fsbuf); |
50 | |
51 | case _PC_2_SYMLINKS: |
52 | return __statfs_symlinks (result: __statfs (file, &fsbuf), fsbuf: &fsbuf); |
53 | |
54 | case _PC_CHOWN_RESTRICTED: |
55 | return __statfs_chown_restricted (result: __statfs (file, &fsbuf), fsbuf: &fsbuf); |
56 | |
57 | default: |
58 | return posix_pathconf (path: file, name); |
59 | } |
60 | } |
61 | |
62 | |
63 | static long int |
64 | distinguish_extX (const struct statfs *fsbuf, const char *file, int fd) |
65 | { |
66 | char buf[64]; |
67 | char path[PATH_MAX]; |
68 | struct __stat64_t64 st; |
69 | |
70 | if ((file == NULL ? __fstat64_time64 (fd, &st) |
71 | : __stat64_time64 (file, &st)) != 0) |
72 | /* Strange. The statfd call worked, but stat fails. Default to |
73 | the more pessimistic value. */ |
74 | return EXT2_LINK_MAX; |
75 | |
76 | __snprintf (buf, sizeof (buf), "/sys/dev/block/%u:%u" , |
77 | __gnu_dev_major (dev: st.st_dev), __gnu_dev_minor (dev: st.st_dev)); |
78 | |
79 | ssize_t n = __readlink (path: buf, buf: path, len: sizeof (path)); |
80 | if (n != -1 && n < sizeof (path)) |
81 | { |
82 | path[n] = '\0'; |
83 | char *base = strdupa (__basename (path)); |
84 | __snprintf (path, sizeof (path), "/sys/fs/ext4/%s" , base); |
85 | |
86 | return __access (path, F_OK) == 0 ? EXT4_LINK_MAX : EXT2_LINK_MAX; |
87 | } |
88 | |
89 | /* XXX Is there a better way to distinguish ext2/3 from ext4 than |
90 | iterating over the mounted filesystems and compare the device |
91 | numbers? */ |
92 | FILE *mtab = __setmntent ("/proc/mounts" , "r" ); |
93 | if (mtab == NULL) |
94 | mtab = __setmntent (_PATH_MOUNTED, "r" ); |
95 | |
96 | /* By default be conservative. */ |
97 | long int result = EXT2_LINK_MAX; |
98 | if (mtab != NULL) |
99 | { |
100 | struct mntent mntbuf; |
101 | char tmpbuf[1024]; |
102 | |
103 | /* No locking needed. */ |
104 | (void) __fsetlocking (mtab, FSETLOCKING_BYCALLER); |
105 | |
106 | while (__getmntent_r (mtab, &mntbuf, tmpbuf, sizeof (tmpbuf))) |
107 | { |
108 | if (strcmp (mntbuf.mnt_type, "ext2" ) != 0 |
109 | && strcmp (mntbuf.mnt_type, "ext3" ) != 0 |
110 | && strcmp (mntbuf.mnt_type, "ext4" ) != 0) |
111 | continue; |
112 | |
113 | struct __stat64_t64 fsst; |
114 | if (__stat64_time64 (mntbuf.mnt_dir, &fsst) >= 0 |
115 | && st.st_dev == fsst.st_dev) |
116 | { |
117 | if (strcmp (mntbuf.mnt_type, "ext4" ) == 0) |
118 | result = EXT4_LINK_MAX; |
119 | break; |
120 | } |
121 | } |
122 | |
123 | /* Close the file. */ |
124 | __endmntent (mtab); |
125 | } |
126 | |
127 | return result; |
128 | } |
129 | |
130 | |
131 | /* Used like: return statfs_link_max (__statfs (name, &buf), &buf); */ |
132 | long int |
133 | __statfs_link_max (int result, const struct statfs *fsbuf, const char *file, |
134 | int fd) |
135 | { |
136 | if (result < 0) |
137 | { |
138 | if (errno == ENOSYS) |
139 | /* Not possible, return the default value. */ |
140 | return LINUX_LINK_MAX; |
141 | |
142 | /* Some error occurred. */ |
143 | return -1; |
144 | } |
145 | |
146 | switch (fsbuf->f_type) |
147 | { |
148 | case EXT2_SUPER_MAGIC: |
149 | /* Unfortunately the kernel does not return a different magic number |
150 | for ext4. This would be necessary to easily detect etx4 since it |
151 | has a different LINK_MAX value. Therefore we have to find it out |
152 | the hard way. */ |
153 | return distinguish_extX (fsbuf, file, fd); |
154 | |
155 | case F2FS_SUPER_MAGIC: |
156 | return F2FS_LINK_MAX; |
157 | |
158 | case MINIX_SUPER_MAGIC: |
159 | case MINIX_SUPER_MAGIC2: |
160 | return MINIX_LINK_MAX; |
161 | |
162 | case MINIX2_SUPER_MAGIC: |
163 | case MINIX2_SUPER_MAGIC2: |
164 | return MINIX2_LINK_MAX; |
165 | |
166 | case XENIX_SUPER_MAGIC: |
167 | return XENIX_LINK_MAX; |
168 | |
169 | case SYSV4_SUPER_MAGIC: |
170 | case SYSV2_SUPER_MAGIC: |
171 | return SYSV_LINK_MAX; |
172 | |
173 | case COH_SUPER_MAGIC: |
174 | return COH_LINK_MAX; |
175 | |
176 | case UFS_MAGIC: |
177 | case UFS_CIGAM: |
178 | return UFS_LINK_MAX; |
179 | |
180 | case REISERFS_SUPER_MAGIC: |
181 | return REISERFS_LINK_MAX; |
182 | |
183 | case XFS_SUPER_MAGIC: |
184 | return XFS_LINK_MAX; |
185 | |
186 | case LUSTRE_SUPER_MAGIC: |
187 | return LUSTRE_LINK_MAX; |
188 | |
189 | default: |
190 | return LINUX_LINK_MAX; |
191 | } |
192 | } |
193 | |
194 | |
195 | /* Used like: return statfs_filesize_max (__statfs (name, &buf), &buf); */ |
196 | long int |
197 | __statfs_filesize_max (int result, const struct statfs *fsbuf) |
198 | { |
199 | if (result < 0) |
200 | { |
201 | if (errno == ENOSYS) |
202 | /* Not possible, return the default value. */ |
203 | return 32; |
204 | |
205 | /* Some error occurred. */ |
206 | return -1; |
207 | } |
208 | |
209 | switch (fsbuf->f_type) |
210 | { |
211 | case F2FS_SUPER_MAGIC: |
212 | return 256; |
213 | |
214 | case BTRFS_SUPER_MAGIC: |
215 | return 255; |
216 | |
217 | case EXT2_SUPER_MAGIC: |
218 | case UFS_MAGIC: |
219 | case UFS_CIGAM: |
220 | case REISERFS_SUPER_MAGIC: |
221 | case XFS_SUPER_MAGIC: |
222 | case SMB_SUPER_MAGIC: |
223 | case NTFS_SUPER_MAGIC: |
224 | case UDF_SUPER_MAGIC: |
225 | case JFS_SUPER_MAGIC: |
226 | case VXFS_SUPER_MAGIC: |
227 | case CGROUP_SUPER_MAGIC: |
228 | case LUSTRE_SUPER_MAGIC: |
229 | return 64; |
230 | |
231 | case MSDOS_SUPER_MAGIC: |
232 | case JFFS_SUPER_MAGIC: |
233 | case JFFS2_SUPER_MAGIC: |
234 | case NCP_SUPER_MAGIC: |
235 | case ROMFS_SUPER_MAGIC: |
236 | return 32; |
237 | |
238 | default: |
239 | return 32; |
240 | } |
241 | } |
242 | |
243 | |
244 | /* Used like: return statfs_link_max (__statfs (name, &buf), &buf); */ |
245 | long int |
246 | __statfs_symlinks (int result, const struct statfs *fsbuf) |
247 | { |
248 | if (result < 0) |
249 | { |
250 | if (errno == ENOSYS) |
251 | /* Not possible, return the default value. */ |
252 | return 1; |
253 | |
254 | /* Some error occurred. */ |
255 | return -1; |
256 | } |
257 | |
258 | switch (fsbuf->f_type) |
259 | { |
260 | case ADFS_SUPER_MAGIC: |
261 | case BFS_MAGIC: |
262 | case CRAMFS_MAGIC: |
263 | case DEVPTS_SUPER_MAGIC: |
264 | case EFS_SUPER_MAGIC: |
265 | case EFS_MAGIC: |
266 | case MSDOS_SUPER_MAGIC: |
267 | case NTFS_SUPER_MAGIC: |
268 | case QNX4_SUPER_MAGIC: |
269 | case ROMFS_SUPER_MAGIC: |
270 | /* No symlink support. */ |
271 | return 0; |
272 | |
273 | default: |
274 | return 1; |
275 | } |
276 | } |
277 | |
278 | |
279 | /* Used like: return __statfs_chown_restricted (__statfs (name, &buf), &buf);*/ |
280 | long int |
281 | __statfs_chown_restricted (int result, const struct statfs *fsbuf) |
282 | { |
283 | if (result < 0) |
284 | { |
285 | if (errno == ENOSYS) |
286 | /* Not possible, return the default value. */ |
287 | return 1; |
288 | |
289 | /* Some error occurred. */ |
290 | return -1; |
291 | } |
292 | |
293 | return 1; |
294 | } |
295 | |