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