Upload Tizen:Base source
[external/eglibc.git] / sysdeps / unix / sysv / linux / internal_statvfs.c
1 /* Copyright (C) 1998-2006, 2010 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     }
119
120   FILE *mtab = __setmntent ("/proc/mounts", "r");
121   if (mtab == NULL)
122     mtab = __setmntent (_PATH_MOUNTED, "r");
123
124   int result = 0;
125   if (mtab != NULL)
126     {
127       bool success = false;
128       struct mntent mntbuf;
129       char tmpbuf[1024];
130
131       /* No locking needed.  */
132       (void) __fsetlocking (mtab, FSETLOCKING_BYCALLER);
133
134     again:
135       while (__getmntent_r (mtab, &mntbuf, tmpbuf, sizeof (tmpbuf)))
136         {
137           /* In a first round we look for a given mount point, if
138              we have a name.  */
139           if (name != NULL && strcmp (name, mntbuf.mnt_dir) != 0)
140             continue;
141           /* We need to look at the entry only if the filesystem
142              name matches.  If we have a filesystem name.  */
143           else if (fsname != NULL
144                    && strcmp (fsname, mntbuf.mnt_type) != 0
145                    && (fsname2 == NULL
146                        || strcmp (fsname2, mntbuf.mnt_type) != 0)
147                    && (fsname3 == NULL
148                        || strcmp (fsname3, mntbuf.mnt_type) != 0))
149             continue;
150
151           /* Find out about the device the current entry is for.  */
152           struct stat64 fsst;
153           if (stat64 (mntbuf.mnt_dir, &fsst) >= 0
154               && st->st_dev == fsst.st_dev)
155             {
156               /* Bingo, we found the entry for the device FD is on.
157                  Now interpret the option string.  */
158               char *cp = mntbuf.mnt_opts;
159               char *opt;
160
161               while ((opt = strsep (&cp, ",")) != NULL)
162                 if (strcmp (opt, "ro") == 0)
163                   result |= ST_RDONLY;
164                 else if (strcmp (opt, "nosuid") == 0)
165                   result |= ST_NOSUID;
166                 else if (strcmp (opt, "noexec") == 0)
167                   result |= ST_NOEXEC;
168                 else if (strcmp (opt, "nodev") == 0)
169                   result |= ST_NODEV;
170                 else if (strcmp (opt, "sync") == 0)
171                   result |= ST_SYNCHRONOUS;
172                 else if (strcmp (opt, "mand") == 0)
173                   result |= ST_MANDLOCK;
174                 else if (strcmp (opt, "noatime") == 0)
175                   result |= ST_NOATIME;
176                 else if (strcmp (opt, "nodiratime") == 0)
177                   result |= ST_NODIRATIME;
178                 else if (strcmp (opt, "relatime") == 0)
179                   result |= ST_RELATIME;
180
181               /* We can stop looking for more entries.  */
182               success = true;
183               break;
184             }
185         }
186       /* Maybe the kernel names for the filesystems changed or the
187          statvfs call got a name which was not the mount point.  Check
188          again, this time without checking for name matches first.  */
189       if (! success && (name != NULL || fsname != NULL))
190         {
191           if (name != NULL)
192             /* Try without a mount point name.  */
193             name = NULL;
194           else
195             {
196               /* Try without a filesystem name.  */
197               assert (fsname != NULL);
198               fsname = fsname2 = fsname3 = NULL;
199             }
200
201           /* It is not strictly allowed to use rewind here.  But
202              this code is part of the implementation so it is
203              acceptable.  */
204           rewind (mtab);
205
206           goto again;
207         }
208
209       /* Close the file.  */
210       __endmntent (mtab);
211     }
212
213   return result;
214 }
215 # endif
216 #else
217 extern int __statvfs_getflags (const char *name, int fstype,
218                                struct stat64 *st);
219 #endif
220
221
222 void
223 INTERNAL_STATVFS (const char *name, struct STATVFS *buf,
224                   struct STATFS *fsbuf, struct stat64 *st)
225 {
226   /* Now fill in the fields we have information for.  */
227   buf->f_bsize = fsbuf->f_bsize;
228   /* Linux has the f_frsize size only in later version of the kernel.
229      If the value is not filled in use f_bsize.  */
230   buf->f_frsize = fsbuf->f_frsize ?: fsbuf->f_bsize;
231   buf->f_blocks = fsbuf->f_blocks;
232   buf->f_bfree = fsbuf->f_bfree;
233   buf->f_bavail = fsbuf->f_bavail;
234   buf->f_files = fsbuf->f_files;
235   buf->f_ffree = fsbuf->f_ffree;
236   if (sizeof (buf->f_fsid) == sizeof (fsbuf->f_fsid))
237     buf->f_fsid = ((fsbuf->f_fsid.__val[0]
238                     & ((1UL << (8 * sizeof (fsbuf->f_fsid.__val[0]))) - 1))
239                    | ((unsigned long int) fsbuf->f_fsid.__val[1]
240                       << (8 * (sizeof (buf->f_fsid)
241                                - sizeof (fsbuf->f_fsid.__val[0])))));
242   else
243     /* We cannot help here.  The statvfs element is not large enough to
244        contain both words of the statfs f_fsid field.  */
245     buf->f_fsid = fsbuf->f_fsid.__val[0];
246 #ifdef _STATVFSBUF_F_UNUSED
247   buf->__f_unused = 0;
248 #endif
249   buf->f_namemax = fsbuf->f_namelen;
250   memset (buf->__f_spare, '\0', sizeof (buf->__f_spare));
251
252   /* What remains to do is to fill the fields f_favail and f_flag.  */
253
254   /* XXX I have no idea how to compute f_favail.  Any idea???  */
255   buf->f_favail = buf->f_ffree;
256
257 #ifndef __ASSUME_STATFS_F_FLAGS
258   if ((fsbuf->f_flags & ST_VALID) == 0)
259     /* Determining the flags is tricky.  We have to read /proc/mounts or
260        the /etc/mtab file and search for the entry which matches the given
261        file.  The way we can test for matching filesystem is using the
262        device number.  */
263     buf->f_flag = __statvfs_getflags (name, fsbuf->f_type, st);
264   else
265 #endif
266     buf->f_flag = fsbuf->f_flags ^ ST_VALID;
267 }