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

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