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