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