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-2024
   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 <http://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 /* MOUNTED_GETFSSTAT */
  70 #endif /* STAT_STATVFS || STAT_STATVFS64 */
  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              \
 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)
 218 
 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
 231 
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 239 {
 240     (void) fs_type;
 241 
 242     if (fs_name[0] && fs_name[1] == ':')
 243     {
 244         char drive[4];
 245         sprintf (drive, "%c:\\", fs_name[0]);
 246         switch (GetDriveType (drive))
 247         {
 248         case DRIVE_REMOVABLE:
 249         case DRIVE_FIXED:
 250         case DRIVE_CDROM:
 251         case DRIVE_RAMDISK:
 252             return 0;
 253         }
 254     }
 255     return 1;
 256 }
 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
 282 
 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))
 293 
 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)))
 296 
 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))
 305 
 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
 316 
 317 /*** file scope type declarations ****************************************************************/
 318 
 319 /* A mount table entry. */
 320 struct mount_entry
 321 {
 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. */
 331 };
 332 
 333 struct fs_usage
 334 {
 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. */
 342 };
 343 
 344 /*** forward declarations (file scope functions) *************************************************/
 345 
 346 /*** file scope variables ************************************************************************/
 347 
 348 #ifdef HAVE_INFOMOUNT_LIST
 349 static GSList *mc_mount_list = NULL;
 350 #endif /* HAVE_INFOMOUNT_LIST */
 351 
 352 /* --------------------------------------------------------------------------------------------- */
 353 /*** file scope functions ************************************************************************/
 354 /* --------------------------------------------------------------------------------------------- */
 355 
 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)
     /* [previous][next][first][last][top][bottom][index][help]  */
 363 {
 364 #if ! (defined __linux__ && (defined __GLIBC__ || defined __UCLIBC__))
 365     return 1;
 366 #else
 367     static int statvfs_works_cache = -1;
 368     struct utsname name;
 369 
 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
 374 }
 375 #endif
 376 
 377 /* --------------------------------------------------------------------------------------------- */
 378 
 379 #ifdef HAVE_INFOMOUNT_LIST
 380 static void
 381 free_mount_entry (struct mount_entry *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 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);
 391 }
 392 
 393 /* --------------------------------------------------------------------------------------------- */
 394 
 395 #ifdef MOUNTED_GETMNTINFO       /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
 396 
 397 #ifndef HAVE_STRUCT_STATFS_F_FSTYPENAME
 398 static char *
 399 fstype_to_string (short int t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 400 {
 401     switch (t)
 402     {
 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 "?";
 510     }
 511 }
 512 #endif /* ! HAVE_STRUCT_STATFS_F_FSTYPENAME */
 513 
 514 /* --------------------------------------------------------------------------------------------- */
 515 
 516 static char *
 517 fsp_to_string (const struct statfs *fsp)
     /* [previous][next][first][last][top][bottom][index][help]  */
 518 {
 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
 524 }
 525 #endif /* MOUNTED_GETMNTINFO */
 526 
 527 /* --------------------------------------------------------------------------------------------- */
 528 
 529 #ifdef MOUNTED_VMOUNT           /* AIX */
 530 static char *
 531 fstype_to_string (int t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 532 {
 533     struct vfs_ent *e;
 534 
 535     e = getvfsbytype (t);
 536     if (!e || !e->vfsent_name)
 537         return "none";
 538     else
 539         return e->vfsent_name;
 540 }
 541 #endif /* MOUNTED_VMOUNT */
 542 
 543 /* --------------------------------------------------------------------------------------------- */
 544 
 545 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
 546 
 547 /* Return the device number from MOUNT_OPTIONS, if possible.
 548    Otherwise return (dev_t) -1.  */
 549 
 550 /* --------------------------------------------------------------------------------------------- */
 551 
 552 static dev_t
 553 dev_from_mount_options (char const *mount_options)
     /* [previous][next][first][last][top][bottom][index][help]  */
 554 {
 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);
 561 
 562     if (devopt)
 563     {
 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;
 573     }
 574 #endif
 575 
 576     (void) mount_options;
 577     return -1;
 578 }
 579 
 580 #endif
 581 
 582 /* --------------------------------------------------------------------------------------------- */
 583 
 584 #if defined MOUNTED_GETMNTENT1 && (defined __linux__ || defined __ANDROID__)    /* GNU/Linux, Android */
 585 
 586 /* Unescape the paths in mount tables.
 587    STR is updated in place.  */
 588 static void
 589 unescape_tab (char *str)
     /* [previous][next][first][last][top][bottom][index][help]  */
 590 {
 591     size_t i, j = 0;
 592     size_t len;
 593 
 594     len = strlen (str) + 1;
 595 
 596     for (i = 0; i < len; i++)
 597     {
 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')
 601         {
 602             str[j++] = (str[i + 1] - '0') * 64 + (str[i + 2] - '0') * 8 + (str[i + 3] - '0');
 603             i += 3;
 604         }
 605         else
 606             str[j++] = str[i];
 607     }
 608 }
 609 #endif
 610 
 611 /* --------------------------------------------------------------------------------------------- */
 612 
 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. */
 615 
 616 static GSList *
 617 read_file_system_list (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 618 {
 619     GSList *mount_list = NULL;
 620     struct mount_entry *me;
 621 
 622 #ifdef MOUNTED_GETMNTENT1       /* glibc, HP-UX, IRIX, Cygwin, Android,
 623                                    also (obsolete) 4.3BSD, SunOS */
 624     {
 625         FILE *fp;
 626 
 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";
 633 
 634         fp = fopen (mountinfo, "r");
 635         if (fp != NULL)
 636         {
 637             char *line = NULL;
 638             size_t buf_size = 0;
 639 
 640             while (getline (&line, &buf_size, fp) != -1)
 641             {
 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;
 648 
 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);
 656 
 657                 if (rc != 3 && rc != 7) /* 7 if %n included in count.  */
 658                     continue;
 659 
 660                 /* skip optional fields, terminated by " - "  */
 661                 dash = strstr (line + target_e, " - ");
 662                 if (dash == NULL)
 663                     continue;
 664 
 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;
 672 
 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);
 681 
 682                 me = g_malloc (sizeof *me);
 683 
 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);
 696 
 697                 mount_list = g_slist_prepend (mount_list, me);
 698             }
 699 
 700             free (line);
 701 
 702             if (ferror (fp) != 0)
 703             {
 704                 int saved_errno = errno;
 705 
 706                 fclose (fp);
 707                 errno = saved_errno;
 708                 goto free_then_fail;
 709             }
 710 
 711             if (fclose (fp) == EOF)
 712                 goto free_then_fail;
 713         }
 714         else                    /* fallback to /proc/self/mounts (/etc/mtab).  */
 715 #endif /* __linux __ || __ANDROID__ */
 716         {
 717             struct mntent *mnt;
 718             const char *table = MOUNTED;
 719 
 720             fp = setmntent (table, "r");
 721             if (fp == NULL)
 722                 return NULL;
 723 
 724             while ((mnt = getmntent (fp)) != NULL)
 725             {
 726                 gboolean bind;
 727 
 728                 bind = hasmntopt (mnt, "bind") != NULL;
 729 
 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);
 739 
 740                 mount_list = g_slist_prepend (mount_list, me);
 741             }
 742 
 743             if (endmntent (fp) == 0)
 744                 goto free_then_fail;
 745         }
 746     }
 747 #endif /* MOUNTED_GETMNTENT1. */
 748 
 749 #ifdef MOUNTED_GETMNTINFO       /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
 750     {
 751         struct statfs *fsp;
 752         int entries;
 753 
 754         entries = getmntinfo (&fsp, MNT_NOWAIT);
 755         if (entries < 0)
 756             return NULL;
 757         for (; entries-- > 0; fsp++)
 758         {
 759             char *fs_type = fsp_to_string (fsp);
 760 
 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. */
 770 
 771             mount_list = g_slist_prepend (mount_list, me);
 772         }
 773     }
 774 #endif /* MOUNTED_GETMNTINFO */
 775 
 776 #ifdef MOUNTED_GETMNTINFO2      /* NetBSD, Minix */
 777     {
 778         struct statvfs *fsp;
 779         int entries;
 780 
 781         entries = getmntinfo (&fsp, MNT_NOWAIT);
 782         if (entries < 0)
 783             return NULL;
 784         for (; entries-- > 0; fsp++)
 785         {
 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. */
 795 
 796             mount_list = g_slist_prepend (mount_list, me);
 797         }
 798     }
 799 #endif /* MOUNTED_GETMNTINFO2 */
 800 
 801 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
 802     {
 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.  */
 812 
 813         DIR *dirp;
 814         struct rootdir_entry
 815         {
 816             char *name;
 817             dev_t dev;
 818             ino_t ino;
 819             struct rootdir_entry *next;
 820         };
 821         struct rootdir_entry *rootdir_list;
 822         struct rootdir_entry **rootdir_tail;
 823         int32 pos;
 824         dev_t dev;
 825         fs_info fi;
 826 
 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)
 832         {
 833             struct dirent *d;
 834 
 835             while ((d = readdir (dirp)) != NULL)
 836             {
 837                 char *name;
 838                 struct stat statbuf;
 839 
 840                 if (DIR_IS_DOT (d->d_name))
 841                     continue;
 842 
 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);
 847 
 848                 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
 849                 {
 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;
 854 
 855                     /* Add to the linked list.  */
 856                     *rootdir_tail = re;
 857                     rootdir_tail = &re->next;
 858                 }
 859                 else
 860                     g_free (name);
 861             }
 862             closedir (dirp);
 863         }
 864         *rootdir_tail = NULL;
 865 
 866         for (pos = 0; (dev = next_dev (&pos)) >= 0;)
 867             if (fs_stat_dev (dev, &fi) >= 0)
 868             {
 869                 /* Note: fi.dev == dev. */
 870                 struct rootdir_entry *re;
 871 
 872                 for (re = rootdir_list; re; re = re->next)
 873                     if (re->dev == fi.dev && re->ino == fi.root)
 874                         break;
 875 
 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;
 886 
 887                 mount_list = g_slist_prepend (mount_list, me);
 888             }
 889 
 890         while (rootdir_list != NULL)
 891         {
 892             struct rootdir_entry *re = rootdir_list;
 893 
 894             rootdir_list = re->next;
 895             g_free (re->name);
 896             g_free (re);
 897         }
 898     }
 899 #endif /* MOUNTED_FS_STAT_DEV */
 900 
 901 #ifdef MOUNTED_GETFSSTAT        /*  OSF/1, also (obsolete) Apple Darwin 1.3 */
 902     {
 903         int numsys, counter;
 904         size_t bufsize;
 905         struct statfs *stats;
 906 
 907         numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
 908         if (numsys < 0)
 909             return NULL;
 910         if (SIZE_MAX / sizeof (*stats) <= numsys)
 911         {
 912             fprintf (stderr, "%s\n", _("Memory exhausted!"));
 913             exit (EXIT_FAILURE);
 914         }
 915 
 916         bufsize = (1 + numsys) * sizeof (*stats);
 917         stats = g_malloc (bufsize);
 918         numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
 919 
 920         if (numsys < 0)
 921         {
 922             g_free (stats);
 923             return NULL;
 924         }
 925 
 926         for (counter = 0; counter < numsys; counter++)
 927         {
 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. */
 937 
 938             mount_list = g_slist_prepend (mount_list, me);
 939         }
 940 
 941         g_free (stats);
 942     }
 943 #endif /* MOUNTED_GETFSSTAT */
 944 
 945 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
 946     {
 947         struct mnttab mnt;
 948         char *table = "/etc/mnttab";
 949         FILE *fp;
 950 
 951         fp = fopen (table, "r");
 952         if (fp == NULL)
 953             return NULL;
 954 
 955         while (fread (&mnt, sizeof (mnt), 1, fp) > 0)
 956         {
 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;
 964             {
 965                 struct statfs fsd;
 966                 char typebuf[FSTYPSZ];
 967 
 968                 if (statfs (me->me_mountdir, &fsd, sizeof (fsd), 0) != -1
 969                     && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
 970                 {
 971                     me->me_type = g_strdup (typebuf);
 972                     me->me_type_malloced = 1;
 973                 }
 974             }
 975             me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
 976             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
 977 
 978             mount_list = g_slist_prepend (mount_list, me);
 979         }
 980 
 981         if (ferror (fp))
 982         {
 983             /* The last fread() call must have failed.  */
 984             int saved_errno = errno;
 985 
 986             fclose (fp);
 987             errno = saved_errno;
 988             goto free_then_fail;
 989         }
 990 
 991         if (fclose (fp) == EOF)
 992             goto free_then_fail;
 993     }
 994 #endif /* MOUNTED_FREAD_FSTYP */
 995 
 996 #ifdef MOUNTED_GETEXTMNTENT     /* Solaris >= 8 */
 997     {
 998         struct extmnttab mnt;
 999         const char *table = MNTTAB;
1000         FILE *fp;
1001         int ret;
1002 
1003         /* No locking is needed, because the contents of /etc/mnttab is generated by the kernel. */
1004 
1005         errno = 0;
1006         fp = fopen (table, "r");
1007         if (fp == NULL)
1008             ret = errno;
1009         else
1010         {
1011             while ((ret = getextmntent (fp, &mnt, 1)) == 0)
1012             {
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);
1022 
1023                 mount_list = g_slist_prepend (mount_list, me);
1024             }
1025 
1026             ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1027             /* Here ret = -1 means success, ret >= 0 means failure. */
1028         }
1029 
1030         if (ret >= 0)
1031         {
1032             errno = ret;
1033             goto free_then_fail;
1034         }
1035     }
1036 #endif /* MOUNTED_GETEXTMNTENT */
1037 
1038 #ifdef MOUNTED_GETMNTENT2       /* Solaris < 8, also (obsolete) SVR4 */
1039     {
1040         struct mnttab mnt;
1041         const char *table = MNTTAB;
1042         FILE *fp;
1043         int ret;
1044         int lockfd = -1;
1045 
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)
1056         {
1057             struct flock flock;
1058 
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)
1065                 {
1066                     int saved_errno = errno;
1067                     close (lockfd);
1068                     errno = saved_errno;
1069                     return NULL;
1070                 }
1071         }
1072         else if (errno != ENOENT)
1073             return NULL;
1074 #endif
1075 
1076         errno = 0;
1077         fp = fopen (table, "r");
1078         if (fp == NULL)
1079             ret = errno;
1080         else
1081         {
1082             while ((ret = getmntent (fp, &mnt)) == 0)
1083             {
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);
1093 
1094                 mount_list = g_slist_prepend (mount_list, me);
1095             }
1096 
1097             ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1098             /* Here ret = -1 means success, ret >= 0 means failure. */
1099         }
1100 
1101         if (lockfd >= 0 && close (lockfd) != 0)
1102             ret = errno;
1103 
1104         if (ret >= 0)
1105         {
1106             errno = ret;
1107             goto free_then_fail;
1108         }
1109     }
1110 #endif /* MOUNTED_GETMNTENT2.  */
1111 
1112 #ifdef MOUNTED_VMOUNT           /* AIX */
1113     {
1114         int bufsize;
1115         void *entries;
1116         char *thisent;
1117         struct vmount *vmp;
1118         int n_entries;
1119         int i;
1120 
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);
1126 
1127         /* Get the list of mounted file systems.  */
1128         n_entries = mntctl (MCTL_QUERY, bufsize, entries);
1129         if (n_entries < 0)
1130         {
1131             int saved_errno = errno;
1132 
1133             g_free (entries);
1134             errno = saved_errno;
1135             return NULL;
1136         }
1137 
1138         for (i = 0, thisent = entries; i < n_entries; i++, thisent += vmp->vmt_length)
1139         {
1140             char *options, *ignore;
1141 
1142             vmp = (struct vmount *) thisent;
1143             me = g_malloc (sizeof (*me));
1144             if (vmp->vmt_flags & MNT_REMOTE)
1145             {
1146                 char *host, *dir;
1147 
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);
1153             }
1154             else
1155             {
1156                 me->me_remote = 0;
1157                 me->me_devname = g_strdup (thisent + vmp->vmt_data[VMT_OBJECT].vmt_off);
1158             }
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.  */
1170 
1171             mount_list = g_slist_prepend (mount_list, me);
1172         }
1173         g_free (entries);
1174     }
1175 #endif /* MOUNTED_VMOUNT. */
1176 
1177 #ifdef MOUNTED_INTERIX_STATVFS  /* Interix */
1178     {
1179         DIR *dirp = opendir ("/dev/fs");
1180         char node[9 + NAME_MAX];
1181 
1182         if (!dirp)
1183             goto free_then_fail;
1184 
1185         while (1)
1186         {
1187             struct statvfs dev;
1188             struct dirent entry;
1189             struct dirent *result;
1190 
1191             if (readdir_r (dirp, &entry, &result) || result == NULL)
1192                 break;
1193 
1194             strcpy (node, "/dev/fs/");
1195             strcat (node, entry.d_name);
1196 
1197             if (statvfs (node, &dev) == 0)
1198             {
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. */
1208 
1209                 mount_list = g_slist_prepend (mount_list, me);
1210             }
1211         }
1212         closedir (dirp);
1213     }
1214 #endif /* MOUNTED_INTERIX_STATVFS */
1215 
1216     return g_slist_reverse (mount_list);
1217 
1218   free_then_fail:
1219     {
1220         int saved_errno = errno;
1221 
1222         g_slist_free_full (mount_list, (GDestroyNotify) free_mount_entry);
1223 
1224         errno = saved_errno;
1225         return NULL;
1226     }
1227 }
1228 #endif /* HAVE_INFOMOUNT_LIST */
1229 
1230 /* --------------------------------------------------------------------------------------------- */
1231 
1232 #ifdef HAVE_INFOMOUNT_QNX
1233 /**
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.
1238  */
1239 
1240 static GSList *
1241 read_file_system_list (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1242 {
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;
1249 
1250     if (list != NULL)
1251     {
1252         me = (struct mount_entry *) list->data;
1253 
1254         g_free (me->me_devname);
1255         g_free (me->me_mountdir);
1256         g_free (me->me_mntroot);
1257         g_free (me->me_type);
1258     }
1259     else
1260     {
1261         me = (struct mount_entry *) g_malloc (sizeof (struct mount_entry));
1262         list = g_slist_prepend (list, me);
1263     }
1264 
1265     if (!getcwd (dir, _POSIX_PATH_MAX))
1266         return (NULL);
1267 
1268     fd = open (dir, O_RDONLY);
1269     if (fd == -1)
1270         return (NULL);
1271 
1272     i = disk_get_entry (fd, &de);
1273 
1274     close (fd);
1275 
1276     if (i == -1)
1277         return (NULL);
1278 
1279     switch (de.disk_type)
1280     {
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";
1304     }
1305 
1306     if (fsys_get_mount_dev (dir, &dev) == -1)
1307         return (NULL);
1308 
1309     if (fsys_get_mount_pt (dev, &dir) == -1)
1310         return (NULL);
1311 
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;
1317 
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 */
1325 
1326     return (list);
1327 }
1328 #endif /* HAVE_INFOMOUNT_QNX */
1329 
1330 /* --------------------------------------------------------------------------------------------- */
1331 
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)
     /* [previous][next][first][last][top][bottom][index][help]  */
1342 {
1343 #ifdef STAT_STATVFS             /* POSIX, except pre-2.6.36 glibc/Linux */
1344 
1345     if (statvfs_works ())
1346     {
1347         struct statvfs vfsd;
1348 
1349         if (statvfs (file, &vfsd) < 0)
1350             return -1;
1351 
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));
1356 
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);
1363     }
1364     else
1365 #endif
1366 
1367     {
1368 #if defined STAT_STATVFS64      /* AIX */
1369 
1370         struct statvfs64 fsd;
1371 
1372         if (statvfs64 (file, &fsd) < 0)
1373             return -1;
1374 
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* */
1381 
1382 #elif defined STAT_STATFS3_OSF1 /* OSF/1 */
1383 
1384         struct statfs fsd;
1385 
1386         if (statfs (file, &fsd, sizeof (struct statfs)) != 0)
1387             return -1;
1388 
1389         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1390 
1391 #elif defined STAT_STATFS2_FRSIZE       /* 2.6 < glibc/Linux < 2.6.36 */
1392 
1393         struct statfs fsd;
1394 
1395         if (statfs (file, &fsd) < 0)
1396             return -1;
1397 
1398         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_frsize);
1399 
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 */
1403 
1404         struct statfs fsd;
1405 
1406         if (statfs (file, &fsd) < 0)
1407             return -1;
1408 
1409         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_bsize);
1410 
1411 #ifdef STATFS_TRUNCATES_BLOCK_COUNTS
1412 
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)
1419         {
1420             fsd.f_blocks = fsd.f_spare[0];
1421             fsd.f_bfree = fsd.f_spare[1];
1422             fsd.f_bavail = fsd.f_spare[2];
1423         }
1424 #endif /* STATFS_TRUNCATES_BLOCK_COUNTS */
1425 
1426 #elif defined STAT_STATFS2_FSIZE        /* 4.4BSD and older NetBSD */
1427 
1428         struct statfs fsd;
1429 
1430         if (statfs (file, &fsd) < 0)
1431             return -1;
1432 
1433         fsp->fsu_blocksize = PROPAGATE_ALL_ONES (fsd.f_fsize);
1434 
1435 #elif defined STAT_STATFS4      /* SVR3, old Irix */
1436 
1437         struct statfs fsd;
1438 
1439         if (statfs (file, &fsd, sizeof (fsd), 0) < 0)
1440             return -1;
1441 
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
1447 
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)
1451 
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);
1458 
1459 #endif
1460     }
1461 
1462     (void) disk;                /* avoid argument-unused warning */
1463 
1464     return 0;
1465 }
1466 #endif /* HAVE_INFOMOUNT */
1467 
1468 /* --------------------------------------------------------------------------------------------- */
1469 /*** public functions ****************************************************************************/
1470 /* --------------------------------------------------------------------------------------------- */
1471 
1472 void
1473 free_my_statfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1474 {
1475 #ifdef HAVE_INFOMOUNT_LIST
1476     g_clear_slist (&mc_mount_list, (GDestroyNotify) free_mount_entry);
1477 #endif /* HAVE_INFOMOUNT_LIST */
1478 }
1479 
1480 /* --------------------------------------------------------------------------------------------- */
1481 
1482 void
1483 init_my_statfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1484 {
1485 #ifdef HAVE_INFOMOUNT_LIST
1486     free_my_statfs ();
1487     mc_mount_list = read_file_system_list ();
1488 #endif /* HAVE_INFOMOUNT_LIST */
1489 }
1490 
1491 /* --------------------------------------------------------------------------------------------- */
1492 
1493 void
1494 my_statfs (struct my_statfs *myfs_stats, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
1495 {
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;
1501 
1502     for (temp = mc_mount_list; temp != NULL; temp = g_slist_next (temp))
1503     {
1504         struct mount_entry *me;
1505         size_t i;
1506 
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'))
1511         {
1512             len = i;
1513             entry = me;
1514         }
1515     }
1516 
1517     if (entry != NULL)
1518     {
1519         memset (&fs_use, 0, sizeof (fs_use));
1520         get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1521 
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;
1533     }
1534     else
1535 #endif /* HAVE_INFOMOUNT_LIST */
1536 
1537 #ifdef HAVE_INFOMOUNT_QNX
1538         /*
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.
1542          */
1543         struct mount_entry *entry;
1544     struct fs_usage fs_use;
1545 
1546     entry = read_file_system_list ();
1547     if (entry != NULL)
1548     {
1549         get_fs_usage (entry->me_mountdir, NULL, &fs_use);
1550 
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;
1556 
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;
1561     }
1562     else
1563 #endif /* HAVE_INFOMOUNT_QNX */
1564     {
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;
1572     }
1573 }
1574 
1575 /* --------------------------------------------------------------------------------------------- */

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