Ticket #4536: skins: add root variant of julia256 skin.
[midnight-commander.git] / src / filemanager / mountlist.c
blobbab6aa1f73102fc6471f355693da83d146bb0e89
1 /*
2 Return a list of mounted file systems
4 Copyright (C) 1991-2024
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 */
84 #include <mntent.h>
85 #include <sys/types.h>
86 #if defined __ANDROID__ /* Android */
87 /* Bionic versions from between 2014-01-09 and 2015-01-08 define MOUNTED to
88 an incorrect value; older Bionic versions don't define it at all. */
89 #undef MOUNTED
90 #define MOUNTED "/proc/mounts"
91 #elif !defined MOUNTED
92 #ifdef _PATH_MOUNTED /* GNU libc */
93 #define MOUNTED _PATH_MOUNTED
94 #endif
95 #ifdef MNT_MNTTAB /* HP-UX. */
96 #define MOUNTED MNT_MNTTAB
97 #endif
98 #endif
99 #endif
101 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
102 #include <sys/mount.h>
103 #endif
105 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
106 #include <sys/statvfs.h>
107 #endif
109 #ifdef MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
110 #include <fs_info.h>
111 #include <dirent.h>
112 #endif
114 #ifdef MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
115 #include <mnttab.h>
116 #include <sys/fstyp.h>
117 #include <sys/statfs.h>
118 #endif
120 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
121 #include <sys/mnttab.h>
122 #endif
124 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
125 #include <sys/mnttab.h>
126 #endif
128 #ifdef MOUNTED_VMOUNT /* AIX */
129 #include <fshelp.h>
130 #include <sys/vfs.h>
131 #endif
133 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
134 #include <sys/statvfs.h>
135 #include <dirent.h>
136 #endif
138 #ifdef HAVE_SYS_MNTENT_H
139 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
140 #include <sys/mntent.h>
141 #endif
143 #ifdef MOUNTED_GETMNTENT1
144 #if !HAVE_SETMNTENT /* Android <= 4.4 */
145 #define setmntent(fp,mode) fopen (fp, mode)
146 #endif
147 #if !HAVE_ENDMNTENT /* Android <= 4.4 */
148 #define endmntent(fp) fclose (fp)
149 #endif
150 #endif
152 #ifndef HAVE_HASMNTOPT
153 #define hasmntopt(mnt, opt) ((char *) 0)
154 #endif
156 #undef MNT_IGNORE
157 #ifdef MNTOPT_IGNORE
158 #if defined __sun && defined __SVR4
159 /* Solaris defines hasmntopt(struct mnttab *, char *)
160 while it is otherwise hasmntopt(struct mnttab *, const char *). */
161 #define MNT_IGNORE(M) hasmntopt (M, (char *) MNTOPT_IGNORE)
162 #else
163 #define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
164 #endif
165 #else
166 #define MNT_IGNORE(M) 0
167 #endif
169 #ifdef HAVE_INFOMOUNT_QNX
170 #include <sys/disk.h>
171 #include <sys/fsys.h>
172 #endif
174 #ifdef HAVE_SYS_STATVFS_H /* SVR4. */
175 #include <sys/statvfs.h>
176 #endif
178 #include "lib/global.h"
179 #include "lib/strutil.h" /* str_verscmp() */
180 #include "lib/unixcompat.h" /* makedev */
181 #include "mountlist.h"
183 /*** global variables ****************************************************************************/
185 /*** file scope macro definitions ****************************************************************/
187 #if defined (__QNX__) && !defined(__QNXNTO__) && !defined (HAVE_INFOMOUNT_LIST)
188 #define HAVE_INFOMOUNT_QNX
189 #endif
191 #if defined(HAVE_INFOMOUNT_LIST) || defined(HAVE_INFOMOUNT_QNX)
192 #define HAVE_INFOMOUNT
193 #endif
195 /* The results of opendir() in this file are not used with dirfd and fchdir,
196 therefore save some unnecessary work in fchdir.c. */
197 #undef opendir
198 #undef closedir
200 #define ME_DUMMY_0(Fs_name, Fs_type) \
201 (strcmp (Fs_type, "autofs") == 0 \
202 || strcmp (Fs_type, "proc") == 0 \
203 || strcmp (Fs_type, "subfs") == 0 \
204 /* for Linux 2.6/3.x */ \
205 || strcmp (Fs_type, "debugfs") == 0 \
206 || strcmp (Fs_type, "devpts") == 0 \
207 || strcmp (Fs_type, "fusectl") == 0 \
208 || strcmp (Fs_type, "fuse.portal") == 0 \
209 || strcmp (Fs_type, "mqueue") == 0 \
210 || strcmp (Fs_type, "rpc_pipefs") == 0 \
211 || strcmp (Fs_type, "sysfs") == 0 \
212 /* FreeBSD, Linux 2.4 */ \
213 || strcmp (Fs_type, "devfs") == 0 \
214 /* for NetBSD 3.0 */ \
215 || strcmp (Fs_type, "kernfs") == 0 \
216 /* for Irix 6.5 */ \
217 || strcmp (Fs_type, "ignore") == 0)
219 /* Historically, we have marked as "dummy" any file system of type "none",
220 but now that programs like du need to know about bind-mounted directories,
221 we grant an exception to any with "bind" in its list of mount options.
222 I.e., those are *not* dummy entries. */
223 #ifdef MOUNTED_GETMNTENT1
224 #define ME_DUMMY(Fs_name, Fs_type, Bind) \
225 (ME_DUMMY_0 (Fs_name, Fs_type) \
226 || (strcmp (Fs_type, "none") == 0 && !Bind))
227 #else
228 #define ME_DUMMY(Fs_name, Fs_type) \
229 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
230 #endif
232 #ifdef __CYGWIN__
233 #include <windows.h>
234 #define ME_REMOTE me_remote
235 /* All cygwin mount points include ':' or start with '//'; so it
236 requires a native Windows call to determine remote disks. */
237 static int
238 me_remote (char const *fs_name, char const *fs_type)
240 (void) fs_type;
242 if (fs_name[0] && fs_name[1] == ':')
244 char drive[4];
245 sprintf (drive, "%c:\\", fs_name[0]);
246 switch (GetDriveType (drive))
248 case DRIVE_REMOVABLE:
249 case DRIVE_FIXED:
250 case DRIVE_CDROM:
251 case DRIVE_RAMDISK:
252 return 0;
255 return 1;
257 #endif
258 #ifndef ME_REMOTE
259 /* A file system is 'remote' if its Fs_name contains a ':'
260 or if (it is of type (smbfs or smb3 or cifs) and its Fs_name starts with '//')
261 or if it is of any other of the listed types
262 or Fs_name is equal to "-hosts" (used by autofs to mount remote fs).
263 "VM" file systems like prl_fs or vboxsf are not considered remote here. */
264 #define ME_REMOTE(Fs_name, Fs_type) \
265 (strchr (Fs_name, ':') != NULL \
266 || ((Fs_name)[0] == '/' \
267 && (Fs_name)[1] == '/' \
268 && (strcmp (Fs_type, "smbfs") == 0 \
269 || strcmp (Fs_type, "smb3") == 0 \
270 || strcmp (Fs_type, "cifs") == 0)) \
271 || strcmp (Fs_type, "acfs") == 0 \
272 || strcmp (Fs_type, "afs") == 0 \
273 || strcmp (Fs_type, "coda") == 0 \
274 || strcmp (Fs_type, "auristorfs") == 0 \
275 || strcmp (Fs_type, "fhgfs") == 0 \
276 || strcmp (Fs_type, "gpfs") == 0 \
277 || strcmp (Fs_type, "ibrix") == 0 \
278 || strcmp (Fs_type, "ocfs2") == 0 \
279 || strcmp (Fs_type, "vxfs") == 0 \
280 || strcmp ("-hosts", Fs_name) == 0)
281 #endif
283 /* Many space usage primitives use all 1 bits to denote a value that is
284 not applicable or unknown. Propagate this information by returning
285 a uintmax_t value that is all 1 bits if X is all 1 bits, even if X
286 is unsigned and narrower than uintmax_t. */
287 #define PROPAGATE_ALL_ONES(x) \
288 ((sizeof (x) < sizeof (uintmax_t) \
289 && (~ (x) == (sizeof (x) < sizeof (int) \
290 ? - (1 << (sizeof (x) * CHAR_BIT)) \
291 : 0))) \
292 ? UINTMAX_MAX : (uintmax_t) (x))
294 /* Extract the top bit of X as an uintmax_t value. */
295 #define EXTRACT_TOP_BIT(x) ((x) & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
297 /* If a value is negative, many space usage primitives store it into an
298 integer variable by assignment, even if the variable's type is unsigned.
299 So, if a space usage variable X's top bit is set, convert X to the
300 uintmax_t value V such that (- (uintmax_t) V) is the negative of
301 the original value. If X's top bit is clear, just yield X.
302 Use PROPAGATE_TOP_BIT if the original value might be negative;
303 otherwise, use PROPAGATE_ALL_ONES. */
304 #define PROPAGATE_TOP_BIT(x) ((x) | ~ (EXTRACT_TOP_BIT (x) - 1))
306 #ifdef STAT_STATVFS
307 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
308 /* The FRSIZE fallback is not required in this case. */
309 #undef STAT_STATFS2_FRSIZE
310 #else
311 #include <sys/utsname.h>
312 #include <sys/statfs.h>
313 #define STAT_STATFS2_BSIZE 1
314 #endif
315 #endif
317 /*** file scope type declarations ****************************************************************/
319 /* A mount table entry. */
320 struct mount_entry
322 char *me_devname; /* Device node name, including "/dev/". */
323 char *me_mountdir; /* Mount point directory name. */
324 char *me_mntroot; /* Directory on filesystem of device used
325 as root for the (bind) mount. */
326 char *me_type; /* "nfs", "4.2", etc. */
327 dev_t me_dev; /* Device number of me_mountdir. */
328 unsigned int me_dummy:1; /* Nonzero for dummy file systems. */
329 unsigned int me_remote:1; /* Nonzero for remote filesystems. */
330 unsigned int me_type_malloced:1; /* Nonzero if me_type was malloced. */
333 struct fs_usage
335 uintmax_t fsu_blocksize; /* Size of a block. */
336 uintmax_t fsu_blocks; /* Total blocks. */
337 uintmax_t fsu_bfree; /* Free blocks available to superuser. */
338 uintmax_t fsu_bavail; /* Free blocks available to non-superuser. */
339 int fsu_bavail_top_bit_set; /* 1 if fsu_bavail represents a value < 0. */
340 uintmax_t fsu_files; /* Total file nodes. */
341 uintmax_t fsu_ffree; /* Free file nodes. */
344 /*** forward declarations (file scope functions) *************************************************/
346 /*** file scope variables ************************************************************************/
348 #ifdef HAVE_INFOMOUNT_LIST
349 static GSList *mc_mount_list = NULL;
350 #endif /* HAVE_INFOMOUNT_LIST */
352 /* --------------------------------------------------------------------------------------------- */
353 /*** file scope functions ************************************************************************/
354 /* --------------------------------------------------------------------------------------------- */
356 #ifdef STAT_STATVFS
357 /* Return true if statvfs works. This is false for statvfs on systems
358 with GNU libc on Linux kernels before 2.6.36, which stats all
359 preceding entries in /proc/mounts; that makes df hang if even one
360 of the corresponding file systems is hard-mounted but not available. */
361 static int
362 statvfs_works (void)
364 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
365 return 1;
366 #else
367 static int statvfs_works_cache = -1;
368 struct utsname name;
370 if (statvfs_works_cache < 0)
371 statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
372 return statvfs_works_cache;
373 #endif
375 #endif
377 /* --------------------------------------------------------------------------------------------- */
379 #ifdef HAVE_INFOMOUNT_LIST
380 static void
381 free_mount_entry (struct mount_entry *me)
383 if (me == NULL)
384 return;
385 g_free (me->me_devname);
386 g_free (me->me_mountdir);
387 g_free (me->me_mntroot);
388 if (me->me_type_malloced)
389 g_free (me->me_type);
390 g_free (me);
393 /* --------------------------------------------------------------------------------------------- */
395 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
397 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
398 static char *
399 fstype_to_string (short int t)
401 switch (t)
403 #ifdef MOUNT_PC
404 /* cppcheck-suppress syntaxError */
405 case MOUNT_PC:
406 return "pc";
407 #endif
408 #ifdef MOUNT_MFS
409 /* cppcheck-suppress syntaxError */
410 case MOUNT_MFS:
411 return "mfs";
412 #endif
413 #ifdef MOUNT_LO
414 /* cppcheck-suppress syntaxError */
415 case MOUNT_LO:
416 return "lo";
417 #endif
418 #ifdef MOUNT_TFS
419 /* cppcheck-suppress syntaxError */
420 case MOUNT_TFS:
421 return "tfs";
422 #endif
423 #ifdef MOUNT_TMP
424 /* cppcheck-suppress syntaxError */
425 case MOUNT_TMP:
426 return "tmp";
427 #endif
428 #ifdef MOUNT_UFS
429 /* cppcheck-suppress syntaxError */
430 case MOUNT_UFS:
431 return "ufs";
432 #endif
433 #ifdef MOUNT_NFS
434 /* cppcheck-suppress syntaxError */
435 case MOUNT_NFS:
436 return "nfs";
437 #endif
438 #ifdef MOUNT_MSDOS
439 /* cppcheck-suppress syntaxError */
440 case MOUNT_MSDOS:
441 return "msdos";
442 #endif
443 #ifdef MOUNT_LFS
444 /* cppcheck-suppress syntaxError */
445 case MOUNT_LFS:
446 return "lfs";
447 #endif
448 #ifdef MOUNT_LOFS
449 /* cppcheck-suppress syntaxError */
450 case MOUNT_LOFS:
451 return "lofs";
452 #endif
453 #ifdef MOUNT_FDESC
454 /* cppcheck-suppress syntaxError */
455 case MOUNT_FDESC:
456 return "fdesc";
457 #endif
458 #ifdef MOUNT_PORTAL
459 /* cppcheck-suppress syntaxError */
460 case MOUNT_PORTAL:
461 return "portal";
462 #endif
463 #ifdef MOUNT_NULL
464 /* cppcheck-suppress syntaxError */
465 case MOUNT_NULL:
466 return "null";
467 #endif
468 #ifdef MOUNT_UMAP
469 /* cppcheck-suppress syntaxError */
470 case MOUNT_UMAP:
471 return "umap";
472 #endif
473 #ifdef MOUNT_KERNFS
474 /* cppcheck-suppress syntaxError */
475 case MOUNT_KERNFS:
476 return "kernfs";
477 #endif
478 #ifdef MOUNT_PROCFS
479 /* cppcheck-suppress syntaxError */
480 case MOUNT_PROCFS:
481 return "procfs";
482 #endif
483 #ifdef MOUNT_AFS
484 /* cppcheck-suppress syntaxError */
485 case MOUNT_AFS:
486 return "afs";
487 #endif
488 #ifdef MOUNT_CD9660
489 /* cppcheck-suppress syntaxError */
490 case MOUNT_CD9660:
491 return "cd9660";
492 #endif
493 #ifdef MOUNT_UNION
494 /* cppcheck-suppress syntaxError */
495 case MOUNT_UNION:
496 return "union";
497 #endif
498 #ifdef MOUNT_DEVFS
499 /* cppcheck-suppress syntaxError */
500 case MOUNT_DEVFS:
501 return "devfs";
502 #endif
503 #ifdef MOUNT_EXT2FS
504 /* cppcheck-suppress syntaxError */
505 case MOUNT_EXT2FS:
506 return "ext2fs";
507 #endif
508 default:
509 return "?";
512 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
514 /* --------------------------------------------------------------------------------------------- */
516 static char *
517 fsp_to_string (const struct statfs *fsp)
519 #ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
520 return (char *) (fsp->f_fstypename);
521 #else
522 return fstype_to_string (fsp->f_type);
523 #endif
525 #endif /* MOUNTED_GETMNTINFO */
527 /* --------------------------------------------------------------------------------------------- */
529 #ifdef MOUNTED_VMOUNT /* AIX */
530 static char *
531 fstype_to_string (int t)
533 struct vfs_ent *e;
535 e = getvfsbytype (t);
536 if (!e || !e->vfsent_name)
537 return "none";
538 else
539 return e->vfsent_name;
541 #endif /* MOUNTED_VMOUNT */
543 /* --------------------------------------------------------------------------------------------- */
545 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
547 /* Return the device number from MOUNT_OPTIONS, if possible.
548 Otherwise return (dev_t) -1. */
550 /* --------------------------------------------------------------------------------------------- */
552 static dev_t
553 dev_from_mount_options (char const *mount_options)
555 /* GNU/Linux allows file system implementations to define their own
556 meaning for "dev=" mount options, so don't trust the meaning
557 here. */
558 #ifndef __linux__
559 static char const dev_pattern[] = ",dev=";
560 char const *devopt = strstr (mount_options, dev_pattern);
562 if (devopt)
564 char const *optval = devopt + sizeof (dev_pattern) - 1;
565 char *optvalend;
566 unsigned long int dev;
567 errno = 0;
568 dev = strtoul (optval, &optvalend, 16);
569 if (optval != optvalend
570 && (*optvalend == '\0' || *optvalend == ',')
571 && !(dev == ULONG_MAX && errno == ERANGE) && dev == (dev_t) dev)
572 return dev;
574 #endif
576 (void) mount_options;
577 return -1;
580 #endif
582 /* --------------------------------------------------------------------------------------------- */
584 #if defined MOUNTED_GETMNTENT1 && (defined __linux__ || defined __ANDROID__) /* GNU/Linux, Android */
586 /* Unescape the paths in mount tables.
587 STR is updated in place. */
588 static void
589 unescape_tab (char *str)
591 size_t i, j = 0;
592 size_t len;
594 len = strlen (str) + 1;
596 for (i = 0; i < len; i++)
598 if (str[i] == '\\' && (i + 4 < len)
599 && str[i + 1] >= '0' && str[i + 1] <= '3'
600 && str[i + 2] >= '0' && str[i + 2] <= '7' && str[i + 3] >= '0' && str[i + 3] <= '7')
602 str[j++] = (str[i + 1] - '0') * 64 + (str[i + 2] - '0') * 8 + (str[i + 3] - '0');
603 i += 3;
605 else
606 str[j++] = str[i];
609 #endif
611 /* --------------------------------------------------------------------------------------------- */
613 /* Return a list of the currently mounted file systems, or NULL on error.
614 Add each entry to the tail of the list so that they stay in order. */
616 static GSList *
617 read_file_system_list (void)
619 GSList *mount_list = NULL;
620 struct mount_entry *me;
622 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
623 also (obsolete) 4.3BSD, SunOS */
625 FILE *fp;
627 #if defined __linux__ || defined __ANDROID__
628 /* Try parsing mountinfo first, as that make device IDs available.
629 Note we could use libmount routines to simplify this parsing a little
630 (and that code is in previous versions of this function), however
631 libmount depends on libselinux which pulls in many dependencies. */
632 char const *mountinfo = "/proc/self/mountinfo";
634 fp = fopen (mountinfo, "r");
635 if (fp != NULL)
637 char *line = NULL;
638 size_t buf_size = 0;
640 while (getline (&line, &buf_size, fp) != -1)
642 unsigned int devmaj, devmin;
643 int target_s, target_e, type_s, type_e;
644 int source_s, source_e, mntroot_s, mntroot_e;
645 char test;
646 char *dash;
647 int rc;
649 rc = sscanf (line, "%*u " /* id - discarded */
650 "%*u " /* parent - discarded */
651 "%u:%u " /* dev major:minor */
652 "%n%*s%n " /* mountroot */
653 "%n%*s%n" /* target, start and end */
654 "%c", /* more data... */
655 &devmaj, &devmin, &mntroot_s, &mntroot_e, &target_s, &target_e, &test);
657 if (rc != 3 && rc != 7) /* 7 if %n included in count. */
658 continue;
660 /* skip optional fields, terminated by " - " */
661 dash = strstr (line + target_e, " - ");
662 if (dash == NULL)
663 continue;
665 rc = sscanf (dash, " - " /* */
666 "%n%*s%n " /* FS type, start and end */
667 "%n%*s%n " /* source, start and end */
668 "%c", /* more data... */
669 &type_s, &type_e, &source_s, &source_e, &test);
670 if (rc != 1 && rc != 5) /* 5 if %n included in count. */
671 continue;
673 /* manipulate the sub-strings in place. */
674 line[mntroot_e] = '\0';
675 line[target_e] = '\0';
676 dash[type_e] = '\0';
677 dash[source_e] = '\0';
678 unescape_tab (dash + source_s);
679 unescape_tab (line + target_s);
680 unescape_tab (line + mntroot_s);
682 me = g_malloc (sizeof *me);
684 me->me_devname = g_strdup (dash + source_s);
685 me->me_mountdir = g_strdup (line + target_s);
686 me->me_mntroot = g_strdup (line + mntroot_s);
687 me->me_type = g_strdup (dash + type_s);
688 me->me_type_malloced = 1;
689 me->me_dev = makedev (devmaj, devmin);
690 /* we pass "false" for the "Bind" option as that's only
691 significant when the Fs_type is "none" which will not be
692 the case when parsing "/proc/self/mountinfo", and only
693 applies for static /etc/mtab files. */
694 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, FALSE);
695 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
697 mount_list = g_slist_prepend (mount_list, me);
700 free (line);
702 if (ferror (fp) != 0)
704 int saved_errno = errno;
706 fclose (fp);
707 errno = saved_errno;
708 goto free_then_fail;
711 if (fclose (fp) == EOF)
712 goto free_then_fail;
714 else /* fallback to /proc/self/mounts (/etc/mtab). */
715 #endif /* __linux __ || __ANDROID__ */
717 struct mntent *mnt;
718 const char *table = MOUNTED;
720 fp = setmntent (table, "r");
721 if (fp == NULL)
722 return NULL;
724 while ((mnt = getmntent (fp)) != NULL)
726 gboolean bind;
728 bind = hasmntopt (mnt, "bind") != NULL;
730 me = g_malloc (sizeof (*me));
731 me->me_devname = g_strdup (mnt->mnt_fsname);
732 me->me_mountdir = g_strdup (mnt->mnt_dir);
733 me->me_mntroot = NULL;
734 me->me_type = g_strdup (mnt->mnt_type);
735 me->me_type_malloced = 1;
736 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
737 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
738 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
740 mount_list = g_slist_prepend (mount_list, me);
743 if (endmntent (fp) == 0)
744 goto free_then_fail;
747 #endif /* MOUNTED_GETMNTENT1. */
749 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
751 struct statfs *fsp;
752 int entries;
754 entries = getmntinfo (&fsp, MNT_NOWAIT);
755 if (entries < 0)
756 return NULL;
757 for (; entries-- > 0; fsp++)
759 char *fs_type = fsp_to_string (fsp);
761 me = g_malloc (sizeof (*me));
762 me->me_devname = g_strdup (fsp->f_mntfromname);
763 me->me_mountdir = g_strdup (fsp->f_mntonname);
764 me->me_mntroot = NULL;
765 me->me_type = fs_type;
766 me->me_type_malloced = 0;
767 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
768 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
769 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
771 mount_list = g_slist_prepend (mount_list, me);
774 #endif /* MOUNTED_GETMNTINFO */
776 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
778 struct statvfs *fsp;
779 int entries;
781 entries = getmntinfo (&fsp, MNT_NOWAIT);
782 if (entries < 0)
783 return NULL;
784 for (; entries-- > 0; fsp++)
786 me = g_malloc (sizeof (*me));
787 me->me_devname = g_strdup (fsp->f_mntfromname);
788 me->me_mountdir = g_strdup (fsp->f_mntonname);
789 me->me_mntroot = NULL;
790 me->me_type = g_strdup (fsp->f_fstypename);
791 me->me_type_malloced = 1;
792 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
793 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
794 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
796 mount_list = g_slist_prepend (mount_list, me);
799 #endif /* MOUNTED_GETMNTINFO2 */
801 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
803 /* The next_dev() and fs_stat_dev() system calls give the list of
804 all file systems, including the information returned by statvfs()
805 (fs type, total blocks, free blocks etc.), but without the mount
806 point. But on BeOS all file systems except / are mounted in the
807 rootfs, directly under /.
808 The directory name of the mount point is often, but not always,
809 identical to the volume name of the device.
810 We therefore get the list of subdirectories of /, and the list
811 of all file systems, and match the two lists. */
813 DIR *dirp;
814 struct rootdir_entry
816 char *name;
817 dev_t dev;
818 ino_t ino;
819 struct rootdir_entry *next;
821 struct rootdir_entry *rootdir_list;
822 struct rootdir_entry **rootdir_tail;
823 int32 pos;
824 dev_t dev;
825 fs_info fi;
827 /* All volumes are mounted in the rootfs, directly under /. */
828 rootdir_list = NULL;
829 rootdir_tail = &rootdir_list;
830 dirp = opendir (PATH_SEP_STR);
831 if (dirp)
833 struct dirent *d;
835 while ((d = readdir (dirp)) != NULL)
837 char *name;
838 struct stat statbuf;
840 if (DIR_IS_DOT (d->d_name))
841 continue;
843 if (DIR_IS_DOTDOT (d->d_name))
844 name = g_strdup (PATH_SEP_STR);
845 else
846 name = g_strconcat (PATH_SEP_STR, d->d_name, (char *) NULL);
848 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
850 struct rootdir_entry *re = g_malloc (sizeof (*re));
851 re->name = name;
852 re->dev = statbuf.st_dev;
853 re->ino = statbuf.st_ino;
855 /* Add to the linked list. */
856 *rootdir_tail = re;
857 rootdir_tail = &re->next;
859 else
860 g_free (name);
862 closedir (dirp);
864 *rootdir_tail = NULL;
866 for (pos = 0; (dev = next_dev (&pos)) >= 0;)
867 if (fs_stat_dev (dev, &fi) >= 0)
869 /* Note: fi.dev == dev. */
870 struct rootdir_entry *re;
872 for (re = rootdir_list; re; re = re->next)
873 if (re->dev == fi.dev && re->ino == fi.root)
874 break;
876 me = g_malloc (sizeof (*me));
877 me->me_devname =
878 g_strdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
879 me->me_mountdir = g_strdup (re != NULL ? re->name : fi.fsh_name);
880 me->me_mntroot = NULL;
881 me->me_type = g_strdup (fi.fsh_name);
882 me->me_type_malloced = 1;
883 me->me_dev = fi.dev;
884 me->me_dummy = 0;
885 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
887 mount_list = g_slist_prepend (mount_list, me);
890 while (rootdir_list != NULL)
892 struct rootdir_entry *re = rootdir_list;
894 rootdir_list = re->next;
895 g_free (re->name);
896 g_free (re);
899 #endif /* MOUNTED_FS_STAT_DEV */
901 #ifdef MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
903 int numsys, counter;
904 size_t bufsize;
905 struct statfs *stats;
907 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
908 if (numsys < 0)
909 return NULL;
910 if (SIZE_MAX / sizeof (*stats) <= numsys)
912 fprintf (stderr, "%s\n", _("Memory exhausted!"));
913 exit (EXIT_FAILURE);
916 bufsize = (1 + numsys) * sizeof (*stats);
917 stats = g_malloc (bufsize);
918 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
920 if (numsys < 0)
922 g_free (stats);
923 return NULL;
926 for (counter = 0; counter < numsys; counter++)
928 me = g_malloc (sizeof (*me));
929 me->me_devname = g_strdup (stats[counter].f_mntfromname);
930 me->me_mountdir = g_strdup (stats[counter].f_mntonname);
931 me->me_mntroot = NULL;
932 me->me_type = g_strdup (FS_TYPE (stats[counter]));
933 me->me_type_malloced = 1;
934 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
935 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
936 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
938 mount_list = g_slist_prepend (mount_list, me);
941 g_free (stats);
943 #endif /* MOUNTED_GETFSSTAT */
945 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
947 struct mnttab mnt;
948 char *table = "/etc/mnttab";
949 FILE *fp;
951 fp = fopen (table, "r");
952 if (fp == NULL)
953 return NULL;
955 while (fread (&mnt, sizeof (mnt), 1, fp) > 0)
957 me = g_malloc (sizeof (*me));
958 me->me_devname = g_strdup (mnt.mt_dev);
959 me->me_mountdir = g_strdup (mnt.mt_filsys);
960 me->me_mntroot = NULL;
961 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
962 me->me_type = "";
963 me->me_type_malloced = 0;
965 struct statfs fsd;
966 char typebuf[FSTYPSZ];
968 if (statfs (me->me_mountdir, &fsd, sizeof (fsd), 0) != -1
969 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
971 me->me_type = g_strdup (typebuf);
972 me->me_type_malloced = 1;
975 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
976 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
978 mount_list = g_slist_prepend (mount_list, me);
981 if (ferror (fp))
983 /* The last fread() call must have failed. */
984 int saved_errno = errno;
986 fclose (fp);
987 errno = saved_errno;
988 goto free_then_fail;
991 if (fclose (fp) == EOF)
992 goto free_then_fail;
994 #endif /* MOUNTED_FREAD_FSTYP */
996 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
998 struct extmnttab mnt;
999 const char *table = MNTTAB;
1000 FILE *fp;
1001 int ret;
1003 /* No locking is needed, because the contents of /etc/mnttab is generated by the kernel. */
1005 errno = 0;
1006 fp = fopen (table, "r");
1007 if (fp == NULL)
1008 ret = errno;
1009 else
1011 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
1013 me = g_malloc (sizeof *me);
1014 me->me_devname = g_strdup (mnt.mnt_special);
1015 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1016 me->me_mntroot = NULL;
1017 me->me_type = g_strdup (mnt.mnt_fstype);
1018 me->me_type_malloced = 1;
1019 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1020 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1021 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
1023 mount_list = g_slist_prepend (mount_list, me);
1026 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1027 /* Here ret = -1 means success, ret >= 0 means failure. */
1030 if (ret >= 0)
1032 errno = ret;
1033 goto free_then_fail;
1036 #endif /* MOUNTED_GETEXTMNTENT */
1038 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
1040 struct mnttab mnt;
1041 const char *table = MNTTAB;
1042 FILE *fp;
1043 int ret;
1044 int lockfd = -1;
1046 #if defined F_RDLCK && defined F_SETLKW
1047 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
1048 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
1049 for this file name, we should use their macro name instead.
1050 (Why not just lock MNTTAB directly? We don't know.) */
1051 #ifndef MNTTAB_LOCK
1052 #define MNTTAB_LOCK "/etc/.mnttab.lock"
1053 #endif
1054 lockfd = open (MNTTAB_LOCK, O_RDONLY);
1055 if (lockfd >= 0)
1057 struct flock flock;
1059 flock.l_type = F_RDLCK;
1060 flock.l_whence = SEEK_SET;
1061 flock.l_start = 0;
1062 flock.l_len = 0;
1063 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
1064 if (errno != EINTR)
1066 int saved_errno = errno;
1067 close (lockfd);
1068 errno = saved_errno;
1069 return NULL;
1072 else if (errno != ENOENT)
1073 return NULL;
1074 #endif
1076 errno = 0;
1077 fp = fopen (table, "r");
1078 if (fp == NULL)
1079 ret = errno;
1080 else
1082 while ((ret = getmntent (fp, &mnt)) == 0)
1084 me = g_malloc (sizeof (*me));
1085 me->me_devname = g_strdup (mnt.mnt_special);
1086 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1087 me->me_mntroot = NULL;
1088 me->me_type = g_strdup (mnt.mnt_fstype);
1089 me->me_type_malloced = 1;
1090 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1091 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1092 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1094 mount_list = g_slist_prepend (mount_list, me);
1097 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1098 /* Here ret = -1 means success, ret >= 0 means failure. */
1101 if (lockfd >= 0 && close (lockfd) != 0)
1102 ret = errno;
1104 if (ret >= 0)
1106 errno = ret;
1107 goto free_then_fail;
1110 #endif /* MOUNTED_GETMNTENT2. */
1112 #ifdef MOUNTED_VMOUNT /* AIX */
1114 int bufsize;
1115 void *entries;
1116 char *thisent;
1117 struct vmount *vmp;
1118 int n_entries;
1119 int i;
1121 /* Ask how many bytes to allocate for the mounted file system info. */
1122 entries = &bufsize;
1123 if (mntctl (MCTL_QUERY, sizeof (bufsize), entries) != 0)
1124 return NULL;
1125 entries = g_malloc (bufsize);
1127 /* Get the list of mounted file systems. */
1128 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
1129 if (n_entries < 0)
1131 int saved_errno = errno;
1133 g_free (entries);
1134 errno = saved_errno;
1135 return NULL;
1138 for (i = 0, thisent = entries; i < n_entries; i++, thisent += vmp->vmt_length)
1140 char *options, *ignore;
1142 vmp = (struct vmount *) thisent;
1143 me = g_malloc (sizeof (*me));
1144 if (vmp->vmt_flags & MNT_REMOTE)
1146 char *host, *dir;
1148 me->me_remote = 1;
1149 /* Prepend the remote dirname. */
1150 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1151 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1152 me->me_devname = g_strconcat (host, ":", dir, (char *) NULL);
1154 else
1156 me->me_remote = 0;
1157 me->me_devname = g_strdup (thisent + vmp->vmt_data[VMT_OBJECT].vmt_off);
1159 me->me_mountdir = g_strdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1160 me->me_mntroot = NULL;
1161 me->me_type = g_strdup (fstype_to_string (vmp->vmt_gfstype));
1162 me->me_type_malloced = 1;
1163 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1164 ignore = strstr (options, "ignore");
1165 me->me_dummy = (ignore
1166 && (ignore == options || ignore[-1] == ',')
1167 && (ignore[sizeof ("ignore") - 1] == ','
1168 || ignore[sizeof ("ignore") - 1] == '\0'));
1169 me->me_dev = (dev_t) (-1); /* vmt_fsid might be the info we want. */
1171 mount_list = g_slist_prepend (mount_list, me);
1173 g_free (entries);
1175 #endif /* MOUNTED_VMOUNT. */
1177 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1179 DIR *dirp = opendir ("/dev/fs");
1180 char node[9 + NAME_MAX];
1182 if (!dirp)
1183 goto free_then_fail;
1185 while (1)
1187 struct statvfs dev;
1188 struct dirent entry;
1189 struct dirent *result;
1191 if (readdir_r (dirp, &entry, &result) || result == NULL)
1192 break;
1194 strcpy (node, "/dev/fs/");
1195 strcat (node, entry.d_name);
1197 if (statvfs (node, &dev) == 0)
1199 me = g_malloc (sizeof *me);
1200 me->me_devname = g_strdup (dev.f_mntfromname);
1201 me->me_mountdir = g_strdup (dev.f_mntonname);
1202 me->me_mntroot = NULL;
1203 me->me_type = g_strdup (dev.f_fstypename);
1204 me->me_type_malloced = 1;
1205 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1206 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1207 me->me_dev = (dev_t) (-1); /* Magic; means not known yet. */
1209 mount_list = g_slist_prepend (mount_list, me);
1212 closedir (dirp);
1214 #endif /* MOUNTED_INTERIX_STATVFS */
1216 return g_slist_reverse (mount_list);
1218 free_then_fail:
1220 int saved_errno = errno;
1222 g_slist_free_full (mount_list, (GDestroyNotify) free_mount_entry);
1224 errno = saved_errno;
1225 return NULL;
1228 #endif /* HAVE_INFOMOUNT_LIST */
1230 /* --------------------------------------------------------------------------------------------- */
1232 #ifdef HAVE_INFOMOUNT_QNX
1234 ** QNX has no [gs]etmnt*(), [gs]etfs*(), or /etc/mnttab, but can do
1235 ** this via the following code.
1236 ** Note that, as this is based on CWD, it only fills one mount_entry
1237 ** structure. See my_statfs() below for the "other side" of this hack.
1240 static GSList *
1241 read_file_system_list (void)
1243 struct _disk_entry de;
1244 struct statfs fs;
1245 int i, fd;
1246 char *tp, dev[_POSIX_NAME_MAX], dir[_POSIX_PATH_MAX];
1247 struct mount_entry *me = NULL;
1248 static GSList *list = NULL;
1250 if (list != NULL)
1252 me = (struct mount_entry *) list->data;
1254 g_free (me->me_devname);
1255 g_free (me->me_mountdir);
1256 g_free (me->me_mntroot);
1257 g_free (me->me_type);
1259 else
1261 me = (struct mount_entry *) g_malloc (sizeof (struct mount_entry));
1262 list = g_slist_prepend (list, me);
1265 if (!getcwd (dir, _POSIX_PATH_MAX))
1266 return (NULL);
1268 fd = open (dir, O_RDONLY);
1269 if (fd == -1)
1270 return (NULL);
1272 i = disk_get_entry (fd, &de);
1274 close (fd);
1276 if (i == -1)
1277 return (NULL);
1279 switch (de.disk_type)
1281 case _UNMOUNTED:
1282 tp = "unmounted";
1283 break;
1284 case _FLOPPY:
1285 tp = "Floppy";
1286 break;
1287 case _HARD:
1288 tp = "Hard";
1289 break;
1290 case _RAMDISK:
1291 tp = "Ram";
1292 break;
1293 case _REMOVABLE:
1294 tp = "Removable";
1295 break;
1296 case _TAPE:
1297 tp = "Tape";
1298 break;
1299 case _CDROM:
1300 tp = "CDROM";
1301 break;
1302 default:
1303 tp = "unknown";
1306 if (fsys_get_mount_dev (dir, &dev) == -1)
1307 return (NULL);
1309 if (fsys_get_mount_pt (dev, &dir) == -1)
1310 return (NULL);
1312 me->me_devname = g_strdup (dev);
1313 me->me_mountdir = g_strdup (dir);
1314 me->me_mntroot = NULL;
1315 me->me_type = g_strdup (tp);
1316 me->me_dev = de.disk_type;
1318 #ifdef DEBUG
1319 fprintf (stderr,
1320 "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1321 de.disk_type, tp, _DRIVER_NAME_LEN, _DRIVER_NAME_LEN, de.driver_name, de.disk_drv);
1322 fprintf (stderr, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev);
1323 fprintf (stderr, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir);
1324 #endif /* DEBUG */
1326 return (list);
1328 #endif /* HAVE_INFOMOUNT_QNX */
1330 /* --------------------------------------------------------------------------------------------- */
1332 #ifdef HAVE_INFOMOUNT
1333 /* Fill in the fields of FSP with information about space usage for
1334 the file system on which FILE resides.
1335 DISK is the device on which FILE is mounted, for space-getting
1336 methods that need to know it.
1337 Return 0 if successful, -1 if not. When returning -1, ensure that
1338 ERRNO is either a system error value, or zero if DISK is NULL
1339 on a system that requires a non-NULL value. */
1340 static int
1341 get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
1343 #ifdef STAT_STATVFS /* POSIX, except pre-2.6.36 glibc/Linux */
1345 if (statvfs_works ())
1347 struct statvfs vfsd;
1349 if (statvfs (file, &vfsd) < 0)
1350 return -1;
1352 /* f_frsize isn't guaranteed to be supported. */
1353 fsp->fsu_blocksize = (vfsd.f_frsize
1354 ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
1355 : PROPAGATE_ALL_ONES (vfsd.f_bsize));
1357 fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
1358 fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
1359 fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
1360 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
1361 fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
1362 fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
1364 else
1365 #endif
1368 #if defined STAT_STATVFS64 /* AIX */
1370 struct statvfs64 fsd;
1372 if (statvfs64 (file, &fsd) < 0)
1373 return -1;
1375 /* f_frsize isn't guaranteed to be supported. */
1376 /* *INDENT-OFF* */
1377 fsp->fsu_blocksize = fsd.f_frsize
1378 ? PROPAGATE_ALL_ONES (fsd.f_frsize)
1379 : PROPAGATE_ALL_ONES (fsd.f_bsize);
1380 /* *INDENT-ON* */
1382 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1384 struct statfs fsd;
1386 if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
1387 return -1;
1389 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1391 #elif defined STAT_STATFS2_FRSIZE /* 2.6 < glibc/Linux < 2.6.36 */
1393 struct statfs fsd;
1395 if (statfs (file, &fsd) < 0)
1396 return -1;
1398 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
1400 #elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4, \
1401 Mac OS X < 10.4, FreeBSD < 5.0, \
1402 NetBSD < 3.0, OpenBSD < 4.4 */
1404 struct statfs fsd;
1406 if (statfs (file, &fsd) < 0)
1407 return -1;
1409 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1411 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1413 /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1414 struct statfs are truncated to 2GB. These conditions detect that
1415 truncation, presumably without botching the 4.1.1 case, in which
1416 the values are not truncated. The correct counts are stored in
1417 undocumented spare fields. */
1418 if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
1420 fsd.f_blocks = fsd.f_spare[0];
1421 fsd.f_bfree = fsd.f_spare[1];
1422 fsd.f_bavail = fsd.f_spare[2];
1424 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1426 #elif defined STAT_STATFS2_FSIZE /* 4.4BSD and older NetBSD */
1428 struct statfs fsd;
1430 if (statfs (file, &fsd) < 0)
1431 return -1;
1433 fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1435 #elif defined STAT_STATFS4 /* SVR3, old Irix */
1437 struct statfs fsd;
1439 if (statfs (file, &fsd, sizeof (fsd), 0) < 0)
1440 return -1;
1442 /* Empirically, the block counts on most SVR3 and SVR3-derived
1443 systems seem to always be in terms of 512-byte blocks,
1444 no matter what value f_bsize has. */
1445 fsp->fsu_blocksize = 512;
1446 #endif
1448 #if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 \
1449 || defined STAT_STATFS2_FRSIZE || defined STAT_STATFS2_BSIZE \
1450 || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
1452 fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
1453 fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
1454 fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
1455 fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
1456 fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
1457 fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
1459 #endif
1462 (void) disk; /* avoid argument-unused warning */
1464 return 0;
1466 #endif /* HAVE_INFOMOUNT */
1468 /* --------------------------------------------------------------------------------------------- */
1469 /*** public functions ****************************************************************************/
1470 /* --------------------------------------------------------------------------------------------- */
1472 void
1473 free_my_statfs (void)
1475 #ifdef HAVE_INFOMOUNT_LIST
1476 g_clear_slist (&mc_mount_list, (GDestroyNotify) free_mount_entry);
1477 #endif /* HAVE_INFOMOUNT_LIST */
1480 /* --------------------------------------------------------------------------------------------- */
1482 void
1483 init_my_statfs (void)
1485 #ifdef HAVE_INFOMOUNT_LIST
1486 free_my_statfs ();
1487 mc_mount_list = read_file_system_list ();
1488 #endif /* HAVE_INFOMOUNT_LIST */
1491 /* --------------------------------------------------------------------------------------------- */
1493 void
1494 my_statfs (struct my_statfs *myfs_stats, const char *path)
1496 #ifdef HAVE_INFOMOUNT_LIST
1497 size_t len = 0;
1498 struct mount_entry *entry = NULL;
1499 GSList *temp;
1500 struct fs_usage fs_use;
1502 for (temp = mc_mount_list; temp != NULL; temp = g_slist_next (temp))
1504 struct mount_entry *me;
1505 size_t i;
1507 me = (struct mount_entry *) temp->data;
1508 i = strlen (me->me_mountdir);
1509 if (i > len && (strncmp (path, me->me_mountdir, i) == 0) &&
1510 (entry == NULL || IS_PATH_SEP (path[i]) || path[i] == '\0'))
1512 len = i;
1513 entry = me;
1517 if (entry != NULL)
1519 memset (&fs_use, 0, sizeof (fs_use));
1520 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1522 myfs_stats->type = entry->me_dev;
1523 myfs_stats->typename = entry->me_type;
1524 myfs_stats->mpoint = entry->me_mountdir;
1525 myfs_stats->mroot = entry->me_mntroot;
1526 myfs_stats->device = entry->me_devname;
1527 myfs_stats->avail =
1528 ((uintmax_t) (getuid ()? fs_use.fsu_bavail : fs_use.fsu_bfree) *
1529 fs_use.fsu_blocksize) >> 10;
1530 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1531 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1532 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1534 else
1535 #endif /* HAVE_INFOMOUNT_LIST */
1537 #ifdef HAVE_INFOMOUNT_QNX
1539 ** This is the "other side" of the hack to read_file_system_list() above.
1540 ** It's not the most efficient approach, but consumes less memory. It
1541 ** also accommodates QNX's ability to mount filesystems on the fly.
1543 struct mount_entry *entry;
1544 struct fs_usage fs_use;
1546 entry = read_file_system_list ();
1547 if (entry != NULL)
1549 get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1551 myfs_stats->type = entry->me_dev;
1552 myfs_stats->typename = entry->me_type;
1553 myfs_stats->mpoint = entry->me_mountdir;
1554 myfs_stats->mroot = entry->me_mntroot;
1555 myfs_stats->device = entry->me_devname;
1557 myfs_stats->avail = ((uintmax_t) fs_use.fsu_bfree * fs_use.fsu_blocksize) >> 10;
1558 myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1559 myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1560 myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1562 else
1563 #endif /* HAVE_INFOMOUNT_QNX */
1565 myfs_stats->type = 0;
1566 myfs_stats->mpoint = "unknown";
1567 myfs_stats->device = "unknown";
1568 myfs_stats->avail = 0;
1569 myfs_stats->total = 0;
1570 myfs_stats->nfree = 0;
1571 myfs_stats->nodes = 0;
1575 /* --------------------------------------------------------------------------------------------- */