Sync with gnulib 8ae091f7c8cd35cc7dff82c544d04da6fd095e11.
[midnight-commander.git] / src / filemanager / mountlist.c
blob8a0e7c679b05dac9f0b61d42ecab3ab84202c71d
1 /*
2 Return a list of mounted file systems
4 Copyright (C) 1991-2018
5 Free Software Foundation, Inc.
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software: you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation, either version 3 of the License,
12 or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /** \file mountlist.c
24 * \brief Source: list of mounted filesystems
27 #include <config.h>
29 #include <limits.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdint.h> /* SIZE_MAX */
34 #include <sys/types.h>
36 #include <errno.h>
38 /* This header needs to be included before sys/mount.h on *BSD */
39 #ifdef HAVE_SYS_PARAM_H
40 #include <sys/param.h>
41 #endif
43 #if defined STAT_STATVFS || defined STAT_STATVFS64 /* POSIX 1003.1-2001 (and later) with XSI */
44 #include <sys/statvfs.h>
45 #else
46 /* Don't include backward-compatibility files unless they're needed.
47 Eventually we'd like to remove all this cruft. */
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <sys/stat.h>
52 #ifdef MOUNTED_GETFSSTAT /* OSF_1, also (obsolete) Apple Darwin 1.3 */
53 #ifdef HAVE_SYS_UCRED_H
54 #include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
55 NGROUPS is used as an array dimension in ucred.h */
56 #include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
57 #endif
58 #ifdef HAVE_SYS_MOUNT_H
59 #include <sys/mount.h>
60 #endif
61 #ifdef HAVE_SYS_FS_TYPES_H
62 #include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
63 #endif
64 #ifdef HAVE_STRUCT_FSSTAT_F_FSTYPENAME
65 #define FS_TYPE(Ent) ((Ent).f_fstypename)
66 #else
67 #define FS_TYPE(Ent) mnt_names[(Ent).f_type]
68 #endif
69 #endif /* MOUNTED_GETFSSTAT */
70 #endif /* STAT_STATVFS || STAT_STATVFS64 */
72 #ifdef HAVE_SYS_VFS_H
73 #include <sys/vfs.h>
74 #endif
75 #ifdef HAVE_SYS_FS_S5PARAM_H /* Fujitsu UXP/V */
76 #include <sys/fs/s5param.h>
77 #endif
78 #ifdef HAVE_SYS_STATFS_H
79 #include <sys/statfs.h>
80 #endif
82 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
83 also (obsolete) 4.3BSD, SunOS, Dynix */
84 #include <mntent.h>
85 #include <sys/types.h>
86 #ifndef MOUNTED
87 #ifdef _PATH_MOUNTED /* GNU libc */
88 #define MOUNTED _PATH_MOUNTED
89 #endif
90 #ifdef MNT_MNTTAB /* HP-UX. */
91 #define MOUNTED MNT_MNTTAB
92 #endif
93 #ifdef MNTTABNAME /* Dynix. */
94 #define MOUNTED MNTTABNAME
95 #endif
96 #endif
97 #endif
99 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
100 #include <sys/mount.h>
101 #endif
103 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
104 #include <sys/statvfs.h>
105 #endif
107 #ifdef MOUNTED_GETMNT /* (obsolete) Ultrix */
108 #include <sys/mount.h>
109 #include <sys/fs_types.h>
110 #endif
112 #ifdef MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
113 #include <fs_info.h>
114 #include <dirent.h>
115 #endif
117 #ifdef MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
118 #include <mnttab.h>
119 #include <sys/fstyp.h>
120 #include <sys/statfs.h>
121 #endif
123 #ifdef MOUNTED_LISTMNTENT /* (obsolete) Cray UNICOS 9 */
124 #include <mntent.h>
125 #endif
127 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
128 #include <sys/mnttab.h>
129 #endif
131 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
132 #include <sys/mnttab.h>
133 #endif
135 #ifdef MOUNTED_VMOUNT /* AIX */
136 #include <fshelp.h>
137 #include <sys/vfs.h>
138 #endif
140 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
141 #include <sys/statvfs.h>
142 #include <dirent.h>
143 #endif
145 #ifdef HAVE_SYS_MNTENT_H
146 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
147 #include <sys/mntent.h>
148 #endif
150 #ifndef HAVE_HASMNTOPT
151 #define hasmntopt(mnt, opt) ((char *) 0)
152 #endif
154 #undef MNT_IGNORE
155 #ifdef MNTOPT_IGNORE
156 #if defined __sun && defined __SVR4
157 /* Solaris defines hasmntopt(struct mnttab *, char *)
158 while it is otherwise hasmntopt(struct mnttab *, const char *). */
159 #define MNT_IGNORE(M) hasmntopt (M, (char *) MNTOPT_IGNORE)
160 #else
161 #define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
162 #endif
163 #else
164 #define MNT_IGNORE(M) 0
165 #endif
167 #ifdef HAVE_INFOMOUNT_QNX
168 #include <sys/disk.h>
169 #include <sys/fsys.h>
170 #endif
172 #ifdef HAVE_SYS_STATVFS_H /* SVR4. */
173 #include <sys/statvfs.h>
174 #endif
176 #include "lib/global.h"
177 #include "lib/strutil.h" /* str_verscmp() */
178 #include "lib/unixcompat.h" /* makedev */
179 #include "mountlist.h"
181 /*** global variables ****************************************************************************/
183 /*** file scope macro definitions ****************************************************************/
185 #if defined (__QNX__) && !defined(__QNXNTO__) && !defined (HAVE_INFOMOUNT_LIST)
186 #define HAVE_INFOMOUNT_QNX
187 #endif
189 #if defined(HAVE_INFOMOUNT_LIST) || defined(HAVE_INFOMOUNT_QNX)
190 #define HAVE_INFOMOUNT
191 #endif
193 /* The results of opendir() in this file are not used with dirfd and fchdir,
194 therefore save some unnecessary work in fchdir.c. */
195 #undef opendir
196 #undef closedir
198 #define ME_DUMMY_0(Fs_name, Fs_type) \
199 (strcmp (Fs_type, "autofs") == 0 \
200 || strcmp (Fs_type, "proc") == 0 \
201 || strcmp (Fs_type, "subfs") == 0 \
202 /* for Linux 2.6/3.x */ \
203 || strcmp (Fs_type, "debugfs") == 0 \
204 || strcmp (Fs_type, "devpts") == 0 \
205 || strcmp (Fs_type, "fusectl") == 0 \
206 || strcmp (Fs_type, "mqueue") == 0 \
207 || strcmp (Fs_type, "rpc_pipefs") == 0 \
208 || strcmp (Fs_type, "sysfs") == 0 \
209 /* FreeBSD, Linux 2.4 */ \
210 || strcmp (Fs_type, "devfs") == 0 \
211 /* for NetBSD 3.0 */ \
212 || strcmp (Fs_type, "kernfs") == 0 \
213 /* for Irix 6.5 */ \
214 || strcmp (Fs_type, "ignore") == 0)
216 /* Historically, we have marked as "dummy" any file system of type "none",
217 but now that programs like du need to know about bind-mounted directories,
218 we grant an exception to any with "bind" in its list of mount options.
219 I.e., those are *not* dummy entries. */
220 #ifdef MOUNTED_GETMNTENT1
221 #define ME_DUMMY(Fs_name, Fs_type, Bind) \
222 (ME_DUMMY_0 (Fs_name, Fs_type) \
223 || (strcmp (Fs_type, "none") == 0 && !Bind))
224 #else
225 #define ME_DUMMY(Fs_name, Fs_type) \
226 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
227 #endif
229 #ifdef __CYGWIN__
230 #include <windows.h>
231 #define ME_REMOTE me_remote
232 /* All cygwin mount points include ':' or start with '//'; so it
233 requires a native Windows call to determine remote disks. */
234 static int
235 me_remote (char const *fs_name, char const *fs_type)
237 (void) fs_type;
239 if (fs_name[0] && fs_name[1] == ':')
241 char drive[4];
242 sprintf (drive, "%c:\\", fs_name[0]);
243 switch (GetDriveType (drive))
245 case DRIVE_REMOVABLE:
246 case DRIVE_FIXED:
247 case DRIVE_CDROM:
248 case DRIVE_RAMDISK:
249 return 0;
252 return 1;
254 #endif
255 #ifndef ME_REMOTE
256 /* A file system is 'remote' if its Fs_name contains a ':'
257 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
258 or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). */
259 #define ME_REMOTE(Fs_name, Fs_type) \
260 (strchr (Fs_name, ':') != NULL \
261 || ((Fs_name)[0] == '/' \
262 && (Fs_name)[1] == '/' \
263 && (strcmp (Fs_type, "smbfs") == 0 || strcmp (Fs_type, "cifs") == 0)) \
264 || (strcmp("-hosts", Fs_name) == 0))
265 #endif
267 /* Many space usage primitives use all 1 bits to denote a value that is
268 not applicable or unknown. Propagate this information by returning
269 a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
270 is unsigned and narrower than uintmax_t. */
271 #define PROPAGATE_ALL_ONES(x) \
272 ((sizeof (x) < sizeof (uintmax_t) \
273 && (~ (x) == (sizeof (x) < sizeof (int) \
274 ? - (1 << (sizeof (x) * CHAR_BIT)) \
275 : 0))) \
276 ? UINTMAX_MAX : (uintmax_t) (x))
278 /* Extract the top bit of X as an uintmax_t value. */
279 #define EXTRACT_TOP_BIT(x) ((x) & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
281 /* If a value is negative, many space usage primitives store it into an
282 integer variable by assignment, even if the variable's type is unsigned.
283 So, if a space usage variable X's top bit is set, convert X to the
284 uintmax_t value V such that (- (uintmax_t) V) is the negative of
285 the original value. If X's top bit is clear, just yield X.
286 Use PROPAGATE_TOP_BIT if the original value might be negative;
287 otherwise, use PROPAGATE_ALL_ONES. */
288 #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
290 #ifdef STAT_STATVFS
291 #if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
292 /* The FRSIZE fallback is not required in this case. */
293 #undef STAT_STATFS2_FRSIZE
294 #else
295 #include <sys/utsname.h>
296 #include <sys/statfs.h>
297 #define STAT_STATFS2_BSIZE 1
298 #endif
299 #endif
301 /*** file scope type declarations ****************************************************************/
303 /* A mount table entry. */
304 struct mount_entry
306 char *me_devname; /* Device node name, including "/dev/". */
307 char *me_mountdir; /* Mount point directory name. */
308 char *me_mntroot; /* Directory on filesystem of device used
309 as root for the (bind) mount. */
310 char *me_type; /* "nfs", "4.2", etc. */
311 dev_t me_dev; /* Device number of me_mountdir. */
312 unsigned int me_dummy:1; /* Nonzero for dummy file systems. */
313 unsigned int me_remote:1; /* Nonzero for remote fileystems. */
314 unsigned int me_type_malloced:1; /* Nonzero if me_type was malloced. */
317 struct fs_usage
319 uintmax_t fsu_blocksize; /* Size of a block. */
320 uintmax_t fsu_blocks; /* Total blocks. */
321 uintmax_t fsu_bfree; /* Free blocks available to superuser. */
322 uintmax_t fsu_bavail; /* Free blocks available to non-superuser. */
323 int fsu_bavail_top_bit_set; /* 1 if fsu_bavail represents a value < 0. */
324 uintmax_t fsu_files; /* Total file nodes. */
325 uintmax_t fsu_ffree; /* Free file nodes. */
328 /*** file scope variables ************************************************************************/
330 #ifdef HAVE_INFOMOUNT_LIST
331 static GSList *mc_mount_list = NULL;
332 #endif /* HAVE_INFOMOUNT_LIST */
334 /*** file scope functions ************************************************************************/
335 /* --------------------------------------------------------------------------------------------- */
337 #ifdef STAT_STATVFS
338 /* Return true if statvfs works. This is false for statvfs on systems
339 with GNU libc on Linux kernels before 2.6.36, which stats all
340 preceding entries in /proc/mounts; that makes df hang if even one
341 of the corresponding file systems is hard-mounted but not available. */
342 static int
343 statvfs_works (void)
345 #if ! (__linux__ && (__GLIBC__ || __UCLIBC__))
346 return 1;
347 #else
348 static int statvfs_works_cache = -1;
349 struct utsname name;
351 if (statvfs_works_cache < 0)
352 statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
353 return statvfs_works_cache;
354 #endif
356 #endif
358 /* --------------------------------------------------------------------------------------------- */
360 #ifdef HAVE_INFOMOUNT_LIST
361 static void
362 free_mount_entry (struct mount_entry *me)
364 if (me == NULL)
365 return;
366 g_free (me->me_devname);
367 g_free (me->me_mountdir);
368 g_free (me->me_mntroot);
369 if (me->me_type_malloced)
370 g_free (me->me_type);
371 g_free (me);
374 /* --------------------------------------------------------------------------------------------- */
376 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
378 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
379 static char *
380 fstype_to_string (short int t)
382 switch (t)
384 #ifdef MOUNT_PC
385 /* cppcheck-suppress syntaxError */
386 case MOUNT_PC:
387 return "pc";
388 #endif
389 #ifdef MOUNT_MFS
390 /* cppcheck-suppress syntaxError */
391 case MOUNT_MFS:
392 return "mfs";
393 #endif
394 #ifdef MOUNT_LO
395 /* cppcheck-suppress syntaxError */
396 case MOUNT_LO:
397 return "lo";
398 #endif
399 #ifdef MOUNT_TFS
400 /* cppcheck-suppress syntaxError */
401 case MOUNT_TFS:
402 return "tfs";
403 #endif
404 #ifdef MOUNT_TMP
405 /* cppcheck-suppress syntaxError */
406 case MOUNT_TMP:
407 return "tmp";
408 #endif
409 #ifdef MOUNT_UFS
410 /* cppcheck-suppress syntaxError */
411 case MOUNT_UFS:
412 return "ufs";
413 #endif
414 #ifdef MOUNT_NFS
415 /* cppcheck-suppress syntaxError */
416 case MOUNT_NFS:
417 return "nfs";
418 #endif
419 #ifdef MOUNT_MSDOS
420 /* cppcheck-suppress syntaxError */
421 case MOUNT_MSDOS:
422 return "msdos";
423 #endif
424 #ifdef MOUNT_LFS
425 /* cppcheck-suppress syntaxError */
426 case MOUNT_LFS:
427 return "lfs";
428 #endif
429 #ifdef MOUNT_LOFS
430 /* cppcheck-suppress syntaxError */
431 case MOUNT_LOFS:
432 return "lofs";
433 #endif
434 #ifdef MOUNT_FDESC
435 /* cppcheck-suppress syntaxError */
436 case MOUNT_FDESC:
437 return "fdesc";
438 #endif
439 #ifdef MOUNT_PORTAL
440 /* cppcheck-suppress syntaxError */
441 case MOUNT_PORTAL:
442 return "portal";
443 #endif
444 #ifdef MOUNT_NULL
445 /* cppcheck-suppress syntaxError */
446 case MOUNT_NULL:
447 return "null";
448 #endif
449 #ifdef MOUNT_UMAP
450 /* cppcheck-suppress syntaxError */
451 case MOUNT_UMAP:
452 return "umap";
453 #endif
454 #ifdef MOUNT_KERNFS
455 /* cppcheck-suppress syntaxError */
456 case MOUNT_KERNFS:
457 return "kernfs";
458 #endif
459 #ifdef MOUNT_PROCFS
460 /* cppcheck-suppress syntaxError */
461 case MOUNT_PROCFS:
462 return "procfs";
463 #endif
464 #ifdef MOUNT_AFS
465 /* cppcheck-suppress syntaxError */
466 case MOUNT_AFS:
467 return "afs";
468 #endif
469 #ifdef MOUNT_CD9660
470 /* cppcheck-suppress syntaxError */
471 case MOUNT_CD9660:
472 return "cd9660";
473 #endif
474 #ifdef MOUNT_UNION
475 /* cppcheck-suppress syntaxError */
476 case MOUNT_UNION:
477 return "union";
478 #endif
479 #ifdef MOUNT_DEVFS
480 /* cppcheck-suppress syntaxError */
481 case MOUNT_DEVFS:
482 return "devfs";
483 #endif
484 #ifdef MOUNT_EXT2FS
485 /* cppcheck-suppress syntaxError */
486 case MOUNT_EXT2FS:
487 return "ext2fs";
488 #endif
489 default:
490 return "?";
493 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
495 /* --------------------------------------------------------------------------------------------- */
497 static char *
498 fsp_to_string (const struct statfs *fsp)
500 #ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
501 return (char *) (fsp->f_fstypename);
502 #else
503 return fstype_to_string (fsp->f_type);
504 #endif
506 #endif /* MOUNTED_GETMNTINFO */
508 /* --------------------------------------------------------------------------------------------- */
510 #ifdef MOUNTED_VMOUNT /* AIX */
511 static char *
512 fstype_to_string (int t)
514 struct vfs_ent *e;
516 e = getvfsbytype (t);
517 if (!e || !e->vfsent_name)
518 return "none";
519 else
520 return e->vfsent_name;
522 #endif /* MOUNTED_VMOUNT */
524 /* --------------------------------------------------------------------------------------------- */
526 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
528 /* Return the device number from MOUNT_OPTIONS, if possible.
529 Otherwise return (dev_t) -1. */
531 /* --------------------------------------------------------------------------------------------- */
533 static dev_t
534 dev_from_mount_options (char const *mount_options)
536 /* GNU/Linux allows file system implementations to define their own
537 meaning for "dev=" mount options, so don't trust the meaning
538 here. */
539 #ifndef __linux__
540 static char const dev_pattern[] = ",dev=";
541 char const *devopt = strstr (mount_options, dev_pattern);
543 if (devopt)
545 char const *optval = devopt + sizeof (dev_pattern) - 1;
546 char *optvalend;
547 unsigned long int dev;
548 errno = 0;
549 dev = strtoul (optval, &optvalend, 16);
550 if (optval != optvalend
551 && (*optvalend == '\0' || *optvalend == ',')
552 && !(dev == ULONG_MAX && errno == ERANGE) && dev == (dev_t) dev)
553 return dev;
555 #endif
557 (void) mount_options;
558 return -1;
561 #endif
563 /* --------------------------------------------------------------------------------------------- */
565 #if defined MOUNTED_GETMNTENT1 && defined __linux__ /* GNU/Linux, Android */
567 /* Unescape the paths in mount tables.
568 STR is updated in place. */
569 static void
570 unescape_tab (char *str)
572 size_t i, j = 0;
573 size_t len;
575 len = strlen (str) + 1;
577 for (i = 0; i < len; i++)
579 if (str[i] == '\\' && (i + 4 < len)
580 && str[i + 1] >= '0' && str[i + 1] <= '3'
581 && str[i + 2] >= '0' && str[i + 2] <= '7' && str[i + 3] >= '0' && str[i + 3] <= '7')
583 str[j++] = (str[i + 1] - '0') * 64 + (str[i + 2] - '0') * 8 + (str[i + 3] - '0');
584 i += 3;
586 else
587 str[j++] = str[i];
590 #endif
592 /* --------------------------------------------------------------------------------------------- */
594 /* Return a list of the currently mounted file systems, or NULL on error.
595 Add each entry to the tail of the list so that they stay in order. */
597 static GSList *
598 read_file_system_list (void)
600 GSList *mount_list = NULL;
601 struct mount_entry *me;
603 #ifdef MOUNTED_LISTMNTENT /* (obsolete) Cray UNICOS 9 */
605 struct tabmntent *mntlist, *p;
607 /* the third and fourth arguments could be used to filter mounts,
608 but Crays doesn't seem to have any mounts that we want to
609 remove. Specifically, automount create normal NFS mounts.
612 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
613 return NULL;
614 for (p = mntlist; p; p = p->next)
616 struct mntent *mnt = p->ment;
618 me = g_malloc (sizeof (*me));
619 me->me_devname = g_strdup (mnt->mnt_fsname);
620 me->me_mountdir = g_strdup (mnt->mnt_dir);
621 me->me_mntroot = NULL;
622 me->me_type = g_strdup (mnt->mnt_type);
623 me->me_type_malloced = 1;
624 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
625 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
626 me->me_dev = -1;
628 mount_list = g_slist_prepend (mount_list, me);
630 freemntlist (mntlist);
632 #endif
634 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
635 also (obsolete) 4.3BSD, SunOS, Dynix */
637 FILE *fp;
639 #ifdef __linux__
640 /* Try parsing mountinfo first, as that make device IDs available.
641 Note we could use libmount routines to simplify this parsing a little
642 (and that code is in previous versions of this function), however
643 libmount depends on libselinux which pulls in many dependencies. */
644 char const *mountinfo = "/proc/self/mountinfo";
646 fp = fopen (mountinfo, "r");
647 if (fp != NULL)
649 char *line = NULL;
650 size_t buf_size = 0;
652 while (getline (&line, &buf_size, fp) != -1)
654 unsigned int devmaj, devmin;
655 int target_s, target_e, type_s, type_e;
656 int source_s, source_e, mntroot_s, mntroot_e;
657 char test;
658 char *dash;
659 int rc;
661 rc = sscanf (line, "%*u " /* id - discarded */
662 "%*u " /* parent - discarded */
663 "%u:%u " /* dev major:minor */
664 "%n%*s%n " /* mountroot */
665 "%n%*s%n" /* target, start and end */
666 "%c", /* more data... */
667 &devmaj, &devmin, &mntroot_s, &mntroot_e, &target_s, &target_e, &test);
669 if (rc != 3 && rc != 7) /* 7 if %n included in count. */
670 continue;
672 /* skip optional fields, terminated by " - " */
673 dash = strstr (line + target_e, " - ");
674 if (dash == NULL)
675 continue;
677 rc = sscanf (dash, " - " /* */
678 "%n%*s%n " /* FS type, start and end */
679 "%n%*s%n " /* source, start and end */
680 "%c", /* more data... */
681 &type_s, &type_e, &source_s, &source_e, &test);
682 if (rc != 1 && rc != 5) /* 5 if %n included in count. */
683 continue;
685 /* manipulate the sub-strings in place. */
686 line[mntroot_e] = '\0';
687 line[target_e] = '\0';
688 dash[type_e] = '\0';
689 dash[source_e] = '\0';
690 unescape_tab (dash + source_s);
691 unescape_tab (line + target_s);
692 unescape_tab (line + mntroot_s);
694 me = g_malloc (sizeof *me);
696 me->me_devname = g_strdup (dash + source_s);
697 me->me_mountdir = g_strdup (line + target_s);
698 me->me_mntroot = g_strdup (line + mntroot_s);
699 me->me_type = g_strdup (dash + type_s);
700 me->me_type_malloced = 1;
701 me->me_dev = makedev (devmaj, devmin);
702 /* we pass "false" for the "Bind" option as that's only
703 significant when the Fs_type is "none" which will not be
704 the case when parsing "/proc/self/mountinfo", and only
705 applies for static /etc/mtab files. */
706 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, FALSE);
707 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
709 mount_list = g_slist_prepend (mount_list, me);
712 free (line);
714 if (ferror (fp) != 0)
716 int saved_errno = errno;
718 fclose (fp);
719 errno = saved_errno;
720 goto free_then_fail;
723 if (fclose (fp) == EOF)
724 goto free_then_fail;
726 else /* fallback to /proc/self/mounts (/etc/mtab). */
727 #endif /* __linux __ */
729 struct mntent *mnt;
730 const char *table = MOUNTED;
732 fp = setmntent (table, "r");
733 if (fp == NULL)
734 return NULL;
736 while ((mnt = getmntent (fp)) != NULL)
738 gboolean bind;
740 bind = hasmntopt (mnt, "bind") != NULL;
742 me = g_malloc (sizeof (*me));
743 me->me_devname = g_strdup (mnt->mnt_fsname);
744 me->me_mountdir = g_strdup (mnt->mnt_dir);
745 me->me_mntroot = NULL;
746 me->me_type = g_strdup (mnt->mnt_type);
747 me->me_type_malloced = 1;
748 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
749 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
750 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
752 mount_list = g_slist_prepend (mount_list, me);
755 if (endmntent (fp) == 0)
756 goto free_then_fail;
759 #endif /* MOUNTED_GETMNTENT1. */
761 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
763 struct statfs *fsp;
764 int entries;
766 entries = getmntinfo (&fsp, MNT_NOWAIT);
767 if (entries < 0)
768 return NULL;
769 for (; entries-- > 0; fsp++)
771 char *fs_type = fsp_to_string (fsp);
773 me = g_malloc (sizeof (*me));
774 me->me_devname = g_strdup (fsp->f_mntfromname);
775 me->me_mountdir = g_strdup (fsp->f_mntonname);
776 me->me_mntroot = NULL;
777 me->me_type = fs_type;
778 me->me_type_malloced = 0;
779 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
780 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
781 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
783 mount_list = g_slist_prepend (mount_list, me);
786 #endif /* MOUNTED_GETMNTINFO */
788 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
790 struct statvfs *fsp;
791 int entries;
793 entries = getmntinfo (&fsp, MNT_NOWAIT);
794 if (entries < 0)
795 return NULL;
796 for (; entries-- > 0; fsp++)
798 me = g_malloc (sizeof (*me));
799 me->me_devname = g_strdup (fsp->f_mntfromname);
800 me->me_mountdir = g_strdup (fsp->f_mntonname);
801 me->me_mntroot = NULL;
802 me->me_type = g_strdup (fsp->f_fstypename);
803 me->me_type_malloced = 1;
804 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
805 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
806 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
808 mount_list = g_slist_prepend (mount_list, me);
811 #endif /* MOUNTED_GETMNTINFO2 */
813 #ifdef MOUNTED_GETMNT /* (obsolete) Ultrix */
815 int offset = 0;
816 int val;
817 struct fs_data fsd;
819 while (TRUE)
821 errno = 0;
822 val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, (char *) NULL);
823 if (val < 0)
824 goto free_then_fail;
825 if (val == 0)
826 break;
828 me = g_malloc (sizeof (*me));
829 me->me_devname = g_strdup (fsd.fd_req.devname);
830 me->me_mountdir = g_strdup (fsd.fd_req.path);
831 me->me_mntroot = NULL;
832 me->me_type = gt_names[fsd.fd_req.fstype];
833 me->me_type_malloced = 0;
834 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
835 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
836 me->me_dev = fsd.fd_req.dev;
838 mount_list = g_slist_prepend (mount_list, me);
841 #endif /* MOUNTED_GETMNT. */
843 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
845 /* The next_dev() and fs_stat_dev() system calls give the list of
846 all file systems, including the information returned by statvfs()
847 (fs type, total blocks, free blocks etc.), but without the mount
848 point. But on BeOS all file systems except / are mounted in the
849 rootfs, directly under /.
850 The directory name of the mount point is often, but not always,
851 identical to the volume name of the device.
852 We therefore get the list of subdirectories of /, and the list
853 of all file systems, and match the two lists. */
855 DIR *dirp;
856 struct rootdir_entry
858 char *name;
859 dev_t dev;
860 ino_t ino;
861 struct rootdir_entry *next;
863 struct rootdir_entry *rootdir_list;
864 struct rootdir_entry **rootdir_tail;
865 int32 pos;
866 dev_t dev;
867 fs_info fi;
869 /* All volumes are mounted in the rootfs, directly under /. */
870 rootdir_list = NULL;
871 rootdir_tail = &rootdir_list;
872 dirp = opendir (PATH_SEP_STR);
873 if (dirp)
875 struct dirent *d;
877 while ((d = readdir (dirp)) != NULL)
879 char *name;
880 struct stat statbuf;
882 if (DIR_IS_DOT (d->d_name))
883 continue;
885 if (DIR_IS_DOTDOT (d->d_name))
886 name = g_strdup (PATH_SEP_STR);
887 else
888 name = g_strconcat (PATH_SEP_STR, d->d_name, (char *) NULL);
890 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
892 struct rootdir_entry *re = g_malloc (sizeof (*re));
893 re->name = name;
894 re->dev = statbuf.st_dev;
895 re->ino = statbuf.st_ino;
897 /* Add to the linked list. */
898 *rootdir_tail = re;
899 rootdir_tail = &re->next;
901 else
902 g_free (name);
904 closedir (dirp);
906 *rootdir_tail = NULL;
908 for (pos = 0; (dev = next_dev (&pos)) >= 0;)
909 if (fs_stat_dev (dev, &fi) >= 0)
911 /* Note: fi.dev == dev. */
912 struct rootdir_entry *re;
914 for (re = rootdir_list; re; re = re->next)
915 if (re->dev == fi.dev && re->ino == fi.root)
916 break;
918 me = g_malloc (sizeof (*me));
919 me->me_devname =
920 g_strdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
921 me->me_mountdir = g_strdup (re != NULL ? re->name : fi.fsh_name);
922 me->me_mntroot = NULL;
923 me->me_type = g_strdup (fi.fsh_name);
924 me->me_type_malloced = 1;
925 me->me_dev = fi.dev;
926 me->me_dummy = 0;
927 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
929 mount_list = g_slist_prepend (mount_list, me);
932 while (rootdir_list != NULL)
934 struct rootdir_entry *re = rootdir_list;
936 rootdir_list = re->next;
937 g_free (re->name);
938 g_free (re);
941 #endif /* MOUNTED_FS_STAT_DEV */
943 #ifdef MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
945 int numsys, counter;
946 size_t bufsize;
947 struct statfs *stats;
949 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
950 if (numsys < 0)
951 return NULL;
952 if (SIZE_MAX / sizeof (*stats) <= numsys)
954 fprintf (stderr, "%s\n", _("Memory exhausted!"));
955 exit (EXIT_FAILURE);
958 bufsize = (1 + numsys) * sizeof (*stats);
959 stats = g_malloc (bufsize);
960 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
962 if (numsys < 0)
964 g_free (stats);
965 return NULL;
968 for (counter = 0; counter < numsys; counter++)
970 me = g_malloc (sizeof (*me));
971 me->me_devname = g_strdup (stats[counter].f_mntfromname);
972 me->me_mountdir = g_strdup (stats[counter].f_mntonname);
973 me->me_mntroot = NULL;
974 me->me_type = g_strdup (FS_TYPE (stats[counter]));
975 me->me_type_malloced = 1;
976 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
977 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
978 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
980 mount_list = g_slist_prepend (mount_list, me);
983 g_free (stats);
985 #endif /* MOUNTED_GETFSSTAT */
987 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
989 struct mnttab mnt;
990 char *table = "/etc/mnttab";
991 FILE *fp;
993 fp = fopen (table, "r");
994 if (fp == NULL)
995 return NULL;
997 while (fread (&mnt, sizeof (mnt), 1, fp) > 0)
999 me = g_malloc (sizeof (*me));
1000 me->me_devname = g_strdup (mnt.mt_dev);
1001 me->me_mountdir = g_strdup (mnt.mt_filsys);
1002 me->me_mntroot = NULL;
1003 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1004 me->me_type = "";
1005 me->me_type_malloced = 0;
1007 struct statfs fsd;
1008 char typebuf[FSTYPSZ];
1010 if (statfs (me->me_mountdir, &fsd, sizeof (fsd), 0) != -1
1011 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
1013 me->me_type = g_strdup (typebuf);
1014 me->me_type_malloced = 1;
1017 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1018 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1020 mount_list = g_slist_prepend (mount_list, me);
1023 if (ferror (fp))
1025 /* The last fread() call must have failed. */
1026 int saved_errno = errno;
1028 fclose (fp);
1029 errno = saved_errno;
1030 goto free_then_fail;
1033 if (fclose (fp) == EOF)
1034 goto free_then_fail;
1036 #endif /* MOUNTED_FREAD_FSTYP */
1038 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
1040 struct extmnttab mnt;
1041 const char *table = MNTTAB;
1042 FILE *fp;
1043 int ret;
1045 /* No locking is needed, because the contents of /etc/mnttab is generated by the kernel. */
1047 errno = 0;
1048 fp = fopen (table, "r");
1049 if (fp == NULL)
1050 ret = errno;
1051 else
1053 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
1055 me = g_malloc (sizeof *me);
1056 me->me_devname = g_strdup (mnt.mnt_special);
1057 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1058 me->me_mntroot = NULL;
1059 me->me_type = g_strdup (mnt.mnt_fstype);
1060 me->me_type_malloced = 1;
1061 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1062 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1063 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
1065 mount_list = g_slist_prepend (mount_list, me);
1068 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1069 /* Here ret = -1 means success, ret >= 0 means failure. */
1072 if (ret >= 0)
1074 errno = ret;
1075 goto free_then_fail;
1078 #endif /* MOUNTED_GETEXTMNTENT */
1080 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
1082 struct mnttab mnt;
1083 const char *table = MNTTAB;
1084 FILE *fp;
1085 int ret;
1086 int lockfd = -1;
1088 #if defined F_RDLCK && defined F_SETLKW
1089 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
1090 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
1091 for this file name, we should use their macro name instead.
1092 (Why not just lock MNTTAB directly? We don't know.) */
1093 #ifndef MNTTAB_LOCK
1094 #define MNTTAB_LOCK "/etc/.mnttab.lock"
1095 #endif
1096 lockfd = open (MNTTAB_LOCK, O_RDONLY);
1097 if (lockfd >= 0)
1099 struct flock flock;
1101 flock.l_type = F_RDLCK;
1102 flock.l_whence = SEEK_SET;
1103 flock.l_start = 0;
1104 flock.l_len = 0;
1105 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
1106 if (errno != EINTR)
1108 int saved_errno = errno;
1109 close (lockfd);
1110 errno = saved_errno;
1111 return NULL;
1114 else if (errno != ENOENT)
1115 return NULL;
1116 #endif
1118 errno = 0;
1119 fp = fopen (table, "r");
1120 if (fp == NULL)
1121 ret = errno;
1122 else
1124 while ((ret = getmntent (fp, &mnt)) == 0)
1126 me = g_malloc (sizeof (*me));
1127 me->me_devname = g_strdup (mnt.mnt_special);
1128 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1129 me->me_mntroot = NULL;
1130 me->me_type = g_strdup (mnt.mnt_fstype);
1131 me->me_type_malloced = 1;
1132 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1133 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1134 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1136 mount_list = g_slist_prepend (mount_list, me);
1139 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1140 /* Here ret = -1 means success, ret >= 0 means failure. */
1143 if (lockfd >= 0 && close (lockfd) != 0)
1144 ret = errno;
1146 if (ret >= 0)
1148 errno = ret;
1149 goto free_then_fail;
1152 #endif /* MOUNTED_GETMNTENT2. */
1154 #ifdef MOUNTED_VMOUNT /* AIX */
1156 int bufsize;
1157 void *entries;
1158 char *thisent;
1159 struct vmount *vmp;
1160 int n_entries;
1161 int i;
1163 /* Ask how many bytes to allocate for the mounted file system info. */
1164 entries = &bufsize;
1165 if (mntctl (MCTL_QUERY, sizeof (bufsize), entries) != 0)
1166 return NULL;
1167 entries = g_malloc (bufsize);
1169 /* Get the list of mounted file systems. */
1170 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
1171 if (n_entries < 0)
1173 int saved_errno = errno;
1175 g_free (entries);
1176 errno = saved_errno;
1177 return NULL;
1180 for (i = 0, thisent = entries; i < n_entries; i++, thisent += vmp->vmt_length)
1182 char *options, *ignore;
1184 vmp = (struct vmount *) thisent;
1185 me = g_malloc (sizeof (*me));
1186 if (vmp->vmt_flags & MNT_REMOTE)
1188 char *host, *dir;
1190 me->me_remote = 1;
1191 /* Prepend the remote dirname. */
1192 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1193 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1194 me->me_devname = g_strconcat (host, ":", dir, (char *) NULL);
1196 else
1198 me->me_remote = 0;
1199 me->me_devname = g_strdup (thisent + vmp->vmt_data[VMT_OBJECT].vmt_off);
1201 me->me_mountdir = g_strdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1202 me->me_mntroot = NULL;
1203 me->me_type = g_strdup (fstype_to_string (vmp->vmt_gfstype));
1204 me->me_type_malloced = 1;
1205 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1206 ignore = strstr (options, "ignore");
1207 me->me_dummy = (ignore
1208 && (ignore == options || ignore[-1] == ',')
1209 && (ignore[sizeof ("ignore") - 1] == ','
1210 || ignore[sizeof ("ignore") - 1] == '\0'));
1211 me->me_dev = (dev_t) (-1); /* vmt_fsid might be the info we want. */
1213 mount_list = g_slist_prepend (mount_list, me);
1215 g_free (entries);
1217 #endif /* MOUNTED_VMOUNT. */
1219 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1221 DIR *dirp = opendir ("/dev/fs");
1222 char node[9 + NAME_MAX];
1224 if (!dirp)
1225 goto free_then_fail;
1227 while (1)
1229 struct statvfs dev;
1230 struct dirent entry;
1231 struct dirent *result;
1233 if (readdir_r (dirp, &entry, &result) || result == NULL)
1234 break;
1236 strcpy (node, "/dev/fs/");
1237 strcat (node, entry.d_name);
1239 if (statvfs (node, &dev) == 0)
1241 me = g_malloc (sizeof *me);
1242 me->me_devname = g_strdup (dev.f_mntfromname);
1243 me->me_mountdir = g_strdup (dev.f_mntonname);
1244 me->me_mntroot = NULL;
1245 me->me_type = g_strdup (dev.f_fstypename);
1246 me->me_type_malloced = 1;
1247 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1248 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1249 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1251 mount_list = g_slist_prepend (mount_list, me);
1254 closedir (dirp);
1256 #endif /* MOUNTED_INTERIX_STATVFS */
1258 return g_slist_reverse (mount_list);
1260 free_then_fail:
1262 int saved_errno = errno;
1264 g_slist_free_full (mount_list, (GDestroyNotify) free_mount_entry);
1266 errno = saved_errno;
1267 return NULL;
1270 #endif /* HAVE_INFOMOUNT_LIST */
1272 /* --------------------------------------------------------------------------------------------- */
1274 #ifdef HAVE_INFOMOUNT_QNX
1276 ** QNX has no [gs]etmnt*(), [gs]etfs*(), or /etc/mnttab, but can do
1277 ** this via the following code.
1278 ** Note that, as this is based on CWD, it only fills one mount_entry
1279 ** structure. See my_statfs() below for the "other side" of this hack.
1282 static GSList *
1283 read_file_system_list (void)
1285 struct _disk_entry de;
1286 struct statfs fs;
1287 int i, fd;
1288 char *tp, dev[_POSIX_NAME_MAX], dir[_POSIX_PATH_MAX];
1289 struct mount_entry *me = NULL;
1290 static GSList *list = NULL;
1292 if (list != NULL)
1294 me = (struct mount_entry *) list->data;
1296 g_free (me->me_devname);
1297 g_free (me->me_mountdir);
1298 g_free (me->me_mntroot);
1299 g_free (me->me_type);
1301 else
1303 me = (struct mount_entry *) g_malloc (sizeof (struct mount_entry));
1304 list = g_slist_prepend (list, me);
1307 if (!getcwd (dir, _POSIX_PATH_MAX))
1308 return (NULL);
1310 fd = open (dir, O_RDONLY);
1311 if (fd == -1)
1312 return (NULL);
1314 i = disk_get_entry (fd, &de);
1316 close (fd);
1318 if (i == -1)
1319 return (NULL);
1321 switch (de.disk_type)
1323 case _UNMOUNTED:
1324 tp = "unmounted";
1325 break;
1326 case _FLOPPY:
1327 tp = "Floppy";
1328 break;
1329 case _HARD:
1330 tp = "Hard";
1331 break;
1332 case _RAMDISK:
1333 tp = "Ram";
1334 break;
1335 case _REMOVABLE:
1336 tp = "Removable";
1337 break;
1338 case _TAPE:
1339 tp = "Tape";
1340 break;
1341 case _CDROM:
1342 tp = "CDROM";
1343 break;
1344 default:
1345 tp = "unknown";
1348 if (fsys_get_mount_dev (dir, &dev) == -1)
1349 return (NULL);
1351 if (fsys_get_mount_pt (dev, &dir) == -1)
1352 return (NULL);
1354 me->me_devname = g_strdup (dev);
1355 me->me_mountdir = g_strdup (dir);
1356 me->me_mntroot = NULL;
1357 me->me_type = g_strdup (tp);
1358 me->me_dev = de.disk_type;
1360 #ifdef DEBUG
1361 fprintf (stderr,
1362 "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1363 de.disk_type, tp, _DRIVER_NAME_LEN, _DRIVER_NAME_LEN, de.driver_name, de.disk_drv);
1364 fprintf (stderr, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev);
1365 fprintf (stderr, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir);
1366 #endif /* DEBUG */
1368 return (list);
1370 #endif /* HAVE_INFOMOUNT_QNX */
1372 /* --------------------------------------------------------------------------------------------- */
1374 #ifdef HAVE_INFOMOUNT
1375 /* Fill in the fields of FSP with information about space usage for
1376 the file system on which FILE resides.
1377 DISK is the device on which FILE is mounted, for space-getting
1378 methods that need to know it.
1379 Return 0 if successful, -1 if not. When returning -1, ensure that
1380 ERRNO is either a system error value, or zero if DISK is NULL
1381 on a system that requires a non-NULL value. */
1382 static int
1383 get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
1385 #ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */
1387 if (statvfs_works ())
1389 struct statvfs vfsd;
1391 if (statvfs (file, &vfsd) < 0)
1392 return -1;
1394 /* f_frsize isn't guaranteed to be supported. */
1395 fsp->fsu_blocksize = (vfsd.f_frsize
1396 ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
1397 : PROPAGATE_ALL_ONES (vfsd.f_bsize));
1399 fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
1400 fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
1401 fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
1402 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
1403 fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
1404 fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
1406 else
1407 #endif
1410 #if defined STAT_STATVFS64 /* AIX */
1412 struct statvfs64 fsd;
1414 if (statvfs64 (file, &fsd) < 0)
1415 return -1;
1417 /* f_frsize isn't guaranteed to be supported. */
1418 /* *INDENT-OFF* */
1419 fsp->fsu_blocksize = fsd.f_frsize
1420 ? PROPAGATE_ALL_ONES (fsd.f_frsize)
1421 : PROPAGATE_ALL_ONES (fsd.f_bsize);
1422 /* *INDENT-ON* */
1424 #elif defined STAT_STATFS2_FS_DATA /* Ultrix */
1426 struct fs_data fsd;
1428 if (statfs (file, &fsd) != 1)
1429 return -1;
1431 fsp->fsu_blocksize = 1024;
1432 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.fd_req.btot);
1433 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.fd_req.bfree);
1434 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.fd_req.bfreen);
1435 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.fd_req.bfreen) != 0;
1436 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.fd_req.gtot);
1437 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.fd_req.gfree);
1439 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1441 struct statfs fsd;
1443 if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
1444 return -1;
1446 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1448 #elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */
1450 struct statfs fsd;
1452 if (statfs (file, &fsd) < 0)
1453 return -1;
1455 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
1457 #elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
1458 Mac OS X < 10.4, FreeBSD < 5.0, \
1459 NetBSD < 3.0, OpenBSD < 4.4 */
1461 struct statfs fsd;
1463 if (statfs (file, &fsd) < 0)
1464 return -1;
1466 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1468 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1470 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1471 struct statfs are truncated to 2GB. These conditions detect that
1472 truncation, presumably without botching the 4.1.1 case, in which
1473 the values are not truncated. The correct counts are stored in
1474 undocumented spare fields. */
1475 if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
1477 fsd.f_blocks = fsd.f_spare[0];
1478 fsd.f_bfree = fsd.f_spare[1];
1479 fsd.f_bavail = fsd.f_spare[2];
1481 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1483 #elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */
1485 struct statfs fsd;
1487 if (statfs (file, &fsd) < 0)
1488 return -1;
1490 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1492 #elif defined STAT_STATFS4 /* SVR3, Dynix, old Irix */
1494 #if !defined _SEQUENT_
1495 #define f_bavail f_bfree
1496 #endif
1498 struct statfs fsd;
1500 if (statfs (file, &fsd, sizeof (fsd), 0) < 0)
1501 return -1;
1503 /* Empirically, the block counts on most SVR3 and SVR3-derived
1504 systems seem to always be in terms of 512-byte blocks,
1505 no matter what value f_bsize has. */
1506 #if defined _CRAY
1507 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1508 #else
1509 fsp->fsu_blocksize = 512;
1510 #endif
1512 #endif
1514 #if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 \
1515 || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE \
1516 || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
1518 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
1519 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
1520 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
1521 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
1522 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
1523 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
1525 #endif
1528 (void) disk; /* avoid argument-unused warning */
1530 return 0;
1532 #endif /* HAVE_INFOMOUNT */
1534 /* --------------------------------------------------------------------------------------------- */
1535 /*** public functions ****************************************************************************/
1536 /* --------------------------------------------------------------------------------------------- */
1538 void
1539 free_my_statfs (void)
1541 #ifdef HAVE_INFOMOUNT_LIST
1542 g_slist_free_full (mc_mount_list, (GDestroyNotify) free_mount_entry);
1543 mc_mount_list = NULL;
1544 #endif /* HAVE_INFOMOUNT_LIST */
1547 /* --------------------------------------------------------------------------------------------- */
1549 void
1550 init_my_statfs (void)
1552 #ifdef HAVE_INFOMOUNT_LIST
1553 free_my_statfs ();
1554 mc_mount_list = read_file_system_list ();
1555 #endif /* HAVE_INFOMOUNT_LIST */
1558 /* --------------------------------------------------------------------------------------------- */
1560 void
1561 my_statfs (struct my_statfs *myfs_stats, const char *path)
1563 #ifdef HAVE_INFOMOUNT_LIST
1564 size_t len = 0;
1565 struct mount_entry *entry = NULL;
1566 GSList *temp;
1567 struct fs_usage fs_use;
1569 for (temp = mc_mount_list; temp != NULL; temp = g_slist_next (temp))
1571 struct mount_entry *me;
1572 size_t i;
1574 me = (struct mount_entry *) temp->data;
1575 i = strlen (me->me_mountdir);
1576 if (i > len && (strncmp (path, me->me_mountdir, i) == 0) &&
1577 (entry == NULL || IS_PATH_SEP (path[i]) || path[i] == '\0'))
1579 len = i;
1580 entry = me;
1584 if (entry != NULL)
1586 memset (&fs_use, 0, sizeof (fs_use));
1587 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1589 myfs_stats->type = entry->me_dev;
1590 myfs_stats->typename = entry->me_type;
1591 myfs_stats->mpoint = entry->me_mountdir;
1592 myfs_stats->mroot = entry->me_mntroot;
1593 myfs_stats->device = entry->me_devname;
1594 myfs_stats->avail =
1595 ((uintmax_t) (getuid ()? fs_use.fsu_bavail : fs_use.fsu_bfree) *
1596 fs_use.fsu_blocksize) >> 10;
1597 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1598 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1599 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1601 else
1602 #endif /* HAVE_INFOMOUNT_LIST */
1604 #ifdef HAVE_INFOMOUNT_QNX
1606 ** This is the "other side" of the hack to read_file_system_list() above.
1607 ** It's not the most efficient approach, but consumes less memory. It
1608 ** also accommodates QNX's ability to mount filesystems on the fly.
1610 struct mount_entry *entry;
1611 struct fs_usage fs_use;
1613 entry = read_file_system_list ();
1614 if (entry != NULL)
1616 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1618 myfs_stats->type = entry->me_dev;
1619 myfs_stats->typename = entry->me_type;
1620 myfs_stats->mpoint = entry->me_mountdir;
1621 myfs_stats->mroot = entry->me_mntroot;
1622 myfs_stats->device = entry->me_devname;
1624 myfs_stats->avail = ((uintmax_t) fs_use.fsu_bfree * fs_use.fsu_blocksize) >> 10;
1625 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1626 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1627 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1629 else
1630 #endif /* HAVE_INFOMOUNT_QNX */
1632 myfs_stats->type = 0;
1633 myfs_stats->mpoint = "unknown";
1634 myfs_stats->device = "unknown";
1635 myfs_stats->avail = 0;
1636 myfs_stats->total = 0;
1637 myfs_stats->nfree = 0;
1638 myfs_stats->nodes = 0;
1642 /* --------------------------------------------------------------------------------------------- */