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

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