1 /* Get file-specific information about a file.  Linux version.
2    Copyright (C) 1991-2022 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
__pathconf(const char * file,int name)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 (__statfs (file, &fsbuf), &fsbuf, file, -1);
47 
48     case _PC_FILESIZEBITS:
49       return __statfs_filesize_max (__statfs (file, &fsbuf), &fsbuf);
50 
51     case _PC_2_SYMLINKS:
52       return __statfs_symlinks (__statfs (file, &fsbuf), &fsbuf);
53 
54     case _PC_CHOWN_RESTRICTED:
55       return __statfs_chown_restricted (__statfs (file, &fsbuf), &fsbuf);
56 
57     default:
58       return posix_pathconf (file, name);
59     }
60 }
61 
62 
63 static long int
distinguish_extX(const struct statfs * fsbuf,const char * file,int fd)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 (st.st_dev), __gnu_dev_minor (st.st_dev));
78 
79   ssize_t n = __readlink (buf, path, 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
__statfs_link_max(int result,const struct statfs * fsbuf,const char * file,int fd)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 occured.  */
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
__statfs_filesize_max(int result,const struct statfs * fsbuf)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 occured.  */
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
__statfs_symlinks(int result,const struct statfs * fsbuf)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 occured.  */
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
__statfs_chown_restricted(int result,const struct statfs * fsbuf)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 occured.  */
290       return -1;
291     }
292 
293   return 1;
294 }
295