1/* Copyright (C) 1998-2016 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
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 <http://www.gnu.org/licenses/>. */
18
19#include <assert.h>
20#include <errno.h>
21#include <mntent.h>
22#include <paths.h>
23#include <stdbool.h>
24#include <stdio_ext.h>
25#include <string.h>
26#include <sys/mount.h>
27#include <sys/stat.h>
28#include <sys/statfs.h>
29#include <sys/statvfs.h>
30#include "linux_fsinfo.h"
31#include <kernel-features.h>
32
33
34/* Special internal-only bit value. */
35#define ST_VALID 0x0020
36
37
38#ifndef STATFS
39# define STATFS statfs
40# define STATVFS statvfs
41# define INTERNAL_STATVFS __internal_statvfs
42
43
44# ifndef __ASSUME_STATFS_F_FLAGS
45int
46__statvfs_getflags (const char *name, int fstype, int fd)
47{
48 struct stat64 st;
49
50 if ((fd < 0 ? stat64 (name, &st) : fstat64 (fd, &st)) < 0)
51 return 0;
52
53 const char *fsname = NULL;
54 const char *fsname2 = NULL;
55 const char *fsname3 = NULL;
56
57 /* Map the filesystem type we got from the statfs call to a string. */
58 switch (fstype)
59 {
60 case EXT2_SUPER_MAGIC:
61 fsname = "ext4";
62 fsname2 = "ext3";
63 fsname3 = "ext2";
64 break;
65 case DEVPTS_SUPER_MAGIC:
66 fsname= "devpts";
67 break;
68 case SHMFS_SUPER_MAGIC:
69 fsname = "tmpfs";
70 break;
71 case PROC_SUPER_MAGIC:
72 fsname = "proc";
73 break;
74 case USBDEVFS_SUPER_MAGIC:
75 fsname = "usbdevfs";
76 break;
77 case AUTOFS_SUPER_MAGIC:
78 fsname = "autofs";
79 break;
80 case NFS_SUPER_MAGIC:
81 fsname = "nfs";
82 break;
83 case SYSFS_MAGIC:
84 fsname = "sysfs";
85 break;
86 case REISERFS_SUPER_MAGIC:
87 fsname = "reiserfs";
88 break;
89 case XFS_SUPER_MAGIC:
90 fsname = "xfs";
91 break;
92 case JFS_SUPER_MAGIC:
93 fsname = "jfs";
94 break;
95 case HPFS_SUPER_MAGIC:
96 fsname = "hpfs";
97 break;
98 case DEVFS_SUPER_MAGIC:
99 fsname = "devfs";
100 break;
101 case ISOFS_SUPER_MAGIC:
102 fsname = "iso9660";
103 break;
104 case MSDOS_SUPER_MAGIC:
105 fsname = "msdos";
106 break;
107 case NTFS_SUPER_MAGIC:
108 fsname = "ntfs";
109 break;
110 case LOGFS_MAGIC_U32:
111 fsname = "logfs";
112 break;
113 case BTRFS_SUPER_MAGIC:
114 fsname = "btrfs";
115 break;
116 case CGROUP_SUPER_MAGIC:
117 fsname = "cgroup";
118 break;
119 case LUSTRE_SUPER_MAGIC:
120 fsname = "lustre";
121 break;
122 case F2FS_SUPER_MAGIC:
123 fsname = "f2fs";
124 break;
125 case EFIVARFS_MAGIC:
126 fsname = "efivarfs";
127 break;
128 }
129
130 FILE *mtab = __setmntent ("/proc/mounts", "r");
131 if (mtab == NULL)
132 mtab = __setmntent (_PATH_MOUNTED, "r");
133
134 int result = 0;
135 if (mtab != NULL)
136 {
137 bool success = false;
138 struct mntent mntbuf;
139 char tmpbuf[1024];
140
141 /* No locking needed. */
142 (void) __fsetlocking (mtab, FSETLOCKING_BYCALLER);
143
144 again:
145 while (__getmntent_r (mtab, &mntbuf, tmpbuf, sizeof (tmpbuf)))
146 {
147 /* In a first round we look for a given mount point, if
148 we have a name. */
149 if (name != NULL && strcmp (name, mntbuf.mnt_dir) != 0)
150 continue;
151 /* We need to look at the entry only if the filesystem
152 name matches. If we have a filesystem name. */
153 else if (fsname != NULL
154 && strcmp (fsname, mntbuf.mnt_type) != 0
155 && (fsname2 == NULL
156 || strcmp (fsname2, mntbuf.mnt_type) != 0)
157 && (fsname3 == NULL
158 || strcmp (fsname3, mntbuf.mnt_type) != 0))
159 continue;
160
161 /* Find out about the device the current entry is for. */
162 struct stat64 fsst;
163 if (stat64 (mntbuf.mnt_dir, &fsst) >= 0
164 && st.st_dev == fsst.st_dev)
165 {
166 /* Bingo, we found the entry for the device FD is on.
167 Now interpret the option string. */
168 char *cp = mntbuf.mnt_opts;
169 char *opt;
170
171 while ((opt = strsep (&cp, ",")) != NULL)
172 if (strcmp (opt, "ro") == 0)
173 result |= ST_RDONLY;
174 else if (strcmp (opt, "nosuid") == 0)
175 result |= ST_NOSUID;
176 else if (strcmp (opt, "noexec") == 0)
177 result |= ST_NOEXEC;
178 else if (strcmp (opt, "nodev") == 0)
179 result |= ST_NODEV;
180 else if (strcmp (opt, "sync") == 0)
181 result |= ST_SYNCHRONOUS;
182 else if (strcmp (opt, "mand") == 0)
183 result |= ST_MANDLOCK;
184 else if (strcmp (opt, "noatime") == 0)
185 result |= ST_NOATIME;
186 else if (strcmp (opt, "nodiratime") == 0)
187 result |= ST_NODIRATIME;
188 else if (strcmp (opt, "relatime") == 0)
189 result |= ST_RELATIME;
190
191 /* We can stop looking for more entries. */
192 success = true;
193 break;
194 }
195 }
196 /* Maybe the kernel names for the filesystems changed or the
197 statvfs call got a name which was not the mount point. Check
198 again, this time without checking for name matches first. */
199 if (! success && (name != NULL || fsname != NULL))
200 {
201 if (name != NULL)
202 /* Try without a mount point name. */
203 name = NULL;
204 else
205 {
206 /* Try without a filesystem name. */
207 assert (fsname != NULL);
208 fsname = fsname2 = fsname3 = NULL;
209 }
210
211 /* It is not strictly allowed to use rewind here. But
212 this code is part of the implementation so it is
213 acceptable. */
214 rewind (mtab);
215
216 goto again;
217 }
218
219 /* Close the file. */
220 __endmntent (mtab);
221 }
222
223 return result;
224}
225# endif
226#else
227extern int __statvfs_getflags (const char *name, int fstype, int fd);
228#endif
229
230
231void
232INTERNAL_STATVFS (const char *name, struct STATVFS *buf,
233 struct STATFS *fsbuf, int fd)
234{
235 /* Now fill in the fields we have information for. */
236 buf->f_bsize = fsbuf->f_bsize;
237 /* Linux has the f_frsize size only in later version of the kernel.
238 If the value is not filled in use f_bsize. */
239 buf->f_frsize = fsbuf->f_frsize ?: fsbuf->f_bsize;
240 buf->f_blocks = fsbuf->f_blocks;
241 buf->f_bfree = fsbuf->f_bfree;
242 buf->f_bavail = fsbuf->f_bavail;
243 buf->f_files = fsbuf->f_files;
244 buf->f_ffree = fsbuf->f_ffree;
245 if (sizeof (buf->f_fsid) == sizeof (fsbuf->f_fsid))
246 /* The shifting uses 'unsigned long long int' even though the target
247 field might only have 32 bits. This is OK since the 'if' branch
248 is not used in this case but the compiler would still generate
249 warnings. */
250 buf->f_fsid = ((fsbuf->f_fsid.__val[0]
251 & ((1ULL << (8 * sizeof (fsbuf->f_fsid.__val[0]))) - 1))
252 | ((unsigned long long int) fsbuf->f_fsid.__val[1]
253 << (8 * (sizeof (buf->f_fsid)
254 - sizeof (fsbuf->f_fsid.__val[0])))));
255 else
256 /* We cannot help here. The statvfs element is not large enough to
257 contain both words of the statfs f_fsid field. */
258 buf->f_fsid = fsbuf->f_fsid.__val[0];
259#ifdef _STATVFSBUF_F_UNUSED
260 buf->__f_unused = 0;
261#endif
262 buf->f_namemax = fsbuf->f_namelen;
263 memset (buf->__f_spare, '\0', sizeof (buf->__f_spare));
264
265 /* What remains to do is to fill the fields f_favail and f_flag. */
266
267 /* XXX I have no idea how to compute f_favail. Any idea??? */
268 buf->f_favail = buf->f_ffree;
269
270#ifndef __ASSUME_STATFS_F_FLAGS
271 if ((fsbuf->f_flags & ST_VALID) == 0)
272 /* Determining the flags is tricky. We have to read /proc/mounts or
273 the /etc/mtab file and search for the entry which matches the given
274 file. The way we can test for matching filesystem is using the
275 device number. */
276 buf->f_flag = __statvfs_getflags (name, fsbuf->f_type, fd);
277 else
278#endif
279 buf->f_flag = fsbuf->f_flags ^ ST_VALID;
280}
281