1  /* mountlist.c -- return a list of mounted file systems
       2  
       3     Copyright (C) 1991-1992, 1997-2022 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <config.h>
      19  
      20  #include "mountlist.h"
      21  
      22  #include <limits.h>
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include <stdint.h>
      27  
      28  #include "xalloc.h"
      29  
      30  #include <errno.h>
      31  
      32  #include <fcntl.h>
      33  
      34  #include <unistd.h>
      35  
      36  #if HAVE_SYS_PARAM_H
      37  # include <sys/param.h>
      38  #endif
      39  
      40  #if MAJOR_IN_MKDEV
      41  # include <sys/mkdev.h>
      42  #elif MAJOR_IN_SYSMACROS
      43  # include <sys/sysmacros.h>
      44  #endif
      45  
      46  #if defined MOUNTED_GETFSSTAT   /* OSF/1, also (obsolete) Apple Darwin 1.3 */
      47  # if HAVE_SYS_UCRED_H
      48  #  include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
      49                        NGROUPS is used as an array dimension in ucred.h */
      50  #  include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
      51  # endif
      52  # if HAVE_SYS_MOUNT_H
      53  #  include <sys/mount.h>
      54  # endif
      55  # if HAVE_SYS_FS_TYPES_H
      56  #  include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
      57  # endif
      58  # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
      59  #  define FS_TYPE(Ent) ((Ent).f_fstypename)
      60  # else
      61  #  define FS_TYPE(Ent) mnt_names[(Ent).f_type]
      62  # endif
      63  #endif /* MOUNTED_GETFSSTAT */
      64  
      65  #ifdef MOUNTED_GETMNTENT1       /* glibc, HP-UX, IRIX, Cygwin, Android,
      66                                     also (obsolete) 4.3BSD, SunOS */
      67  # include <mntent.h>
      68  # include <sys/types.h>
      69  # if defined __ANDROID__        /* Android */
      70     /* Bionic versions from between 2014-01-09 and 2015-01-08 define MOUNTED to
      71        an incorrect value; older Bionic versions don't define it at all.  */
      72  #  undef MOUNTED
      73  #  define MOUNTED "/proc/mounts"
      74  # elif !defined MOUNTED
      75  #  if defined _PATH_MOUNTED     /* GNU libc  */
      76  #   define MOUNTED _PATH_MOUNTED
      77  #  endif
      78  #  if defined MNT_MNTTAB        /* HP-UX.  */
      79  #   define MOUNTED MNT_MNTTAB
      80  #  endif
      81  # endif
      82  #endif
      83  
      84  #ifdef MOUNTED_GETMNTINFO       /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
      85  # include <sys/mount.h>
      86  #endif
      87  
      88  #ifdef MOUNTED_GETMNTINFO2      /* NetBSD, Minix */
      89  # include <sys/statvfs.h>
      90  #endif
      91  
      92  #ifdef MOUNTED_FS_STAT_DEV      /* Haiku, also (obsolete) BeOS */
      93  # include <fs_info.h>
      94  # include <dirent.h>
      95  #endif
      96  
      97  #ifdef MOUNTED_FREAD_FSTYP      /* (obsolete) SVR3 */
      98  # include <mnttab.h>
      99  # include <sys/fstyp.h>
     100  # include <sys/statfs.h>
     101  #endif
     102  
     103  #ifdef MOUNTED_GETEXTMNTENT     /* Solaris >= 8 */
     104  # include <sys/mnttab.h>
     105  #endif
     106  
     107  #ifdef MOUNTED_GETMNTENT2       /* Solaris < 8, also (obsolete) SVR4 */
     108  # include <sys/mnttab.h>
     109  #endif
     110  
     111  #ifdef MOUNTED_VMOUNT           /* AIX */
     112  # include <fshelp.h>
     113  # include <sys/vfs.h>
     114  #endif
     115  
     116  #ifdef MOUNTED_INTERIX_STATVFS  /* Interix */
     117  # include <sys/statvfs.h>
     118  # include <dirent.h>
     119  #endif
     120  
     121  #if HAVE_SYS_MNTENT_H
     122  /* This is to get MNTOPT_IGNORE on e.g. SVR4.  */
     123  # include <sys/mntent.h>
     124  #endif
     125  
     126  #ifdef MOUNTED_GETMNTENT1
     127  # if !HAVE_SETMNTENT            /* Android <= 4.4 */
     128  #  define setmntent(fp,mode) fopen (fp, mode "e")
     129  # endif
     130  # if !HAVE_ENDMNTENT            /* Android <= 4.4 */
     131  #  define endmntent(fp) fclose (fp)
     132  # endif
     133  #endif
     134  
     135  #ifndef HAVE_HASMNTOPT
     136  # define hasmntopt(mnt, opt) ((char *) 0)
     137  #endif
     138  
     139  #undef MNT_IGNORE
     140  #ifdef MNTOPT_IGNORE
     141  # if defined __sun && defined __SVR4
     142  /* Solaris defines hasmntopt(struct mnttab *, char *)
     143     while it is otherwise hasmntopt(struct mnttab *, const char *).  */
     144  #  define MNT_IGNORE(M) hasmntopt (M, (char *) MNTOPT_IGNORE)
     145  # else
     146  #  define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
     147  # endif
     148  #else
     149  # define MNT_IGNORE(M) 0
     150  #endif
     151  
     152  /* Each of the FILE streams in this file is only used in a single thread.  */
     153  #include "unlocked-io.h"
     154  
     155  /* The results of opendir() in this file are not used with dirfd and fchdir,
     156     therefore save some unnecessary work in fchdir.c.  */
     157  #ifdef GNULIB_defined_opendir
     158  # undef opendir
     159  #endif
     160  #ifdef GNULIB_defined_closedir
     161  # undef closedir
     162  #endif
     163  
     164  #define ME_DUMMY_0(Fs_name, Fs_type)            \
     165    (strcmp (Fs_type, "autofs") == 0              \
     166     || strcmp (Fs_type, "proc") == 0             \
     167     || strcmp (Fs_type, "subfs") == 0            \
     168     /* for Linux 2.6/3.x */                      \
     169     || strcmp (Fs_type, "debugfs") == 0          \
     170     || strcmp (Fs_type, "devpts") == 0           \
     171     || strcmp (Fs_type, "fusectl") == 0          \
     172     || strcmp (Fs_type, "fuse.portal") == 0      \
     173     || strcmp (Fs_type, "mqueue") == 0           \
     174     || strcmp (Fs_type, "rpc_pipefs") == 0       \
     175     || strcmp (Fs_type, "sysfs") == 0            \
     176     /* FreeBSD, Linux 2.4 */                     \
     177     || strcmp (Fs_type, "devfs") == 0            \
     178     /* for NetBSD 3.0 */                         \
     179     || strcmp (Fs_type, "kernfs") == 0           \
     180     /* for Irix 6.5 */                           \
     181     || strcmp (Fs_type, "ignore") == 0)
     182  
     183  /* Historically, we have marked as "dummy" any file system of type "none",
     184     but now that programs like du need to know about bind-mounted directories,
     185     we grant an exception to any with "bind" in its list of mount options.
     186     I.e., those are *not* dummy entries.  */
     187  #ifdef MOUNTED_GETMNTENT1
     188  # define ME_DUMMY(Fs_name, Fs_type, Bind)	\
     189    (ME_DUMMY_0 (Fs_name, Fs_type)		\
     190     || (strcmp (Fs_type, "none") == 0 && !Bind))
     191  #else
     192  # define ME_DUMMY(Fs_name, Fs_type)		\
     193    (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
     194  #endif
     195  
     196  #ifdef __CYGWIN__
     197  # include <windows.h>
     198  /* Don't assume that UNICODE is not defined.  */
     199  # undef GetDriveType
     200  # define GetDriveType GetDriveTypeA
     201  # define ME_REMOTE me_remote
     202  /* All cygwin mount points include ':' or start with '//'; so it
     203     requires a native Windows call to determine remote disks.  */
     204  static bool
     205  me_remote (char const *fs_name, _GL_UNUSED char const *fs_type)
     206  {
     207    if (fs_name[0] && fs_name[1] == ':')
     208      {
     209        char drive[4];
     210        sprintf (drive, "%c:\\", fs_name[0]);
     211        switch (GetDriveType (drive))
     212          {
     213          case DRIVE_REMOVABLE:
     214          case DRIVE_FIXED:
     215          case DRIVE_CDROM:
     216          case DRIVE_RAMDISK:
     217            return false;
     218          }
     219      }
     220    return true;
     221  }
     222  #endif
     223  
     224  #ifndef ME_REMOTE
     225  /* A file system is "remote" if its Fs_name contains a ':'
     226     or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
     227     or if it is of any other of the listed types
     228     or Fs_name is equal to "-hosts" (used by autofs to mount remote fs).
     229     "VM" file systems like prl_fs or vboxsf are not considered remote here. */
     230  # define ME_REMOTE(Fs_name, Fs_type)            \
     231      (strchr (Fs_name, ':') != NULL              \
     232       || ((Fs_name)[0] == '/'                    \
     233           && (Fs_name)[1] == '/'                 \
     234           && (strcmp (Fs_type, "smbfs") == 0     \
     235               || strcmp (Fs_type, "smb3") == 0   \
     236               || strcmp (Fs_type, "cifs") == 0)) \
     237       || strcmp (Fs_type, "acfs") == 0           \
     238       || strcmp (Fs_type, "afs") == 0            \
     239       || strcmp (Fs_type, "coda") == 0           \
     240       || strcmp (Fs_type, "auristorfs") == 0     \
     241       || strcmp (Fs_type, "fhgfs") == 0          \
     242       || strcmp (Fs_type, "gpfs") == 0           \
     243       || strcmp (Fs_type, "ibrix") == 0          \
     244       || strcmp (Fs_type, "ocfs2") == 0          \
     245       || strcmp (Fs_type, "vxfs") == 0           \
     246       || strcmp ("-hosts", Fs_name) == 0)
     247  #endif
     248  
     249  #if MOUNTED_GETMNTINFO          /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
     250  
     251  # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
     252  static char *
     253  fstype_to_string (short int t)
     254  {
     255    switch (t)
     256      {
     257  #  ifdef MOUNT_PC
     258      case MOUNT_PC:
     259        return "pc";
     260  #  endif
     261  #  ifdef MOUNT_MFS
     262      case MOUNT_MFS:
     263        return "mfs";
     264  #  endif
     265  #  ifdef MOUNT_LO
     266      case MOUNT_LO:
     267        return "lo";
     268  #  endif
     269  #  ifdef MOUNT_TFS
     270      case MOUNT_TFS:
     271        return "tfs";
     272  #  endif
     273  #  ifdef MOUNT_TMP
     274      case MOUNT_TMP:
     275        return "tmp";
     276  #  endif
     277  #  ifdef MOUNT_UFS
     278     case MOUNT_UFS:
     279       return "ufs" ;
     280  #  endif
     281  #  ifdef MOUNT_NFS
     282     case MOUNT_NFS:
     283       return "nfs" ;
     284  #  endif
     285  #  ifdef MOUNT_MSDOS
     286     case MOUNT_MSDOS:
     287       return "msdos" ;
     288  #  endif
     289  #  ifdef MOUNT_LFS
     290     case MOUNT_LFS:
     291       return "lfs" ;
     292  #  endif
     293  #  ifdef MOUNT_LOFS
     294     case MOUNT_LOFS:
     295       return "lofs" ;
     296  #  endif
     297  #  ifdef MOUNT_FDESC
     298     case MOUNT_FDESC:
     299       return "fdesc" ;
     300  #  endif
     301  #  ifdef MOUNT_PORTAL
     302     case MOUNT_PORTAL:
     303       return "portal" ;
     304  #  endif
     305  #  ifdef MOUNT_NULL
     306     case MOUNT_NULL:
     307       return "null" ;
     308  #  endif
     309  #  ifdef MOUNT_UMAP
     310     case MOUNT_UMAP:
     311       return "umap" ;
     312  #  endif
     313  #  ifdef MOUNT_KERNFS
     314     case MOUNT_KERNFS:
     315       return "kernfs" ;
     316  #  endif
     317  #  ifdef MOUNT_PROCFS
     318     case MOUNT_PROCFS:
     319       return "procfs" ;
     320  #  endif
     321  #  ifdef MOUNT_AFS
     322     case MOUNT_AFS:
     323       return "afs" ;
     324  #  endif
     325  #  ifdef MOUNT_CD9660
     326     case MOUNT_CD9660:
     327       return "cd9660" ;
     328  #  endif
     329  #  ifdef MOUNT_UNION
     330     case MOUNT_UNION:
     331       return "union" ;
     332  #  endif
     333  #  ifdef MOUNT_DEVFS
     334     case MOUNT_DEVFS:
     335       return "devfs" ;
     336  #  endif
     337  #  ifdef MOUNT_EXT2FS
     338     case MOUNT_EXT2FS:
     339       return "ext2fs" ;
     340  #  endif
     341      default:
     342        return "?";
     343      }
     344  }
     345  # endif
     346  
     347  static char *
     348  fsp_to_string (const struct statfs *fsp)
     349  {
     350  # if HAVE_STRUCT_STATFS_F_FSTYPENAME
     351    return (char *) (fsp->f_fstypename);
     352  # else
     353    return fstype_to_string (fsp->f_type);
     354  # endif
     355  }
     356  
     357  #endif /* MOUNTED_GETMNTINFO */
     358  
     359  #ifdef MOUNTED_VMOUNT           /* AIX */
     360  static char *
     361  fstype_to_string (int t)
     362  {
     363    struct vfs_ent *e;
     364  
     365    e = getvfsbytype (t);
     366    if (!e || !e->vfsent_name)
     367      return "none";
     368    else
     369      return e->vfsent_name;
     370  }
     371  #endif /* MOUNTED_VMOUNT */
     372  
     373  
     374  #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
     375  
     376  /* Return the device number from MOUNT_OPTIONS, if possible.
     377     Otherwise return (dev_t) -1.  */
     378  static dev_t
     379  dev_from_mount_options (char const *mount_options)
     380  {
     381    /* GNU/Linux allows file system implementations to define their own
     382       meaning for "dev=" mount options, so don't trust the meaning
     383       here.  */
     384  # ifndef __linux__
     385  
     386    static char const dev_pattern[] = ",dev=";
     387    char const *devopt = strstr (mount_options, dev_pattern);
     388  
     389    if (devopt)
     390      {
     391        char const *optval = devopt + sizeof dev_pattern - 1;
     392        char *optvalend;
     393        unsigned long int dev;
     394        errno = 0;
     395        dev = strtoul (optval, &optvalend, 16);
     396        if (optval != optvalend
     397            && (*optvalend == '\0' || *optvalend == ',')
     398            && ! (dev == ULONG_MAX && errno == ERANGE)
     399            && dev == (dev_t) dev)
     400          return dev;
     401      }
     402  
     403  # endif
     404    (void) mount_options;
     405    return -1;
     406  }
     407  
     408  #endif
     409  
     410  #if defined MOUNTED_GETMNTENT1 && (defined __linux__ || defined __ANDROID__) /* GNU/Linux, Android */
     411  
     412  /* Unescape the paths in mount tables.
     413     STR is updated in place.  */
     414  
     415  static void
     416  unescape_tab (char *str)
     417  {
     418    size_t i, j = 0;
     419    size_t len = strlen (str) + 1;
     420    for (i = 0; i < len; i++)
     421      {
     422        if (str[i] == '\\' && (i + 4 < len)
     423            && str[i + 1] >= '0' && str[i + 1] <= '3'
     424            && str[i + 2] >= '0' && str[i + 2] <= '7'
     425            && str[i + 3] >= '0' && str[i + 3] <= '7')
     426          {
     427            str[j++] = (str[i + 1] - '0') * 64 +
     428                       (str[i + 2] - '0') * 8 +
     429                       (str[i + 3] - '0');
     430            i += 3;
     431          }
     432        else
     433          str[j++] = str[i];
     434      }
     435  }
     436  
     437  /* Find the next space in STR, terminate the string there in place,
     438     and return that position.  Otherwise return NULL.  */
     439  
     440  static char *
     441  terminate_at_blank (char *str)
     442  {
     443    char *s = strchr (str, ' ');
     444    if (s)
     445      *s = '\0';
     446    return s;
     447  }
     448  #endif
     449  
     450  /* Return a list of the currently mounted file systems, or NULL on error.
     451     Add each entry to the tail of the list so that they stay in order.
     452     If NEED_FS_TYPE is true, ensure that the file system type fields in
     453     the returned list are valid.  Otherwise, they might not be.  */
     454  
     455  struct mount_entry *
     456  read_file_system_list (bool need_fs_type)
     457  {
     458    struct mount_entry *mount_list;
     459    struct mount_entry *me;
     460    struct mount_entry **mtail = &mount_list;
     461    (void) need_fs_type;
     462  
     463  #ifdef MOUNTED_GETMNTENT1       /* glibc, HP-UX, IRIX, Cygwin, Android,
     464                                     also (obsolete) 4.3BSD, SunOS */
     465    {
     466      FILE *fp;
     467  
     468  # if defined __linux__ || defined __ANDROID__
     469      /* Try parsing mountinfo first, as that make device IDs available.
     470         Note we could use libmount routines to simplify this parsing a little
     471         (and that code is in previous versions of this function), however
     472         libmount depends on libselinux which pulls in many dependencies.  */
     473      char const *mountinfo = "/proc/self/mountinfo";
     474      fp = fopen (mountinfo, "re");
     475      if (fp != NULL)
     476        {
     477          char *line = NULL;
     478          size_t buf_size = 0;
     479  
     480          while (getline (&line, &buf_size, fp) != -1)
     481            {
     482              unsigned int devmaj, devmin;
     483              int rc, mntroot_s;
     484  
     485              rc = sscanf(line, "%*u "        /* id - discarded  */
     486                                "%*u "        /* parent - discarded  */
     487                                "%u:%u "      /* dev major:minor  */
     488                                "%n",         /* mountroot (start)  */
     489                                &devmaj, &devmin,
     490                                &mntroot_s);
     491  
     492              if (rc != 2 && rc != 3)  /* 3 if %n included in count.  */
     493                continue;
     494  
     495              /* find end of MNTROOT.  */
     496              char *mntroot = line + mntroot_s;
     497              char *blank = terminate_at_blank (mntroot);
     498              if (! blank)
     499                continue;
     500  
     501              /* find end of TARGET.  */
     502              char *target = blank + 1;
     503              blank = terminate_at_blank (target);
     504              if (! blank)
     505                continue;
     506  
     507              /* skip optional fields, terminated by " - "  */
     508              char *dash = strstr (blank + 1, " - ");
     509              if (! dash)
     510                continue;
     511  
     512              /* advance past the " - " separator.  */
     513              char *fstype = dash + 3;
     514              blank = terminate_at_blank (fstype);
     515              if (! blank)
     516                continue;
     517  
     518              /* find end of SOURCE.  */
     519              char *source = blank + 1;
     520              if (! terminate_at_blank (source))
     521                continue;
     522  
     523              /* manipulate the sub-strings in place.  */
     524              unescape_tab (source);
     525              unescape_tab (target);
     526              unescape_tab (mntroot);
     527              unescape_tab (fstype);
     528  
     529              me = xmalloc (sizeof *me);
     530  
     531              me->me_devname = xstrdup (source);
     532              me->me_mountdir = xstrdup (target);
     533              me->me_mntroot = xstrdup (mntroot);
     534              me->me_type = xstrdup (fstype);
     535              me->me_type_malloced = 1;
     536              me->me_dev = makedev (devmaj, devmin);
     537              /* we pass "false" for the "Bind" option as that's only
     538                 significant when the Fs_type is "none" which will not be
     539                 the case when parsing "/proc/self/mountinfo", and only
     540                 applies for static /etc/mtab files.  */
     541              me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
     542              me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     543  
     544              /* Add to the linked list. */
     545              *mtail = me;
     546              mtail = &me->me_next;
     547            }
     548  
     549          free (line);
     550  
     551          if (ferror (fp))
     552            {
     553              int saved_errno = errno;
     554              fclose (fp);
     555              errno = saved_errno;
     556              goto free_then_fail;
     557            }
     558  
     559          if (fclose (fp) == EOF)
     560            goto free_then_fail;
     561        }
     562      else /* fallback to /proc/self/mounts (/etc/mtab).  */
     563  # endif /* __linux __ || __ANDROID__ */
     564        {
     565          struct mntent *mnt;
     566          char const *table = MOUNTED;
     567  
     568          fp = setmntent (table, "r");
     569          if (fp == NULL)
     570            return NULL;
     571  
     572          while ((mnt = getmntent (fp)))
     573            {
     574              bool bind = hasmntopt (mnt, "bind");
     575  
     576              me = xmalloc (sizeof *me);
     577              me->me_devname = xstrdup (mnt->mnt_fsname);
     578              me->me_mountdir = xstrdup (mnt->mnt_dir);
     579              me->me_mntroot = NULL;
     580              me->me_type = xstrdup (mnt->mnt_type);
     581              me->me_type_malloced = 1;
     582              me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
     583              me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     584              me->me_dev = dev_from_mount_options (mnt->mnt_opts);
     585  
     586              /* Add to the linked list. */
     587              *mtail = me;
     588              mtail = &me->me_next;
     589            }
     590  
     591          if (endmntent (fp) == 0)
     592            goto free_then_fail;
     593        }
     594    }
     595  #endif /* MOUNTED_GETMNTENT1. */
     596  
     597  #ifdef MOUNTED_GETMNTINFO       /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
     598    {
     599      struct statfs *fsp;
     600      int entries;
     601  
     602      entries = getmntinfo (&fsp, MNT_NOWAIT);
     603      if (entries < 0)
     604        return NULL;
     605      for (; entries-- > 0; fsp++)
     606        {
     607          char *fs_type = fsp_to_string (fsp);
     608  
     609          me = xmalloc (sizeof *me);
     610          me->me_devname = xstrdup (fsp->f_mntfromname);
     611          me->me_mountdir = xstrdup (fsp->f_mntonname);
     612          me->me_mntroot = NULL;
     613          me->me_type = fs_type;
     614          me->me_type_malloced = 0;
     615          me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
     616          me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     617          me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
     618  
     619          /* Add to the linked list. */
     620          *mtail = me;
     621          mtail = &me->me_next;
     622        }
     623    }
     624  #endif /* MOUNTED_GETMNTINFO */
     625  
     626  #ifdef MOUNTED_GETMNTINFO2      /* NetBSD, Minix */
     627    {
     628      struct statvfs *fsp;
     629      int entries;
     630  
     631      entries = getmntinfo (&fsp, MNT_NOWAIT);
     632      if (entries < 0)
     633        return NULL;
     634      for (; entries-- > 0; fsp++)
     635        {
     636          me = xmalloc (sizeof *me);
     637          me->me_devname = xstrdup (fsp->f_mntfromname);
     638          me->me_mountdir = xstrdup (fsp->f_mntonname);
     639          me->me_mntroot = NULL;
     640          me->me_type = xstrdup (fsp->f_fstypename);
     641          me->me_type_malloced = 1;
     642          me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
     643          me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     644          me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
     645  
     646          /* Add to the linked list. */
     647          *mtail = me;
     648          mtail = &me->me_next;
     649        }
     650    }
     651  #endif /* MOUNTED_GETMNTINFO2 */
     652  
     653  #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
     654    {
     655      /* The next_dev() and fs_stat_dev() system calls give the list of
     656         all file systems, including the information returned by statvfs()
     657         (fs type, total blocks, free blocks etc.), but without the mount
     658         point. But on BeOS all file systems except / are mounted in the
     659         rootfs, directly under /.
     660         The directory name of the mount point is often, but not always,
     661         identical to the volume name of the device.
     662         We therefore get the list of subdirectories of /, and the list
     663         of all file systems, and match the two lists.  */
     664  
     665      DIR *dirp;
     666      struct rootdir_entry
     667        {
     668          char *name;
     669          dev_t dev;
     670          ino_t ino;
     671          struct rootdir_entry *next;
     672        };
     673      struct rootdir_entry *rootdir_list;
     674      struct rootdir_entry **rootdir_tail;
     675      int32 pos;
     676      dev_t dev;
     677      fs_info fi;
     678  
     679      /* All volumes are mounted in the rootfs, directly under /. */
     680      rootdir_list = NULL;
     681      rootdir_tail = &rootdir_list;
     682      dirp = opendir ("/");
     683      if (dirp)
     684        {
     685          struct dirent *d;
     686  
     687          while ((d = readdir (dirp)) != NULL)
     688            {
     689              char *name;
     690              struct stat statbuf;
     691  
     692              if (strcmp (d->d_name, "..") == 0)
     693                continue;
     694  
     695              if (strcmp (d->d_name, ".") == 0)
     696                name = xstrdup ("/");
     697              else
     698                {
     699                  name = xmalloc (1 + strlen (d->d_name) + 1);
     700                  name[0] = '/';
     701                  strcpy (name + 1, d->d_name);
     702                }
     703  
     704              if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
     705                {
     706                  struct rootdir_entry *re = xmalloc (sizeof *re);
     707                  re->name = name;
     708                  re->dev = statbuf.st_dev;
     709                  re->ino = statbuf.st_ino;
     710  
     711                  /* Add to the linked list.  */
     712                  *rootdir_tail = re;
     713                  rootdir_tail = &re->next;
     714                }
     715              else
     716                free (name);
     717            }
     718          closedir (dirp);
     719        }
     720      *rootdir_tail = NULL;
     721  
     722      for (pos = 0; (dev = next_dev (&pos)) >= 0; )
     723        if (fs_stat_dev (dev, &fi) >= 0)
     724          {
     725            /* Note: fi.dev == dev. */
     726            struct rootdir_entry *re;
     727  
     728            for (re = rootdir_list; re; re = re->next)
     729              if (re->dev == fi.dev && re->ino == fi.root)
     730                break;
     731  
     732            me = xmalloc (sizeof *me);
     733            me->me_devname = xstrdup (fi.device_name[0] != '\0'
     734                                      ? fi.device_name : fi.fsh_name);
     735            me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
     736            me->me_mntroot = NULL;
     737            me->me_type = xstrdup (fi.fsh_name);
     738            me->me_type_malloced = 1;
     739            me->me_dev = fi.dev;
     740            me->me_dummy = 0;
     741            me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
     742  
     743            /* Add to the linked list. */
     744            *mtail = me;
     745            mtail = &me->me_next;
     746          }
     747      *mtail = NULL;
     748  
     749      while (rootdir_list != NULL)
     750        {
     751          struct rootdir_entry *re = rootdir_list;
     752          rootdir_list = re->next;
     753          free (re->name);
     754          free (re);
     755        }
     756    }
     757  #endif /* MOUNTED_FS_STAT_DEV */
     758  
     759  #if defined MOUNTED_GETFSSTAT   /* OSF/1, also (obsolete) Apple Darwin 1.3 */
     760    {
     761      int numsys, counter;
     762      size_t bufsize;
     763      struct statfs *stats;
     764  
     765      numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
     766      if (numsys < 0)
     767        return NULL;
     768      if (SIZE_MAX / sizeof *stats <= numsys)
     769        xalloc_die ();
     770  
     771      bufsize = (1 + numsys) * sizeof *stats;
     772      stats = xmalloc (bufsize);
     773      numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
     774  
     775      if (numsys < 0)
     776        {
     777          free (stats);
     778          return NULL;
     779        }
     780  
     781      for (counter = 0; counter < numsys; counter++)
     782        {
     783          me = xmalloc (sizeof *me);
     784          me->me_devname = xstrdup (stats[counter].f_mntfromname);
     785          me->me_mountdir = xstrdup (stats[counter].f_mntonname);
     786          me->me_mntroot = NULL;
     787          me->me_type = xstrdup (FS_TYPE (stats[counter]));
     788          me->me_type_malloced = 1;
     789          me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
     790          me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     791          me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
     792  
     793          /* Add to the linked list. */
     794          *mtail = me;
     795          mtail = &me->me_next;
     796        }
     797  
     798      free (stats);
     799    }
     800  #endif /* MOUNTED_GETFSSTAT */
     801  
     802  #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
     803    {
     804      struct mnttab mnt;
     805      char *table = "/etc/mnttab";
     806      FILE *fp;
     807  
     808      fp = fopen (table, "re");
     809      if (fp == NULL)
     810        return NULL;
     811  
     812      while (fread (&mnt, sizeof mnt, 1, fp) > 0)
     813        {
     814          me = xmalloc (sizeof *me);
     815          me->me_devname = xstrdup (mnt.mt_dev);
     816          me->me_mountdir = xstrdup (mnt.mt_filsys);
     817          me->me_mntroot = NULL;
     818          me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
     819          me->me_type = "";
     820          me->me_type_malloced = 0;
     821          if (need_fs_type)
     822            {
     823              struct statfs fsd;
     824              char typebuf[FSTYPSZ];
     825  
     826              if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
     827                  && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
     828                {
     829                  me->me_type = xstrdup (typebuf);
     830                  me->me_type_malloced = 1;
     831                }
     832            }
     833          me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
     834          me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     835  
     836          /* Add to the linked list. */
     837          *mtail = me;
     838          mtail = &me->me_next;
     839        }
     840  
     841      if (ferror (fp))
     842        {
     843          /* The last fread() call must have failed.  */
     844          int saved_errno = errno;
     845          fclose (fp);
     846          errno = saved_errno;
     847          goto free_then_fail;
     848        }
     849  
     850      if (fclose (fp) == EOF)
     851        goto free_then_fail;
     852    }
     853  #endif /* MOUNTED_FREAD_FSTYP.  */
     854  
     855  #ifdef MOUNTED_GETEXTMNTENT     /* Solaris >= 8 */
     856    {
     857      struct extmnttab mnt;
     858      const char *table = MNTTAB;
     859      FILE *fp;
     860      int ret;
     861  
     862      /* No locking is needed, because the contents of /etc/mnttab is generated
     863         by the kernel.  */
     864  
     865      errno = 0;
     866      fp = fopen (table, "re");
     867      if (fp == NULL)
     868        ret = errno;
     869      else
     870        {
     871          while ((ret = getextmntent (fp, &mnt, 1)) == 0)
     872            {
     873              me = xmalloc (sizeof *me);
     874              me->me_devname = xstrdup (mnt.mnt_special);
     875              me->me_mountdir = xstrdup (mnt.mnt_mountp);
     876              me->me_mntroot = NULL;
     877              me->me_type = xstrdup (mnt.mnt_fstype);
     878              me->me_type_malloced = 1;
     879              me->me_dummy = MNT_IGNORE (&mnt) != 0;
     880              me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     881              me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
     882  
     883              /* Add to the linked list. */
     884              *mtail = me;
     885              mtail = &me->me_next;
     886            }
     887  
     888          ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
     889          /* Here ret = -1 means success, ret >= 0 means failure.  */
     890        }
     891  
     892      if (0 <= ret)
     893        {
     894          errno = ret;
     895          goto free_then_fail;
     896        }
     897    }
     898  #endif /* MOUNTED_GETEXTMNTENT */
     899  
     900  #ifdef MOUNTED_GETMNTENT2       /* Solaris < 8, also (obsolete) SVR4 */
     901    {
     902      struct mnttab mnt;
     903      const char *table = MNTTAB;
     904      FILE *fp;
     905      int ret;
     906      int lockfd = -1;
     907  
     908  # if defined F_RDLCK && defined F_SETLKW
     909      /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
     910         e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
     911         for this file name, we should use their macro name instead.
     912         (Why not just lock MNTTAB directly?  We don't know.)  */
     913  #  ifndef MNTTAB_LOCK
     914  #   define MNTTAB_LOCK "/etc/.mnttab.lock"
     915  #  endif
     916      lockfd = open (MNTTAB_LOCK, O_RDONLY | O_CLOEXEC);
     917      if (0 <= lockfd)
     918        {
     919          struct flock flock;
     920          flock.l_type = F_RDLCK;
     921          flock.l_whence = SEEK_SET;
     922          flock.l_start = 0;
     923          flock.l_len = 0;
     924          while (fcntl (lockfd, F_SETLKW, &flock) == -1)
     925            if (errno != EINTR)
     926              {
     927                int saved_errno = errno;
     928                close (lockfd);
     929                errno = saved_errno;
     930                return NULL;
     931              }
     932        }
     933      else if (errno != ENOENT)
     934        return NULL;
     935  # endif
     936  
     937      errno = 0;
     938      fp = fopen (table, "re");
     939      if (fp == NULL)
     940        ret = errno;
     941      else
     942        {
     943          while ((ret = getmntent (fp, &mnt)) == 0)
     944            {
     945              me = xmalloc (sizeof *me);
     946              me->me_devname = xstrdup (mnt.mnt_special);
     947              me->me_mountdir = xstrdup (mnt.mnt_mountp);
     948              me->me_mntroot = NULL;
     949              me->me_type = xstrdup (mnt.mnt_fstype);
     950              me->me_type_malloced = 1;
     951              me->me_dummy = MNT_IGNORE (&mnt) != 0;
     952              me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
     953              me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
     954  
     955              /* Add to the linked list. */
     956              *mtail = me;
     957              mtail = &me->me_next;
     958            }
     959  
     960          ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
     961          /* Here ret = -1 means success, ret >= 0 means failure.  */
     962        }
     963  
     964      if (0 <= lockfd && close (lockfd) != 0)
     965        ret = errno;
     966  
     967      if (0 <= ret)
     968        {
     969          errno = ret;
     970          goto free_then_fail;
     971        }
     972    }
     973  #endif /* MOUNTED_GETMNTENT2.  */
     974  
     975  #ifdef MOUNTED_VMOUNT           /* AIX */
     976    {
     977      int bufsize;
     978      void *entries;
     979      char *thisent;
     980      struct vmount *vmp;
     981      int n_entries;
     982      int i;
     983  
     984      /* Ask how many bytes to allocate for the mounted file system info.  */
     985      entries = &bufsize;
     986      if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
     987        return NULL;
     988      entries = xmalloc (bufsize);
     989  
     990      /* Get the list of mounted file systems.  */
     991      n_entries = mntctl (MCTL_QUERY, bufsize, entries);
     992      if (n_entries < 0)
     993        {
     994          free (entries);
     995          return NULL;
     996        }
     997  
     998      for (i = 0, thisent = entries;
     999           i < n_entries;
    1000           i++, thisent += vmp->vmt_length)
    1001        {
    1002          char *options, *ignore;
    1003  
    1004          vmp = (struct vmount *) thisent;
    1005          me = xmalloc (sizeof *me);
    1006          if (vmp->vmt_flags & MNT_REMOTE)
    1007            {
    1008              char *host, *dir;
    1009  
    1010              me->me_remote = 1;
    1011              /* Prepend the remote dirname.  */
    1012              host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
    1013              dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
    1014              me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
    1015              strcpy (me->me_devname, host);
    1016              strcat (me->me_devname, ":");
    1017              strcat (me->me_devname, dir);
    1018            }
    1019          else
    1020            {
    1021              me->me_remote = 0;
    1022              me->me_devname = xstrdup (thisent +
    1023                                        vmp->vmt_data[VMT_OBJECT].vmt_off);
    1024            }
    1025          me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
    1026          me->me_mntroot = NULL;
    1027          me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
    1028          me->me_type_malloced = 1;
    1029          options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
    1030          ignore = strstr (options, "ignore");
    1031          me->me_dummy = (ignore
    1032                          && (ignore == options || ignore[-1] == ',')
    1033                          && (ignore[sizeof "ignore" - 1] == ','
    1034                              || ignore[sizeof "ignore" - 1] == '\0'));
    1035          me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
    1036  
    1037          /* Add to the linked list. */
    1038          *mtail = me;
    1039          mtail = &me->me_next;
    1040        }
    1041      free (entries);
    1042    }
    1043  #endif /* MOUNTED_VMOUNT. */
    1044  
    1045  #ifdef MOUNTED_INTERIX_STATVFS  /* Interix */
    1046    {
    1047      DIR *dirp = opendir ("/dev/fs");
    1048      char node[9 + NAME_MAX];
    1049  
    1050      if (!dirp)
    1051        goto free_then_fail;
    1052  
    1053      while (1)
    1054        {
    1055          struct statvfs dev;
    1056          struct dirent entry;
    1057          struct dirent *result;
    1058  
    1059          /* FIXME: readdir_r is planned to be withdrawn from POSIX and
    1060             marked obsolescent in glibc.  Use readdir instead.  */
    1061          if (readdir_r (dirp, &entry, &result) || result == NULL)
    1062            break;
    1063  
    1064          strcpy (node, "/dev/fs/");
    1065          strcat (node, entry.d_name);
    1066  
    1067          if (statvfs (node, &dev) == 0)
    1068            {
    1069              me = xmalloc (sizeof *me);
    1070              me->me_devname = xstrdup (dev.f_mntfromname);
    1071              me->me_mountdir = xstrdup (dev.f_mntonname);
    1072              me->me_mntroot = NULL;
    1073              me->me_type = xstrdup (dev.f_fstypename);
    1074              me->me_type_malloced = 1;
    1075              me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
    1076              me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
    1077              me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
    1078  
    1079              /* Add to the linked list. */
    1080              *mtail = me;
    1081              mtail = &me->me_next;
    1082            }
    1083        }
    1084      closedir (dirp);
    1085    }
    1086  #endif /* MOUNTED_INTERIX_STATVFS */
    1087  
    1088    *mtail = NULL;
    1089    return mount_list;
    1090  
    1091  
    1092   free_then_fail: _GL_UNUSED_LABEL;
    1093    {
    1094      int saved_errno = errno;
    1095      *mtail = NULL;
    1096  
    1097      while (mount_list)
    1098        {
    1099          me = mount_list->me_next;
    1100          free_mount_entry (mount_list);
    1101          mount_list = me;
    1102        }
    1103  
    1104      errno = saved_errno;
    1105      return NULL;
    1106    }
    1107  }
    1108  
    1109  /* Free a mount entry as returned from read_file_system_list ().  */
    1110  
    1111  void
    1112  free_mount_entry (struct mount_entry *me)
    1113  {
    1114    free (me->me_devname);
    1115    free (me->me_mountdir);
    1116    free (me->me_mntroot);
    1117    if (me->me_type_malloced)
    1118      free (me->me_type);
    1119    free (me);
    1120  }