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