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

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