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

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