root/src/filemanager/mountlist.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. me_remote
  2. statvfs_works
  3. free_mount_entry
  4. fstype_to_string
  5. fsp_to_string
  6. fstype_to_string
  7. dev_from_mount_options
  8. unescape_tab
  9. read_file_system_list
  10. read_file_system_list
  11. get_fs_usage
  12. free_my_statfs
  13. init_my_statfs
  14. my_statfs

   1 /*
   2    Return a list of mounted file systems
   3 
   4    Copyright (C) 1991-2025
   5    Free Software Foundation, Inc.
   6 
   7    This file is part of the Midnight Commander.
   8 
   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.
  13 
  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.
  18 
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  21  */
  22 
  23 /** \file mountlist.c
  24  *  \brief Source: list of mounted filesystems
  25  */
  26 
  27 #include <config.h>
  28 
  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>
  35 
  36 #include <errno.h>
  37 
  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
  42 
  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>
  51 
  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
  70 #endif
  71 
  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
  81 
  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
 100 
 101 #ifdef MOUNTED_GETMNTINFO  // Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD
 102 #    include <sys/mount.h>
 103 #endif
 104 
 105 #ifdef MOUNTED_GETMNTINFO2  // NetBSD, Minix
 106 #    include <sys/statvfs.h>
 107 #endif
 108 
 109 #ifdef MOUNTED_FS_STAT_DEV  // Haiku, also (obsolete) BeOS
 110 #    include <fs_info.h>
 111 #    include <dirent.h>
 112 #endif
 113 
 114 #ifdef MOUNTED_FREAD_FSTYP  // (obsolete) SVR3
 115 #    include <mnttab.h>
 116 #    include <sys/fstyp.h>
 117 #    include <sys/statfs.h>
 118 #endif
 119 
 120 #ifdef MOUNTED_GETEXTMNTENT  // Solaris >= 8
 121 #    include <sys/mnttab.h>
 122 #endif
 123 
 124 #ifdef MOUNTED_GETMNTENT2  // Solaris < 8, also (obsolete) SVR4
 125 #    include <sys/mnttab.h>
 126 #endif
 127 
 128 #ifdef MOUNTED_VMOUNT  // AIX
 129 #    include <fshelp.h>
 130 #    include <sys/vfs.h>
 131 #endif
 132 
 133 #ifdef MOUNTED_INTERIX_STATVFS  // Interix
 134 #    include <sys/statvfs.h>
 135 #    include <dirent.h>
 136 #endif
 137 
 138 #ifdef HAVE_SYS_MNTENT_H
 139 /* This is to get MNTOPT_IGNORE on e.g. SVR4.  */
 140 #    include <sys/mntent.h>
 141 #endif
 142 
 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
 151 
 152 #ifndef HAVE_HASMNTOPT
 153 #    define hasmntopt(mnt, opt) ((char *) 0)
 154 #endif
 155 
 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
 168 
 169 #ifdef HAVE_INFOMOUNT_QNX
 170 #    include <sys/disk.h>
 171 #    include <sys/fsys.h>
 172 #endif
 173 
 174 #ifdef HAVE_SYS_STATVFS_H  // SVR4.
 175 #    include <sys/statvfs.h>
 176 #endif
 177 
 178 #include "lib/global.h"
 179 #include "lib/strutil.h"     // str_verscmp()
 180 #include "lib/unixcompat.h"  // makedev
 181 #include "mountlist.h"
 182 
 183 /*** global variables ****************************************************************************/
 184 
 185 /*** file scope macro definitions ****************************************************************/
 186 
 187 #if defined(__QNX__) && !defined(__QNXNTO__) && !defined(HAVE_INFOMOUNT_LIST)
 188 #    define HAVE_INFOMOUNT_QNX
 189 #endif
 190 
 191 #if defined(HAVE_INFOMOUNT_LIST) || defined(HAVE_INFOMOUNT_QNX)
 192 #    define HAVE_INFOMOUNT
 193 #endif
 194 
 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
 199 
 200 #define ME_DUMMY_0(Fs_name, Fs_type)                                                               \
 201     (strcmp (Fs_type, "autofs") == 0 || strcmp (Fs_type, "proc") == 0                              \
 202      || strcmp (Fs_type, "subfs") == 0 /* for Linux 2.6/3.x */                                     \
 203      || strcmp (Fs_type, "debugfs") == 0 || strcmp (Fs_type, "devpts") == 0                        \
 204      || strcmp (Fs_type, "fusectl") == 0 || strcmp (Fs_type, "fuse.portal") == 0                   \
 205      || strcmp (Fs_type, "mqueue") == 0 || strcmp (Fs_type, "rpc_pipefs") == 0                     \
 206      || strcmp (Fs_type, "sysfs") == 0  /* FreeBSD, Linux 2.4 */                                   \
 207      || strcmp (Fs_type, "devfs") == 0  /* for NetBSD 3.0 */                                       \
 208      || strcmp (Fs_type, "kernfs") == 0 /* for Irix 6.5 */                                         \
 209      || strcmp (Fs_type, "ignore") == 0)
 210 
 211 /* Historically, we have marked as "dummy" any file system of type "none",
 212    but now that programs like du need to know about bind-mounted directories,
 213    we grant an exception to any with "bind" in its list of mount options.
 214    I.e., those are *not* dummy entries.  */
 215 #ifdef MOUNTED_GETMNTENT1
 216 #    define ME_DUMMY(Fs_name, Fs_type, Bind)                                                       \
 217         (ME_DUMMY_0 (Fs_name, Fs_type) || (strcmp (Fs_type, "none") == 0 && !Bind))
 218 #else
 219 #    define ME_DUMMY(Fs_name, Fs_type)                                                             \
 220         (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
 221 #endif
 222 
 223 #ifdef __CYGWIN__
 224 #    include <windows.h>
 225 #    define ME_REMOTE me_remote
 226 /* All cygwin mount points include ':' or start with '//'; so it
 227    requires a native Windows call to determine remote disks.  */
 228 static int
 229 me_remote (char const *fs_name, char const *fs_type)
     /* [previous][next][first][last][top][bottom][index][help]  */
 230 {
 231     (void) fs_type;
 232 
 233     if (fs_name[0] && fs_name[1] == ':')
 234     {
 235         char drive[4];
 236         sprintf (drive, "%c:\\", fs_name[0]);
 237         switch (GetDriveType (drive))
 238         {
 239         case DRIVE_REMOVABLE:
 240         case DRIVE_FIXED:
 241         case DRIVE_CDROM:
 242         case DRIVE_RAMDISK:
 243             return 0;
 244         }
 245     }
 246     return 1;
 247 }
 248 #endif
 249 #ifndef ME_REMOTE
 250 /* A file system is 'remote' if its Fs_name contains a ':'
 251    or if (it is of type (smbfs or smb3 or cifs) and its Fs_name starts with '//')
 252    or if it is of any other of the listed types
 253    or Fs_name is equal to "-hosts" (used by autofs to mount remote fs).
 254    "VM" file systems like prl_fs or vboxsf are not considered remote here. */
 255 #    define ME_REMOTE(Fs_name, Fs_type)                                                            \
 256         (strchr (Fs_name, ':') != NULL                                                             \
 257          || ((Fs_name)[0] == '/' && (Fs_name)[1] == '/'                                            \
 258              && (strcmp (Fs_type, "smbfs") == 0 || strcmp (Fs_type, "smb3") == 0                   \
 259                  || strcmp (Fs_type, "cifs") == 0))                                                \
 260          || strcmp (Fs_type, "acfs") == 0 || strcmp (Fs_type, "afs") == 0                          \
 261          || strcmp (Fs_type, "coda") == 0 || strcmp (Fs_type, "auristorfs") == 0                   \
 262          || strcmp (Fs_type, "fhgfs") == 0 || strcmp (Fs_type, "gpfs") == 0                        \
 263          || strcmp (Fs_type, "ibrix") == 0 || strcmp (Fs_type, "ocfs2") == 0                       \
 264          || strcmp (Fs_type, "vxfs") == 0 || strcmp ("-hosts", Fs_name) == 0)
 265 #endif
 266 
 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) ? -(1 << (sizeof (x) * CHAR_BIT)) : 0)))              \
 274          ? UINTMAX_MAX                                                                             \
 275          : (uintmax_t) (x))
 276 
 277 /* Extract the top bit of X as an uintmax_t value.  */
 278 #define EXTRACT_TOP_BIT(x) ((x) & ((uintmax_t) 1 << (sizeof (x) * CHAR_BIT - 1)))
 279 
 280 /* If a value is negative, many space usage primitives store it into an
 281    integer variable by assignment, even if the variable's type is unsigned.
 282    So, if a space usage variable X's top bit is set, convert X to the
 283    uintmax_t value V such that (- (uintmax_t) V) is the negative of
 284    the original value.  If X's top bit is clear, just yield X.
 285    Use PROPAGATE_TOP_BIT if the original value might be negative;
 286    otherwise, use PROPAGATE_ALL_ONES.  */
 287 #define PROPAGATE_TOP_BIT(x) ((x) | ~(EXTRACT_TOP_BIT (x) - 1))
 288 
 289 #ifdef STAT_STATVFS
 290 #    if !(defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
 291 /* The FRSIZE fallback is not required in this case.  */
 292 #        undef STAT_STATFS2_FRSIZE
 293 #    else
 294 #        include <sys/utsname.h>
 295 #        include <sys/statfs.h>
 296 #        define STAT_STATFS2_BSIZE 1
 297 #    endif
 298 #endif
 299 
 300 /*** file scope type declarations ****************************************************************/
 301 
 302 /* A mount table entry. */
 303 struct mount_entry
 304 {
 305     char *me_devname;                   /* Device node name, including "/dev/". */
 306     char *me_mountdir;                  // Mount point directory name.
 307     char *me_mntroot;                   /* Directory on filesystem of device used
 308                                            as root for the (bind) mount. */
 309     char *me_type;                      // "nfs", "4.2", etc.
 310     dev_t me_dev;                       // Device number of me_mountdir.
 311     unsigned int me_dummy : 1;          // Nonzero for dummy file systems.
 312     unsigned int me_remote : 1;         // Nonzero for remote filesystems.
 313     unsigned int me_type_malloced : 1;  // Nonzero if me_type was malloced.
 314 };
 315 
 316 struct fs_usage
 317 {
 318     uintmax_t fsu_blocksize;     // Size of a block.
 319     uintmax_t fsu_blocks;        // Total blocks.
 320     uintmax_t fsu_bfree;         // Free blocks available to superuser.
 321     uintmax_t fsu_bavail;        // Free blocks available to non-superuser.
 322     int fsu_bavail_top_bit_set;  // 1 if fsu_bavail represents a value < 0.
 323     uintmax_t fsu_files;         // Total file nodes.
 324     uintmax_t fsu_ffree;         // Free file nodes.
 325 };
 326 
 327 /*** forward declarations (file scope functions) *************************************************/
 328 
 329 /*** file scope variables ************************************************************************/
 330 
 331 #ifdef HAVE_INFOMOUNT_LIST
 332 static GSList *mc_mount_list = NULL;
 333 #endif
 334 
 335 /* --------------------------------------------------------------------------------------------- */
 336 /*** file scope functions ************************************************************************/
 337 /* --------------------------------------------------------------------------------------------- */
 338 
 339 #ifdef STAT_STATVFS
 340 /* Return true if statvfs works.  This is false for statvfs on systems
 341    with GNU libc on Linux kernels before 2.6.36, which stats all
 342    preceding entries in /proc/mounts; that makes df hang if even one
 343    of the corresponding file systems is hard-mounted but not available.  */
 344 static int
 345 statvfs_works (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 346 {
 347 #    if !(defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
 348     return 1;
 349 #    else
 350     static int statvfs_works_cache = -1;
 351     struct utsname name;
 352 
 353     if (statvfs_works_cache < 0)
 354         statvfs_works_cache = (uname (&name) == 0 && 0 <= str_verscmp (name.release, "2.6.36"));
 355     return statvfs_works_cache;
 356 #    endif
 357 }
 358 #endif
 359 
 360 /* --------------------------------------------------------------------------------------------- */
 361 
 362 #ifdef HAVE_INFOMOUNT_LIST
 363 static void
 364 free_mount_entry (struct mount_entry *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 365 {
 366     if (me == NULL)
 367         return;
 368     g_free (me->me_devname);
 369     g_free (me->me_mountdir);
 370     g_free (me->me_mntroot);
 371     if (me->me_type_malloced)
 372         g_free (me->me_type);
 373     g_free (me);
 374 }
 375 
 376 /* --------------------------------------------------------------------------------------------- */
 377 
 378 #    ifdef MOUNTED_GETMNTINFO  // Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD
 379 
 380 #        ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
 381 static char *
 382 fstype_to_string (short int t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 383 {
 384     switch (t)
 385     {
 386 #            ifdef MOUNT_PC
 387         // cppcheck-suppress syntaxError
 388     case MOUNT_PC:
 389         return "pc";
 390 #            endif
 391 #            ifdef MOUNT_MFS
 392         // cppcheck-suppress syntaxError
 393     case MOUNT_MFS:
 394         return "mfs";
 395 #            endif
 396 #            ifdef MOUNT_LO
 397         // cppcheck-suppress syntaxError
 398     case MOUNT_LO:
 399         return "lo";
 400 #            endif
 401 #            ifdef MOUNT_TFS
 402         // cppcheck-suppress syntaxError
 403     case MOUNT_TFS:
 404         return "tfs";
 405 #            endif
 406 #            ifdef MOUNT_TMP
 407         // cppcheck-suppress syntaxError
 408     case MOUNT_TMP:
 409         return "tmp";
 410 #            endif
 411 #            ifdef MOUNT_UFS
 412         // cppcheck-suppress syntaxError
 413     case MOUNT_UFS:
 414         return "ufs";
 415 #            endif
 416 #            ifdef MOUNT_NFS
 417         // cppcheck-suppress syntaxError
 418     case MOUNT_NFS:
 419         return "nfs";
 420 #            endif
 421 #            ifdef MOUNT_MSDOS
 422         // cppcheck-suppress syntaxError
 423     case MOUNT_MSDOS:
 424         return "msdos";
 425 #            endif
 426 #            ifdef MOUNT_LFS
 427         // cppcheck-suppress syntaxError
 428     case MOUNT_LFS:
 429         return "lfs";
 430 #            endif
 431 #            ifdef MOUNT_LOFS
 432         // cppcheck-suppress syntaxError
 433     case MOUNT_LOFS:
 434         return "lofs";
 435 #            endif
 436 #            ifdef MOUNT_FDESC
 437         // cppcheck-suppress syntaxError
 438     case MOUNT_FDESC:
 439         return "fdesc";
 440 #            endif
 441 #            ifdef MOUNT_PORTAL
 442         // cppcheck-suppress syntaxError
 443     case MOUNT_PORTAL:
 444         return "portal";
 445 #            endif
 446 #            ifdef MOUNT_NULL
 447         // cppcheck-suppress syntaxError
 448     case MOUNT_NULL:
 449         return "null";
 450 #            endif
 451 #            ifdef MOUNT_UMAP
 452         // cppcheck-suppress syntaxError
 453     case MOUNT_UMAP:
 454         return "umap";
 455 #            endif
 456 #            ifdef MOUNT_KERNFS
 457         // cppcheck-suppress syntaxError
 458     case MOUNT_KERNFS:
 459         return "kernfs";
 460 #            endif
 461 #            ifdef MOUNT_PROCFS
 462         // cppcheck-suppress syntaxError
 463     case MOUNT_PROCFS:
 464         return "procfs";
 465 #            endif
 466 #            ifdef MOUNT_AFS
 467         // cppcheck-suppress syntaxError
 468     case MOUNT_AFS:
 469         return "afs";
 470 #            endif
 471 #            ifdef MOUNT_CD9660
 472         // cppcheck-suppress syntaxError
 473     case MOUNT_CD9660:
 474         return "cd9660";
 475 #            endif
 476 #            ifdef MOUNT_UNION
 477         // cppcheck-suppress syntaxError
 478     case MOUNT_UNION:
 479         return "union";
 480 #            endif
 481 #            ifdef MOUNT_DEVFS
 482         // cppcheck-suppress syntaxError
 483     case MOUNT_DEVFS:
 484         return "devfs";
 485 #            endif
 486 #            ifdef MOUNT_EXT2FS
 487         // cppcheck-suppress syntaxError
 488     case MOUNT_EXT2FS:
 489         return "ext2fs";
 490 #            endif
 491     default:
 492         return "?";
 493     }
 494 }
 495 #        endif
 496 
 497 /* --------------------------------------------------------------------------------------------- */
 498 
 499 static char *
 500 fsp_to_string (const struct statfs *fsp)
     /* [previous][next][first][last][top][bottom][index][help]  */
 501 {
 502 #        ifdef HAVE_STRUCT_STATFS_F_FSTYPENAME
 503     return (char *) (fsp->f_fstypename);
 504 #        else
 505     return fstype_to_string (fsp->f_type);
 506 #        endif
 507 }
 508 #    endif
 509 
 510 /* --------------------------------------------------------------------------------------------- */
 511 
 512 #    ifdef MOUNTED_VMOUNT  // AIX
 513 static char *
 514 fstype_to_string (int t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 515 {
 516     struct vfs_ent *e;
 517 
 518     e = getvfsbytype (t);
 519     if (!e || !e->vfsent_name)
 520         return "none";
 521     else
 522         return e->vfsent_name;
 523 }
 524 #    endif
 525 
 526 /* --------------------------------------------------------------------------------------------- */
 527 
 528 #    if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
 529 
 530 /* Return the device number from MOUNT_OPTIONS, if possible.
 531    Otherwise return (dev_t) -1.  */
 532 
 533 /* --------------------------------------------------------------------------------------------- */
 534 
 535 static dev_t
 536 dev_from_mount_options (char const *mount_options)
     /* [previous][next][first][last][top][bottom][index][help]  */
 537 {
 538     /* GNU/Linux allows file system implementations to define their own
 539        meaning for "dev=" mount options, so don't trust the meaning
 540        here.  */
 541 #        ifndef __linux__
 542     static char const dev_pattern[] = ",dev=";
 543     char const *devopt = strstr (mount_options, dev_pattern);
 544 
 545     if (devopt)
 546     {
 547         char const *optval = devopt + sizeof (dev_pattern) - 1;
 548         char *optvalend;
 549         unsigned long int dev;
 550         errno = 0;
 551         dev = strtoul (optval, &optvalend, 16);
 552         if (optval != optvalend && (*optvalend == '\0' || *optvalend == ',')
 553             && !(dev == ULONG_MAX && errno == ERANGE) && dev == (dev_t) dev)
 554             return dev;
 555     }
 556 #        endif
 557 
 558     (void) mount_options;
 559     return -1;
 560 }
 561 
 562 #    endif
 563 
 564 /* --------------------------------------------------------------------------------------------- */
 565 
 566 #    if defined MOUNTED_GETMNTENT1                                                                 \
 567         && (defined __linux__ || defined __ANDROID__)  // GNU/Linux, Android
 568 
 569 /* Unescape the paths in mount tables.
 570    STR is updated in place.  */
 571 static void
 572 unescape_tab (char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 573 {
 574     size_t i, j = 0;
 575     size_t len;
 576 
 577     len = strlen (str) + 1;
 578 
 579     for (i = 0; i < len; i++)
 580     {
 581         if (str[i] == '\\' && (i + 4 < len) && str[i + 1] >= '0' && str[i + 1] <= '3'
 582             && str[i + 2] >= '0' && str[i + 2] <= '7' && str[i + 3] >= '0' && str[i + 3] <= '7')
 583         {
 584             str[j++] = (str[i + 1] - '0') * 64 + (str[i + 2] - '0') * 8 + (str[i + 3] - '0');
 585             i += 3;
 586         }
 587         else
 588             str[j++] = str[i];
 589     }
 590 }
 591 #    endif
 592 
 593 /* --------------------------------------------------------------------------------------------- */
 594 
 595 /* Return a list of the currently mounted file systems, or NULL on error.
 596    Add each entry to the tail of the list so that they stay in order. */
 597 
 598 static GSList *
 599 read_file_system_list (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 600 {
 601     GSList *mount_list = NULL;
 602     struct mount_entry *me;
 603 
 604 #    ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,                              \
 605                                  also (obsolete) 4.3BSD, SunOS */
 606     {
 607         FILE *fp;
 608 
 609 #        if defined __linux__ || defined __ANDROID__
 610         /* Try parsing mountinfo first, as that make device IDs available.
 611            Note we could use libmount routines to simplify this parsing a little
 612            (and that code is in previous versions of this function), however
 613            libmount depends on libselinux which pulls in many dependencies.  */
 614         char const *mountinfo = "/proc/self/mountinfo";
 615 
 616         fp = fopen (mountinfo, "r");
 617         if (fp != NULL)
 618         {
 619             char *line = NULL;
 620             size_t buf_size = 0;
 621 
 622             while (getline (&line, &buf_size, fp) != -1)
 623             {
 624                 unsigned int devmaj, devmin;
 625                 int target_s, target_e, type_s, type_e;
 626                 int source_s, source_e, mntroot_s, mntroot_e;
 627                 char test;
 628                 char *dash;
 629                 int rc;
 630 
 631                 rc = sscanf (line,
 632                              "%*u "      // id - discarded
 633                              "%*u "      // parent - discarded
 634                              "%u:%u "    // dev major:minor
 635                              "%n%*s%n "  // mountroot
 636                              "%n%*s%n"   // target, start and end
 637                              "%c",       // more data...
 638                              &devmaj, &devmin, &mntroot_s, &mntroot_e, &target_s, &target_e, &test);
 639 
 640                 if (rc != 3 && rc != 7)  // 7 if %n included in count.
 641                     continue;
 642 
 643                 // skip optional fields, terminated by " - "
 644                 dash = strstr (line + target_e, " - ");
 645                 if (dash == NULL)
 646                     continue;
 647 
 648                 rc = sscanf (dash,
 649                              " - "       //
 650                              "%n%*s%n "  // FS type, start and end
 651                              "%n%*s%n "  // source, start and end
 652                              "%c",       // more data...
 653                              &type_s, &type_e, &source_s, &source_e, &test);
 654                 if (rc != 1 && rc != 5)  // 5 if %n included in count.
 655                     continue;
 656 
 657                 // manipulate the sub-strings in place.
 658                 line[mntroot_e] = '\0';
 659                 line[target_e] = '\0';
 660                 dash[type_e] = '\0';
 661                 dash[source_e] = '\0';
 662                 unescape_tab (dash + source_s);
 663                 unescape_tab (line + target_s);
 664                 unescape_tab (line + mntroot_s);
 665 
 666                 me = g_malloc (sizeof *me);
 667 
 668                 me->me_devname = g_strdup (dash + source_s);
 669                 me->me_mountdir = g_strdup (line + target_s);
 670                 me->me_mntroot = g_strdup (line + mntroot_s);
 671                 me->me_type = g_strdup (dash + type_s);
 672                 me->me_type_malloced = 1;
 673                 me->me_dev = makedev (devmaj, devmin);
 674                 /* we pass "false" for the "Bind" option as that's only
 675                    significant when the Fs_type is "none" which will not be
 676                    the case when parsing "/proc/self/mountinfo", and only
 677                    applies for static /etc/mtab files.  */
 678                 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, FALSE);
 679                 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
 680 
 681                 mount_list = g_slist_prepend (mount_list, me);
 682             }
 683 
 684             free (line);
 685 
 686             if (ferror (fp) != 0)
 687             {
 688                 int saved_errno = errno;
 689 
 690                 fclose (fp);
 691                 errno = saved_errno;
 692                 goto free_then_fail;
 693             }
 694 
 695             if (fclose (fp) == EOF)
 696                 goto free_then_fail;
 697         }
 698         else  // fallback to /proc/self/mounts (/etc/mtab)
 699 #        endif
 700         {
 701             struct mntent *mnt;
 702             const char *table = MOUNTED;
 703 
 704             fp = setmntent (table, "r");
 705             if (fp == NULL)
 706                 return NULL;
 707 
 708             while ((mnt = getmntent (fp)) != NULL)
 709             {
 710                 gboolean bind;
 711 
 712                 bind = hasmntopt (mnt, "bind") != NULL;
 713 
 714                 me = g_malloc (sizeof (*me));
 715                 me->me_devname = g_strdup (mnt->mnt_fsname);
 716                 me->me_mountdir = g_strdup (mnt->mnt_dir);
 717                 me->me_mntroot = NULL;
 718                 me->me_type = g_strdup (mnt->mnt_type);
 719                 me->me_type_malloced = 1;
 720                 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
 721                 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
 722                 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
 723 
 724                 mount_list = g_slist_prepend (mount_list, me);
 725             }
 726 
 727             if (endmntent (fp) == 0)
 728                 goto free_then_fail;
 729         }
 730     }
 731 #    endif
 732 
 733 #    ifdef MOUNTED_GETMNTINFO  // Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD
 734     {
 735         struct statfs *fsp;
 736         int entries;
 737 
 738         entries = getmntinfo (&fsp, MNT_NOWAIT);
 739         if (entries < 0)
 740             return NULL;
 741         for (; entries-- > 0; fsp++)
 742         {
 743             char *fs_type = fsp_to_string (fsp);
 744 
 745             me = g_malloc (sizeof (*me));
 746             me->me_devname = g_strdup (fsp->f_mntfromname);
 747             me->me_mountdir = g_strdup (fsp->f_mntonname);
 748             me->me_mntroot = NULL;
 749             me->me_type = fs_type;
 750             me->me_type_malloced = 0;
 751             me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
 752             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
 753             me->me_dev = (dev_t) (-1);  // Magic; means not known yet.
 754 
 755             mount_list = g_slist_prepend (mount_list, me);
 756         }
 757     }
 758 #    endif
 759 
 760 #    ifdef MOUNTED_GETMNTINFO2  // NetBSD, Minix
 761     {
 762         struct statvfs *fsp;
 763         int entries;
 764 
 765         entries = getmntinfo (&fsp, MNT_NOWAIT);
 766         if (entries < 0)
 767             return NULL;
 768         for (; entries-- > 0; fsp++)
 769         {
 770             me = g_malloc (sizeof (*me));
 771             me->me_devname = g_strdup (fsp->f_mntfromname);
 772             me->me_mountdir = g_strdup (fsp->f_mntonname);
 773             me->me_mntroot = NULL;
 774             me->me_type = g_strdup (fsp->f_fstypename);
 775             me->me_type_malloced = 1;
 776             me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
 777             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
 778             me->me_dev = (dev_t) (-1);  // Magic; means not known yet.
 779 
 780             mount_list = g_slist_prepend (mount_list, me);
 781         }
 782     }
 783 #    endif
 784 
 785 #    if defined MOUNTED_FS_STAT_DEV  // Haiku, also (obsolete) BeOS
 786     {
 787         /* The next_dev() and fs_stat_dev() system calls give the list of
 788            all file systems, including the information returned by statvfs()
 789            (fs type, total blocks, free blocks etc.), but without the mount
 790            point. But on BeOS all file systems except / are mounted in the
 791            rootfs, directly under /.
 792            The directory name of the mount point is often, but not always,
 793            identical to the volume name of the device.
 794            We therefore get the list of subdirectories of /, and the list
 795            of all file systems, and match the two lists.  */
 796 
 797         DIR *dirp;
 798         struct rootdir_entry
 799         {
 800             char *name;
 801             dev_t dev;
 802             ino_t ino;
 803             struct rootdir_entry *next;
 804         };
 805         struct rootdir_entry *rootdir_list;
 806         struct rootdir_entry **rootdir_tail;
 807         int32 pos;
 808         dev_t dev;
 809         fs_info fi;
 810 
 811         // All volumes are mounted in the rootfs, directly under /
 812         rootdir_list = NULL;
 813         rootdir_tail = &rootdir_list;
 814         dirp = opendir (PATH_SEP_STR);
 815         if (dirp)
 816         {
 817             struct dirent *d;
 818 
 819             while ((d = readdir (dirp)) != NULL)
 820             {
 821                 char *name;
 822                 struct stat statbuf;
 823 
 824                 if (DIR_IS_DOT (d->d_name))
 825                     continue;
 826 
 827                 if (DIR_IS_DOTDOT (d->d_name))
 828                     name = g_strdup (PATH_SEP_STR);
 829                 else
 830                     name = g_strconcat (PATH_SEP_STR, d->d_name, (char *) NULL);
 831 
 832                 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
 833                 {
 834                     struct rootdir_entry *re = g_malloc (sizeof (*re));
 835                     re->name = name;
 836                     re->dev = statbuf.st_dev;
 837                     re->ino = statbuf.st_ino;
 838 
 839                     // Add to the linked list.
 840                     *rootdir_tail = re;
 841                     rootdir_tail = &re->next;
 842                 }
 843                 else
 844                     g_free (name);
 845             }
 846             closedir (dirp);
 847         }
 848         *rootdir_tail = NULL;
 849 
 850         for (pos = 0; (dev = next_dev (&pos)) >= 0;)
 851             if (fs_stat_dev (dev, &fi) >= 0)
 852             {
 853                 // Note: fi.dev == dev.
 854                 struct rootdir_entry *re;
 855 
 856                 for (re = rootdir_list; re; re = re->next)
 857                     if (re->dev == fi.dev && re->ino == fi.root)
 858                         break;
 859 
 860                 me = g_malloc (sizeof (*me));
 861                 me->me_devname =
 862                     g_strdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
 863                 me->me_mountdir = g_strdup (re != NULL ? re->name : fi.fsh_name);
 864                 me->me_mntroot = NULL;
 865                 me->me_type = g_strdup (fi.fsh_name);
 866                 me->me_type_malloced = 1;
 867                 me->me_dev = fi.dev;
 868                 me->me_dummy = 0;
 869                 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
 870 
 871                 mount_list = g_slist_prepend (mount_list, me);
 872             }
 873 
 874         while (rootdir_list != NULL)
 875         {
 876             struct rootdir_entry *re = rootdir_list;
 877 
 878             rootdir_list = re->next;
 879             g_free (re->name);
 880             g_free (re);
 881         }
 882     }
 883 #    endif
 884 
 885 #    ifdef MOUNTED_GETFSSTAT  //  OSF/1, also (obsolete) Apple Darwin 1.3
 886     {
 887         int numsys, counter;
 888         size_t bufsize;
 889         struct statfs *stats;
 890 
 891         numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
 892         if (numsys < 0)
 893             return NULL;
 894         if (SIZE_MAX / sizeof (*stats) <= numsys)
 895         {
 896             fprintf (stderr, "%s\n", _ ("Memory exhausted!"));
 897             exit (EXIT_FAILURE);
 898         }
 899 
 900         bufsize = (1 + numsys) * sizeof (*stats);
 901         stats = g_malloc (bufsize);
 902         numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
 903 
 904         if (numsys < 0)
 905         {
 906             g_free (stats);
 907             return NULL;
 908         }
 909 
 910         for (counter = 0; counter < numsys; counter++)
 911         {
 912             me = g_malloc (sizeof (*me));
 913             me->me_devname = g_strdup (stats[counter].f_mntfromname);
 914             me->me_mountdir = g_strdup (stats[counter].f_mntonname);
 915             me->me_mntroot = NULL;
 916             me->me_type = g_strdup (FS_TYPE (stats[counter]));
 917             me->me_type_malloced = 1;
 918             me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
 919             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
 920             me->me_dev = (dev_t) (-1);  // Magic; means not known yet.
 921 
 922             mount_list = g_slist_prepend (mount_list, me);
 923         }
 924 
 925         g_free (stats);
 926     }
 927 #    endif
 928 
 929 #    if defined MOUNTED_FREAD_FSTYP  // (obsolete) SVR3
 930     {
 931         struct mnttab mnt;
 932         char *table = "/etc/mnttab";
 933         FILE *fp;
 934 
 935         fp = fopen (table, "r");
 936         if (fp == NULL)
 937             return NULL;
 938 
 939         while (fread (&mnt, sizeof (mnt), 1, fp) > 0)
 940         {
 941             me = g_malloc (sizeof (*me));
 942             me->me_devname = g_strdup (mnt.mt_dev);
 943             me->me_mountdir = g_strdup (mnt.mt_filsys);
 944             me->me_mntroot = NULL;
 945             me->me_dev = (dev_t) (-1);  // Magic; means not known yet.
 946             me->me_type = "";
 947             me->me_type_malloced = 0;
 948             {
 949                 struct statfs fsd;
 950                 char typebuf[FSTYPSZ];
 951 
 952                 if (statfs (me->me_mountdir, &fsd, sizeof (fsd), 0) != -1
 953                     && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
 954                 {
 955                     me->me_type = g_strdup (typebuf);
 956                     me->me_type_malloced = 1;
 957                 }
 958             }
 959             me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
 960             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
 961 
 962             mount_list = g_slist_prepend (mount_list, me);
 963         }
 964 
 965         if (ferror (fp))
 966         {
 967             // The last fread() call must have failed.
 968             int saved_errno = errno;
 969 
 970             fclose (fp);
 971             errno = saved_errno;
 972             goto free_then_fail;
 973         }
 974 
 975         if (fclose (fp) == EOF)
 976             goto free_then_fail;
 977     }
 978 #    endif
 979 
 980 #    ifdef MOUNTED_GETEXTMNTENT  // Solaris >= 8
 981     {
 982         struct extmnttab mnt;
 983         const char *table = MNTTAB;
 984         FILE *fp;
 985         int ret;
 986 
 987         // No locking is needed, because the contents of /etc/mnttab is generated by the kernel
 988 
 989         errno = 0;
 990         fp = fopen (table, "r");
 991         if (fp == NULL)
 992             ret = errno;
 993         else
 994         {
 995             while ((ret = getextmntent (fp, &mnt, 1)) == 0)
 996             {
 997                 me = g_malloc (sizeof *me);
 998                 me->me_devname = g_strdup (mnt.mnt_special);
 999                 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1000                 me->me_mntroot = NULL;
1001                 me->me_type = g_strdup (mnt.mnt_fstype);
1002                 me->me_type_malloced = 1;
1003                 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1004                 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1005                 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
1006 
1007                 mount_list = g_slist_prepend (mount_list, me);
1008             }
1009 
1010             ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1011             // Here ret = -1 means success, ret >= 0 means failure.
1012         }
1013 
1014         if (ret >= 0)
1015         {
1016             errno = ret;
1017             goto free_then_fail;
1018         }
1019     }
1020 #    endif
1021 
1022 #    ifdef MOUNTED_GETMNTENT2  // Solaris < 8, also (obsolete) SVR4
1023     {
1024         struct mnttab mnt;
1025         const char *table = MNTTAB;
1026         FILE *fp;
1027         int ret;
1028         int lockfd = -1;
1029 
1030 #        if defined F_RDLCK && defined F_SETLKW
1031         /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
1032            e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
1033            for this file name, we should use their macro name instead.
1034            (Why not just lock MNTTAB directly?  We don't know.)  */
1035 #            ifndef MNTTAB_LOCK
1036 #                define MNTTAB_LOCK "/etc/.mnttab.lock"
1037 #            endif
1038         lockfd = open (MNTTAB_LOCK, O_RDONLY);
1039         if (lockfd >= 0)
1040         {
1041             struct flock flock;
1042 
1043             flock.l_type = F_RDLCK;
1044             flock.l_whence = SEEK_SET;
1045             flock.l_start = 0;
1046             flock.l_len = 0;
1047             while (fcntl (lockfd, F_SETLKW, &flock) == -1)
1048                 if (errno != EINTR)
1049                 {
1050                     int saved_errno = errno;
1051                     close (lockfd);
1052                     errno = saved_errno;
1053                     return NULL;
1054                 }
1055         }
1056         else if (errno != ENOENT)
1057             return NULL;
1058 #        endif
1059 
1060         errno = 0;
1061         fp = fopen (table, "r");
1062         if (fp == NULL)
1063             ret = errno;
1064         else
1065         {
1066             while ((ret = getmntent (fp, &mnt)) == 0)
1067             {
1068                 me = g_malloc (sizeof (*me));
1069                 me->me_devname = g_strdup (mnt.mnt_special);
1070                 me->me_mountdir = g_strdup (mnt.mnt_mountp);
1071                 me->me_mntroot = NULL;
1072                 me->me_type = g_strdup (mnt.mnt_fstype);
1073                 me->me_type_malloced = 1;
1074                 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1075                 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1076                 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1077 
1078                 mount_list = g_slist_prepend (mount_list, me);
1079             }
1080 
1081             ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1082             // Here ret = -1 means success, ret >= 0 means failure.
1083         }
1084 
1085         if (lockfd >= 0 && close (lockfd) != 0)
1086             ret = errno;
1087 
1088         if (ret >= 0)
1089         {
1090             errno = ret;
1091             goto free_then_fail;
1092         }
1093     }
1094 #    endif
1095 
1096 #    ifdef MOUNTED_VMOUNT  // AIX
1097     {
1098         int bufsize;
1099         void *entries;
1100         char *thisent;
1101         struct vmount *vmp;
1102         int n_entries;
1103         int i;
1104 
1105         // Ask how many bytes to allocate for the mounted file system info.
1106         entries = &bufsize;
1107         if (mntctl (MCTL_QUERY, sizeof (bufsize), entries) != 0)
1108             return NULL;
1109         entries = g_malloc (bufsize);
1110 
1111         // Get the list of mounted file systems.
1112         n_entries = mntctl (MCTL_QUERY, bufsize, entries);
1113         if (n_entries < 0)
1114         {
1115             int saved_errno = errno;
1116 
1117             g_free (entries);
1118             errno = saved_errno;
1119             return NULL;
1120         }
1121 
1122         for (i = 0, thisent = entries; i < n_entries; i++, thisent += vmp->vmt_length)
1123         {
1124             char *options, *ignore;
1125 
1126             vmp = (struct vmount *) thisent;
1127             me = g_malloc (sizeof (*me));
1128             if (vmp->vmt_flags & MNT_REMOTE)
1129             {
1130                 char *host, *dir;
1131 
1132                 me->me_remote = 1;
1133                 // Prepend the remote dirname.
1134                 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1135                 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1136                 me->me_devname = g_strconcat (host, ":", dir, (char *) NULL);
1137             }
1138             else
1139             {
1140                 me->me_remote = 0;
1141                 me->me_devname = g_strdup (thisent + vmp->vmt_data[VMT_OBJECT].vmt_off);
1142             }
1143             me->me_mountdir = g_strdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1144             me->me_mntroot = NULL;
1145             me->me_type = g_strdup (fstype_to_string (vmp->vmt_gfstype));
1146             me->me_type_malloced = 1;
1147             options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1148             ignore = strstr (options, "ignore");
1149             me->me_dummy = (ignore && (ignore == options || ignore[-1] == ',')
1150                             && (ignore[sizeof ("ignore") - 1] == ','
1151                                 || ignore[sizeof ("ignore") - 1] == '\0'));
1152             me->me_dev = (dev_t) (-1);  // vmt_fsid might be the info we want.
1153 
1154             mount_list = g_slist_prepend (mount_list, me);
1155         }
1156         g_free (entries);
1157     }
1158 #    endif
1159 
1160 #    ifdef MOUNTED_INTERIX_STATVFS  // Interix
1161     {
1162         DIR *dirp = opendir ("/dev/fs");
1163         char node[9 + NAME_MAX];
1164 
1165         if (!dirp)
1166             goto free_then_fail;
1167 
1168         while (1)
1169         {
1170             struct statvfs dev;
1171             struct dirent entry;
1172             struct dirent *result;
1173 
1174             if (readdir_r (dirp, &entry, &result) || result == NULL)
1175                 break;
1176 
1177             strcpy (node, "/dev/fs/");
1178             strcat (node, entry.d_name);
1179 
1180             if (statvfs (node, &dev) == 0)
1181             {
1182                 me = g_malloc (sizeof *me);
1183                 me->me_devname = g_strdup (dev.f_mntfromname);
1184                 me->me_mountdir = g_strdup (dev.f_mntonname);
1185                 me->me_mntroot = NULL;
1186                 me->me_type = g_strdup (dev.f_fstypename);
1187                 me->me_type_malloced = 1;
1188                 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1189                 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1190                 me->me_dev = (dev_t) (-1);  // Magic; means not known yet.
1191 
1192                 mount_list = g_slist_prepend (mount_list, me);
1193             }
1194         }
1195         closedir (dirp);
1196     }
1197 #    endif
1198 
1199     return g_slist_reverse (mount_list);
1200 
1201 free_then_fail:
1202     MC_UNUSED;
1203     {
1204         int saved_errno = errno;
1205 
1206         g_slist_free_full (mount_list, (GDestroyNotify) free_mount_entry);
1207 
1208         errno = saved_errno;
1209         return NULL;
1210     }
1211 }
1212 #endif
1213 
1214 /* --------------------------------------------------------------------------------------------- */
1215 
1216 #ifdef HAVE_INFOMOUNT_QNX
1217 /**
1218  ** QNX has no [gs]etmnt*(), [gs]etfs*(), or /etc/mnttab, but can do
1219  ** this via the following code.
1220  ** Note that, as this is based on CWD, it only fills one mount_entry
1221  ** structure. See my_statfs() below for the "other side" of this hack.
1222  */
1223 
1224 static GSList *
1225 read_file_system_list (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1226 {
1227     struct _disk_entry de;
1228     struct statfs fs;
1229     int i, fd;
1230     char *tp, dev[_POSIX_NAME_MAX], dir[_POSIX_PATH_MAX];
1231     struct mount_entry *me = NULL;
1232     static GSList *list = NULL;
1233 
1234     if (list != NULL)
1235     {
1236         me = (struct mount_entry *) list->data;
1237 
1238         g_free (me->me_devname);
1239         g_free (me->me_mountdir);
1240         g_free (me->me_mntroot);
1241         g_free (me->me_type);
1242     }
1243     else
1244     {
1245         me = (struct mount_entry *) g_malloc (sizeof (struct mount_entry));
1246         list = g_slist_prepend (list, me);
1247     }
1248 
1249     if (!getcwd (dir, _POSIX_PATH_MAX))
1250         return (NULL);
1251 
1252     fd = open (dir, O_RDONLY);
1253     if (fd == -1)
1254         return (NULL);
1255 
1256     i = disk_get_entry (fd, &de);
1257 
1258     close (fd);
1259 
1260     if (i == -1)
1261         return (NULL);
1262 
1263     switch (de.disk_type)
1264     {
1265     case _UNMOUNTED:
1266         tp = "unmounted";
1267         break;
1268     case _FLOPPY:
1269         tp = "Floppy";
1270         break;
1271     case _HARD:
1272         tp = "Hard";
1273         break;
1274     case _RAMDISK:
1275         tp = "Ram";
1276         break;
1277     case _REMOVABLE:
1278         tp = "Removable";
1279         break;
1280     case _TAPE:
1281         tp = "Tape";
1282         break;
1283     case _CDROM:
1284         tp = "CDROM";
1285         break;
1286     default:
1287         tp = "unknown";
1288     }
1289 
1290     if (fsys_get_mount_dev (dir, &dev) == -1)
1291         return (NULL);
1292 
1293     if (fsys_get_mount_pt (dev, &dir) == -1)
1294         return (NULL);
1295 
1296     me->me_devname = g_strdup (dev);
1297     me->me_mountdir = g_strdup (dir);
1298     me->me_mntroot = NULL;
1299     me->me_type = g_strdup (tp);
1300     me->me_dev = de.disk_type;
1301 
1302 #    ifdef DEBUG
1303     fprintf (stderr,
1304              "disk_get_entry():\n\tdisk_type=%d (%s)\n\tdriver_name='%-*.*s'\n\tdisk_drv=%d\n",
1305              de.disk_type, tp, _DRIVER_NAME_LEN, _DRIVER_NAME_LEN, de.driver_name, de.disk_drv);
1306     fprintf (stderr, "fsys_get_mount_dev():\n\tdevice='%s'\n", dev);
1307     fprintf (stderr, "fsys_get_mount_pt():\n\tmount point='%s'\n", dir);
1308 #    endif
1309 
1310     return (list);
1311 }
1312 #endif
1313 
1314 /* --------------------------------------------------------------------------------------------- */
1315 
1316 #ifdef HAVE_INFOMOUNT
1317 /* Fill in the fields of FSP with information about space usage for
1318    the file system on which FILE resides.
1319    DISK is the device on which FILE is mounted, for space-getting
1320    methods that need to know it.
1321    Return 0 if successful, -1 if not.  When returning -1, ensure that
1322    ERRNO is either a system error value, or zero if DISK is NULL
1323    on a system that requires a non-NULL value.  */
1324 static int
1325 get_fs_usage (char const *file, char const *disk, struct fs_usage *fsp)
     /* [previous][next][first][last][top][bottom][index][help]  */
1326 {
1327 #    ifdef STAT_STATVFS  // POSIX, except pre-2.6.36 glibc/Linux
1328 
1329     if (statvfs_works ())
1330     {
1331         struct statvfs vfsd;
1332 
1333         if (statvfs (file, &vfsd) < 0)
1334             return -1;
1335 
1336         // f_frsize isn't guaranteed to be supported.
1337         fsp->fsu_blocksize = (vfsd.f_frsize ? PROPAGATE_ALL_ONES (vfsd.f_frsize)
1338                                             : PROPAGATE_ALL_ONES (vfsd.f_bsize));
1339 
1340         fsp->fsu_blocks = PROPAGATE_ALL_ONES (vfsd.f_blocks);
1341         fsp->fsu_bfree = PROPAGATE_ALL_ONES (vfsd.f_bfree);
1342         fsp->fsu_bavail = PROPAGATE_TOP_BIT (vfsd.f_bavail);
1343         fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (vfsd.f_bavail) != 0;
1344         fsp->fsu_files = PROPAGATE_ALL_ONES (vfsd.f_files);
1345         fsp->fsu_ffree = PROPAGATE_ALL_ONES (vfsd.f_ffree);
1346     }
1347     else
1348 #    endif
1349 
1350     {
1351 #    if defined STAT_STATVFS64  // AIX
1352 
1353         struct statvfs64 fsd;
1354 
1355         if (statvfs64 (file, &fsd) < 0)
1356             return -1;
1357 
1358         // f_frsize isn't guaranteed to be supported.
1359         fsp->fsu_blocksize =
1360             fsd.f_frsize ? PROPAGATE_ALL_ONES (fsd.f_frsize) : PROPAGATE_ALL_ONES (fsd.f_bsize);
1361 
1362 #    elif defined STAT_STATFS3_OSF1  // OSF/1
1363 
1364         struct statfs fsd;
1365 
1366         if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
1367             return -1;
1368 
1369         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1370 
1371 #    elif defined STAT_STATFS2_FRSIZE  // 2.6 < glibc/Linux < 2.6.36
1372 
1373         struct statfs fsd;
1374 
1375         if (statfs (file, &fsd) < 0)
1376             return -1;
1377 
1378         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
1379 
1380 #    elif defined STAT_STATFS2_BSIZE /* glibc/Linux < 2.6, 4.3BSD, SunOS 4,                        \
1381                                         Mac OS X < 10.4, FreeBSD < 5.0,                            \
1382                                         NetBSD < 3.0, OpenBSD < 4.4 */
1383 
1384         struct statfs fsd;
1385 
1386         if (statfs (file, &fsd) < 0)
1387             return -1;
1388 
1389         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1390 
1391 #        ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1392 
1393         /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the
1394            struct statfs are truncated to 2GB.  These conditions detect that
1395            truncation, presumably without botching the 4.1.1 case, in which
1396            the values are not truncated.  The correct counts are stored in
1397            undocumented spare fields.  */
1398         if (fsd.f_blocks == 0x7fffffff / fsd.f_bsize && fsd.f_spare[0] > 0)
1399         {
1400             fsd.f_blocks = fsd.f_spare[0];
1401             fsd.f_bfree = fsd.f_spare[1];
1402             fsd.f_bavail = fsd.f_spare[2];
1403         }
1404 #        endif
1405 
1406 #    elif defined STAT_STATFS2_FSIZE  // 4.4BSD and older NetBSD
1407 
1408         struct statfs fsd;
1409 
1410         if (statfs (file, &fsd) < 0)
1411             return -1;
1412 
1413         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1414 
1415 #    elif defined STAT_STATFS4  // SVR3, old Irix
1416 
1417         struct statfs fsd;
1418 
1419         if (statfs (file, &fsd, sizeof (fsd), 0) < 0)
1420             return -1;
1421 
1422         /* Empirically, the block counts on most SVR3 and SVR3-derived
1423            systems seem to always be in terms of 512-byte blocks,
1424            no matter what value f_bsize has.  */
1425         fsp->fsu_blocksize = 512;
1426 #    endif
1427 
1428 #    if (defined STAT_STATVFS64 || defined STAT_STATFS3_OSF1 || defined STAT_STATFS2_FRSIZE        \
1429          || defined STAT_STATFS2_BSIZE || defined STAT_STATFS2_FSIZE || defined STAT_STATFS4)
1430 
1431         fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.f_blocks);
1432         fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.f_bfree);
1433         fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.f_bavail);
1434         fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.f_bavail) != 0;
1435         fsp->fsu_files = PROPAGATE_ALL_ONES (fsd.f_files);
1436         fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.f_ffree);
1437 
1438 #    endif
1439     }
1440 
1441     (void) disk;  // avoid argument-unused warning
1442 
1443     return 0;
1444 }
1445 #endif
1446 
1447 /* --------------------------------------------------------------------------------------------- */
1448 /*** public functions ****************************************************************************/
1449 /* --------------------------------------------------------------------------------------------- */
1450 
1451 void
1452 free_my_statfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1453 {
1454 #ifdef HAVE_INFOMOUNT_LIST
1455     g_clear_slist (&mc_mount_list, (GDestroyNotify) free_mount_entry);
1456 #endif
1457 }
1458 
1459 /* --------------------------------------------------------------------------------------------- */
1460 
1461 void
1462 init_my_statfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1463 {
1464 #ifdef HAVE_INFOMOUNT_LIST
1465     free_my_statfs ();
1466     mc_mount_list = read_file_system_list ();
1467 #endif
1468 }
1469 
1470 /* --------------------------------------------------------------------------------------------- */
1471 
1472 void
1473 my_statfs (struct my_statfs *myfs_stats, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
1474 {
1475 #ifdef HAVE_INFOMOUNT_LIST
1476     size_t len = 0;
1477     struct mount_entry *entry = NULL;
1478     GSList *temp;
1479     struct fs_usage fs_use;
1480 
1481     for (temp = mc_mount_list; temp != NULL; temp = g_slist_next (temp))
1482     {
1483         struct mount_entry *me;
1484         size_t i;
1485 
1486         me = (struct mount_entry *) temp->data;
1487         i = strlen (me->me_mountdir);
1488         if (i > len && (strncmp (path, me->me_mountdir, i) == 0)
1489             && (entry == NULL || IS_PATH_SEP (path[i]) || path[i] == '\0'))
1490         {
1491             len = i;
1492             entry = me;
1493         }
1494     }
1495 
1496     if (entry != NULL)
1497     {
1498         memset (&fs_use, 0, sizeof (fs_use));
1499         get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1500 
1501         myfs_stats->type = entry->me_dev;
1502         myfs_stats->typename = entry->me_type;
1503         myfs_stats->mpoint = entry->me_mountdir;
1504         myfs_stats->mroot = entry->me_mntroot;
1505         myfs_stats->device = entry->me_devname;
1506         myfs_stats->avail =
1507             ((uintmax_t) (getuid () ? fs_use.fsu_bavail : fs_use.fsu_bfree) * fs_use.fsu_blocksize)
1508             >> 10;
1509         myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1510         myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1511         myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1512     }
1513     else
1514 #endif
1515 
1516 #ifdef HAVE_INFOMOUNT_QNX
1517         /*
1518          ** This is the "other side" of the hack to read_file_system_list() above.
1519          ** It's not the most efficient approach, but consumes less memory. It
1520          ** also accommodates QNX's ability to mount filesystems on the fly.
1521          */
1522         struct mount_entry *entry;
1523     struct fs_usage fs_use;
1524 
1525     entry = read_file_system_list ();
1526     if (entry != NULL)
1527     {
1528         get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1529 
1530         myfs_stats->type = entry->me_dev;
1531         myfs_stats->typename = entry->me_type;
1532         myfs_stats->mpoint = entry->me_mountdir;
1533         myfs_stats->mroot = entry->me_mntroot;
1534         myfs_stats->device = entry->me_devname;
1535 
1536         myfs_stats->avail = ((uintmax_t) fs_use.fsu_bfree * fs_use.fsu_blocksize) >> 10;
1537         myfs_stats->total = ((uintmax_t) fs_use.fsu_blocks * fs_use.fsu_blocksize) >> 10;
1538         myfs_stats->nfree = (uintmax_t) fs_use.fsu_ffree;
1539         myfs_stats->nodes = (uintmax_t) fs_use.fsu_files;
1540     }
1541     else
1542 #endif
1543     {
1544         myfs_stats->type = 0;
1545         myfs_stats->mpoint = "unknown";
1546         myfs_stats->device = "unknown";
1547         myfs_stats->avail = 0;
1548         myfs_stats->total = 0;
1549         myfs_stats->nfree = 0;
1550         myfs_stats->nodes = 0;
1551     }
1552 }
1553 
1554 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */