Manual pages: mcmcdiffmceditmcview

root/src/filemanager/mountlist.c

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

DEFINITIONS

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

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

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