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

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