mountlist: don't consider "devtmpfs" as dummy
[gnulib.git] / lib / mountlist.c
1 /* mountlist.c -- return a list of mounted file systems
2
3    Copyright (C) 1991-1992, 1997-2013 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 defined MOUNTED_GETFSSTAT   /* OSF_1 and Darwin1.3.x */
41 # if HAVE_SYS_UCRED_H
42 #  include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
43                       NGROUPS is used as an array dimension in ucred.h */
44 #  include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
45 # endif
46 # if HAVE_SYS_MOUNT_H
47 #  include <sys/mount.h>
48 # endif
49 # if HAVE_SYS_FS_TYPES_H
50 #  include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
51 # endif
52 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
53 #  define FS_TYPE(Ent) ((Ent).f_fstypename)
54 # else
55 #  define FS_TYPE(Ent) mnt_names[(Ent).f_type]
56 # endif
57 #endif /* MOUNTED_GETFSSTAT */
58
59 #ifdef MOUNTED_GETMNTENT1       /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
60 # include <mntent.h>
61 # if !defined MOUNTED
62 #  if defined _PATH_MOUNTED     /* GNU libc  */
63 #   define MOUNTED _PATH_MOUNTED
64 #  endif
65 #  if defined MNT_MNTTAB        /* HP-UX.  */
66 #   define MOUNTED MNT_MNTTAB
67 #  endif
68 #  if defined MNTTABNAME        /* Dynix.  */
69 #   define MOUNTED MNTTABNAME
70 #  endif
71 # endif
72 #endif
73
74 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
75 # include <sys/mount.h>
76 #endif
77
78 #ifdef MOUNTED_GETMNTINFO2      /* NetBSD 3.0.  */
79 # include <sys/statvfs.h>
80 #endif
81
82 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
83 # include <sys/mount.h>
84 # include <sys/fs_types.h>
85 #endif
86
87 #ifdef MOUNTED_FS_STAT_DEV      /* BeOS.  */
88 # include <fs_info.h>
89 # include <dirent.h>
90 #endif
91
92 #ifdef MOUNTED_FREAD            /* SVR2.  */
93 # include <mnttab.h>
94 #endif
95
96 #ifdef MOUNTED_FREAD_FSTYP      /* SVR3.  */
97 # include <mnttab.h>
98 # include <sys/fstyp.h>
99 # include <sys/statfs.h>
100 #endif
101
102 #ifdef MOUNTED_LISTMNTENT
103 # include <mntent.h>
104 #endif
105
106 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
107 # include <sys/mnttab.h>
108 #endif
109
110 #ifdef MOUNTED_VMOUNT           /* AIX.  */
111 # include <fshelp.h>
112 # include <sys/vfs.h>
113 #endif
114
115 #ifdef MOUNTED_INTERIX_STATVFS  /* Interix. */
116 # include <sys/statvfs.h>
117 # include <dirent.h>
118 #endif
119
120 #ifdef DOLPHIN
121 /* So special that it's not worth putting this in autoconf.  */
122 # undef MOUNTED_FREAD_FSTYP
123 # define MOUNTED_GETMNTTBL
124 #endif
125
126 #if HAVE_SYS_MNTENT_H
127 /* This is to get MNTOPT_IGNORE on e.g. SVR4.  */
128 # include <sys/mntent.h>
129 #endif
130
131 #undef MNT_IGNORE
132 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
133 # define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
134 #else
135 # define MNT_IGNORE(M) 0
136 #endif
137
138 #if USE_UNLOCKED_IO
139 # include "unlocked-io.h"
140 #endif
141
142 /* The results of open() in this file are not used with fchdir,
143    therefore save some unnecessary work in fchdir.c.  */
144 #undef open
145 #undef close
146
147 /* The results of opendir() in this file are not used with dirfd and fchdir,
148    therefore save some unnecessary work in fchdir.c.  */
149 #undef opendir
150 #undef closedir
151
152 #define ME_DUMMY_0(Fs_name, Fs_type)            \
153   (strcmp (Fs_type, "autofs") == 0              \
154    || strcmp (Fs_type, "proc") == 0             \
155    || strcmp (Fs_type, "subfs") == 0            \
156    /* for Linux 2.6/3.x */                      \
157    || strcmp (Fs_type, "debugfs") == 0          \
158    || strcmp (Fs_type, "devpts") == 0           \
159    || strcmp (Fs_type, "fusectl") == 0          \
160    || strcmp (Fs_type, "mqueue") == 0           \
161    || strcmp (Fs_type, "rpc_pipefs") == 0       \
162    || strcmp (Fs_type, "sysfs") == 0            \
163    /* FreeBSD, Linux 2.4 */                     \
164    || strcmp (Fs_type, "devfs") == 0            \
165    /* for NetBSD 3.0 */                         \
166    || strcmp (Fs_type, "kernfs") == 0           \
167    /* for Irix 6.5 */                           \
168    || strcmp (Fs_type, "ignore") == 0)
169
170 /* Historically, we have marked as "dummy" any file system of type "none",
171    but now that programs like du need to know about bind-mounted directories,
172    we grant an exception to any with "bind" in its list of mount options.
173    I.e., those are *not* dummy entries.  */
174 #ifdef MOUNTED_GETMNTENT1
175 # define ME_DUMMY(Fs_name, Fs_type, Fs_ent)     \
176   (ME_DUMMY_0 (Fs_name, Fs_type)                \
177    || (strcmp (Fs_type, "none") == 0            \
178        && !hasmntopt (Fs_ent, "bind")))
179 #else
180 # define ME_DUMMY(Fs_name, Fs_type)             \
181   (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
182 #endif
183
184 #ifdef __CYGWIN__
185 # include <windows.h>
186 # define ME_REMOTE me_remote
187 /* All cygwin mount points include ':' or start with '//'; so it
188    requires a native Windows call to determine remote disks.  */
189 static bool
190 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
191 {
192   if (fs_name[0] && fs_name[1] == ':')
193     {
194       char drive[4];
195       sprintf (drive, "%c:\\", fs_name[0]);
196       switch (GetDriveType (drive))
197         {
198         case DRIVE_REMOVABLE:
199         case DRIVE_FIXED:
200         case DRIVE_CDROM:
201         case DRIVE_RAMDISK:
202           return false;
203         }
204     }
205   return true;
206 }
207 #endif
208
209 #ifndef ME_REMOTE
210 /* A file system is "remote" if its Fs_name contains a ':'
211    or if (it is of type (smbfs or cifs) and its Fs_name starts with '//').  */
212 # define ME_REMOTE(Fs_name, Fs_type)            \
213     (strchr (Fs_name, ':') != NULL              \
214      || ((Fs_name)[0] == '/'                    \
215          && (Fs_name)[1] == '/'                 \
216          && (strcmp (Fs_type, "smbfs") == 0     \
217              || strcmp (Fs_type, "cifs") == 0)))
218 #endif
219
220 #if MOUNTED_GETMNTINFO
221
222 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
223 static char *
224 fstype_to_string (short int t)
225 {
226   switch (t)
227     {
228 #  ifdef MOUNT_PC
229     case MOUNT_PC:
230       return "pc";
231 #  endif
232 #  ifdef MOUNT_MFS
233     case MOUNT_MFS:
234       return "mfs";
235 #  endif
236 #  ifdef MOUNT_LO
237     case MOUNT_LO:
238       return "lo";
239 #  endif
240 #  ifdef MOUNT_TFS
241     case MOUNT_TFS:
242       return "tfs";
243 #  endif
244 #  ifdef MOUNT_TMP
245     case MOUNT_TMP:
246       return "tmp";
247 #  endif
248 #  ifdef MOUNT_UFS
249    case MOUNT_UFS:
250      return "ufs" ;
251 #  endif
252 #  ifdef MOUNT_NFS
253    case MOUNT_NFS:
254      return "nfs" ;
255 #  endif
256 #  ifdef MOUNT_MSDOS
257    case MOUNT_MSDOS:
258      return "msdos" ;
259 #  endif
260 #  ifdef MOUNT_LFS
261    case MOUNT_LFS:
262      return "lfs" ;
263 #  endif
264 #  ifdef MOUNT_LOFS
265    case MOUNT_LOFS:
266      return "lofs" ;
267 #  endif
268 #  ifdef MOUNT_FDESC
269    case MOUNT_FDESC:
270      return "fdesc" ;
271 #  endif
272 #  ifdef MOUNT_PORTAL
273    case MOUNT_PORTAL:
274      return "portal" ;
275 #  endif
276 #  ifdef MOUNT_NULL
277    case MOUNT_NULL:
278      return "null" ;
279 #  endif
280 #  ifdef MOUNT_UMAP
281    case MOUNT_UMAP:
282      return "umap" ;
283 #  endif
284 #  ifdef MOUNT_KERNFS
285    case MOUNT_KERNFS:
286      return "kernfs" ;
287 #  endif
288 #  ifdef MOUNT_PROCFS
289    case MOUNT_PROCFS:
290      return "procfs" ;
291 #  endif
292 #  ifdef MOUNT_AFS
293    case MOUNT_AFS:
294      return "afs" ;
295 #  endif
296 #  ifdef MOUNT_CD9660
297    case MOUNT_CD9660:
298      return "cd9660" ;
299 #  endif
300 #  ifdef MOUNT_UNION
301    case MOUNT_UNION:
302      return "union" ;
303 #  endif
304 #  ifdef MOUNT_DEVFS
305    case MOUNT_DEVFS:
306      return "devfs" ;
307 #  endif
308 #  ifdef MOUNT_EXT2FS
309    case MOUNT_EXT2FS:
310      return "ext2fs" ;
311 #  endif
312     default:
313       return "?";
314     }
315 }
316 # endif
317
318 static char *
319 fsp_to_string (const struct statfs *fsp)
320 {
321 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
322   return (char *) (fsp->f_fstypename);
323 # else
324   return fstype_to_string (fsp->f_type);
325 # endif
326 }
327
328 #endif /* MOUNTED_GETMNTINFO */
329
330 #ifdef MOUNTED_VMOUNT           /* AIX.  */
331 static char *
332 fstype_to_string (int t)
333 {
334   struct vfs_ent *e;
335
336   e = getvfsbytype (t);
337   if (!e || !e->vfsent_name)
338     return "none";
339   else
340     return e->vfsent_name;
341 }
342 #endif /* MOUNTED_VMOUNT */
343
344
345 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
346
347 /* Return the device number from MOUNT_OPTIONS, if possible.
348    Otherwise return (dev_t) -1.  */
349 static dev_t
350 dev_from_mount_options (char const *mount_options)
351 {
352   /* GNU/Linux allows file system implementations to define their own
353      meaning for "dev=" mount options, so don't trust the meaning
354      here.  */
355 # ifndef __linux__
356
357   static char const dev_pattern[] = ",dev=";
358   char const *devopt = strstr (mount_options, dev_pattern);
359
360   if (devopt)
361     {
362       char const *optval = devopt + sizeof dev_pattern - 1;
363       char *optvalend;
364       unsigned long int dev;
365       errno = 0;
366       dev = strtoul (optval, &optvalend, 16);
367       if (optval != optvalend
368           && (*optvalend == '\0' || *optvalend == ',')
369           && ! (dev == ULONG_MAX && errno == ERANGE)
370           && dev == (dev_t) dev)
371         return dev;
372     }
373
374 # endif
375   (void) mount_options;
376   return -1;
377 }
378
379 #endif
380
381 /* Return a list of the currently mounted file systems, or NULL on error.
382    Add each entry to the tail of the list so that they stay in order.
383    If NEED_FS_TYPE is true, ensure that the file system type fields in
384    the returned list are valid.  Otherwise, they might not be.  */
385
386 struct mount_entry *
387 read_file_system_list (bool need_fs_type)
388 {
389   struct mount_entry *mount_list;
390   struct mount_entry *me;
391   struct mount_entry **mtail = &mount_list;
392   (void) need_fs_type;
393
394 #ifdef MOUNTED_LISTMNTENT
395   {
396     struct tabmntent *mntlist, *p;
397     struct mntent *mnt;
398     struct mount_entry *me;
399
400     /* the third and fourth arguments could be used to filter mounts,
401        but Crays doesn't seem to have any mounts that we want to
402        remove. Specifically, automount create normal NFS mounts.
403        */
404
405     if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
406       return NULL;
407     for (p = mntlist; p; p = p->next)
408       {
409         mnt = p->ment;
410         me = xmalloc (sizeof *me);
411         me->me_devname = xstrdup (mnt->mnt_fsname);
412         me->me_mountdir = xstrdup (mnt->mnt_dir);
413         me->me_type = xstrdup (mnt->mnt_type);
414         me->me_type_malloced = 1;
415         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
416         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
417         me->me_dev = -1;
418         *mtail = me;
419         mtail = &me->me_next;
420       }
421     freemntlist (mntlist);
422   }
423 #endif
424
425 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
426   {
427     struct mntent *mnt;
428     char const *table = MOUNTED;
429     FILE *fp;
430
431     fp = setmntent (table, "r");
432     if (fp == NULL)
433       return NULL;
434
435     while ((mnt = getmntent (fp)))
436       {
437         me = xmalloc (sizeof *me);
438         me->me_devname = xstrdup (mnt->mnt_fsname);
439         me->me_mountdir = xstrdup (mnt->mnt_dir);
440         me->me_type = xstrdup (mnt->mnt_type);
441         me->me_type_malloced = 1;
442         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, mnt);
443         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
444         me->me_dev = dev_from_mount_options (mnt->mnt_opts);
445
446         /* Add to the linked list. */
447         *mtail = me;
448         mtail = &me->me_next;
449       }
450
451     if (endmntent (fp) == 0)
452       goto free_then_fail;
453   }
454 #endif /* MOUNTED_GETMNTENT1. */
455
456 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
457   {
458     struct statfs *fsp;
459     int entries;
460
461     entries = getmntinfo (&fsp, MNT_NOWAIT);
462     if (entries < 0)
463       return NULL;
464     for (; entries-- > 0; fsp++)
465       {
466         char *fs_type = fsp_to_string (fsp);
467
468         me = xmalloc (sizeof *me);
469         me->me_devname = xstrdup (fsp->f_mntfromname);
470         me->me_mountdir = xstrdup (fsp->f_mntonname);
471         me->me_type = fs_type;
472         me->me_type_malloced = 0;
473         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
474         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
475         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
476
477         /* Add to the linked list. */
478         *mtail = me;
479         mtail = &me->me_next;
480       }
481   }
482 #endif /* MOUNTED_GETMNTINFO */
483
484 #ifdef MOUNTED_GETMNTINFO2      /* NetBSD 3.0.  */
485   {
486     struct statvfs *fsp;
487     int entries;
488
489     entries = getmntinfo (&fsp, MNT_NOWAIT);
490     if (entries < 0)
491       return NULL;
492     for (; entries-- > 0; fsp++)
493       {
494         me = xmalloc (sizeof *me);
495         me->me_devname = xstrdup (fsp->f_mntfromname);
496         me->me_mountdir = xstrdup (fsp->f_mntonname);
497         me->me_type = xstrdup (fsp->f_fstypename);
498         me->me_type_malloced = 1;
499         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
500         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
501         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
502
503         /* Add to the linked list. */
504         *mtail = me;
505         mtail = &me->me_next;
506       }
507   }
508 #endif /* MOUNTED_GETMNTINFO2 */
509
510 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
511   {
512     int offset = 0;
513     int val;
514     struct fs_data fsd;
515
516     while (errno = 0,
517            0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
518                               (char *) 0)))
519       {
520         me = xmalloc (sizeof *me);
521         me->me_devname = xstrdup (fsd.fd_req.devname);
522         me->me_mountdir = xstrdup (fsd.fd_req.path);
523         me->me_type = gt_names[fsd.fd_req.fstype];
524         me->me_type_malloced = 0;
525         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
526         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
527         me->me_dev = fsd.fd_req.dev;
528
529         /* Add to the linked list. */
530         *mtail = me;
531         mtail = &me->me_next;
532       }
533     if (val < 0)
534       goto free_then_fail;
535   }
536 #endif /* MOUNTED_GETMNT. */
537
538 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
539   {
540     /* The next_dev() and fs_stat_dev() system calls give the list of
541        all file systems, including the information returned by statvfs()
542        (fs type, total blocks, free blocks etc.), but without the mount
543        point. But on BeOS all file systems except / are mounted in the
544        rootfs, directly under /.
545        The directory name of the mount point is often, but not always,
546        identical to the volume name of the device.
547        We therefore get the list of subdirectories of /, and the list
548        of all file systems, and match the two lists.  */
549
550     DIR *dirp;
551     struct rootdir_entry
552       {
553         char *name;
554         dev_t dev;
555         ino_t ino;
556         struct rootdir_entry *next;
557       };
558     struct rootdir_entry *rootdir_list;
559     struct rootdir_entry **rootdir_tail;
560     int32 pos;
561     dev_t dev;
562     fs_info fi;
563
564     /* All volumes are mounted in the rootfs, directly under /. */
565     rootdir_list = NULL;
566     rootdir_tail = &rootdir_list;
567     dirp = opendir ("/");
568     if (dirp)
569       {
570         struct dirent *d;
571
572         while ((d = readdir (dirp)) != NULL)
573           {
574             char *name;
575             struct stat statbuf;
576
577             if (strcmp (d->d_name, "..") == 0)
578               continue;
579
580             if (strcmp (d->d_name, ".") == 0)
581               name = xstrdup ("/");
582             else
583               {
584                 name = xmalloc (1 + strlen (d->d_name) + 1);
585                 name[0] = '/';
586                 strcpy (name + 1, d->d_name);
587               }
588
589             if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
590               {
591                 struct rootdir_entry *re = xmalloc (sizeof *re);
592                 re->name = name;
593                 re->dev = statbuf.st_dev;
594                 re->ino = statbuf.st_ino;
595
596                 /* Add to the linked list.  */
597                 *rootdir_tail = re;
598                 rootdir_tail = &re->next;
599               }
600             else
601               free (name);
602           }
603         closedir (dirp);
604       }
605     *rootdir_tail = NULL;
606
607     for (pos = 0; (dev = next_dev (&pos)) >= 0; )
608       if (fs_stat_dev (dev, &fi) >= 0)
609         {
610           /* Note: fi.dev == dev. */
611           struct rootdir_entry *re;
612
613           for (re = rootdir_list; re; re = re->next)
614             if (re->dev == fi.dev && re->ino == fi.root)
615               break;
616
617           me = xmalloc (sizeof *me);
618           me->me_devname = xstrdup (fi.device_name[0] != '\0'
619                                     ? fi.device_name : fi.fsh_name);
620           me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
621           me->me_type = xstrdup (fi.fsh_name);
622           me->me_type_malloced = 1;
623           me->me_dev = fi.dev;
624           me->me_dummy = 0;
625           me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
626
627           /* Add to the linked list. */
628           *mtail = me;
629           mtail = &me->me_next;
630         }
631     *mtail = NULL;
632
633     while (rootdir_list != NULL)
634       {
635         struct rootdir_entry *re = rootdir_list;
636         rootdir_list = re->next;
637         free (re->name);
638         free (re);
639       }
640   }
641 #endif /* MOUNTED_FS_STAT_DEV */
642
643 #if defined MOUNTED_GETFSSTAT   /* __alpha running OSF_1 */
644   {
645     int numsys, counter;
646     size_t bufsize;
647     struct statfs *stats;
648
649     numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
650     if (numsys < 0)
651       return NULL;
652     if (SIZE_MAX / sizeof *stats <= numsys)
653       xalloc_die ();
654
655     bufsize = (1 + numsys) * sizeof *stats;
656     stats = xmalloc (bufsize);
657     numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
658
659     if (numsys < 0)
660       {
661         free (stats);
662         return NULL;
663       }
664
665     for (counter = 0; counter < numsys; counter++)
666       {
667         me = xmalloc (sizeof *me);
668         me->me_devname = xstrdup (stats[counter].f_mntfromname);
669         me->me_mountdir = xstrdup (stats[counter].f_mntonname);
670         me->me_type = xstrdup (FS_TYPE (stats[counter]));
671         me->me_type_malloced = 1;
672         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
673         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
674         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
675
676         /* Add to the linked list. */
677         *mtail = me;
678         mtail = &me->me_next;
679       }
680
681     free (stats);
682   }
683 #endif /* MOUNTED_GETFSSTAT */
684
685 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23].  */
686   {
687     struct mnttab mnt;
688     char *table = "/etc/mnttab";
689     FILE *fp;
690
691     fp = fopen (table, "r");
692     if (fp == NULL)
693       return NULL;
694
695     while (fread (&mnt, sizeof mnt, 1, fp) > 0)
696       {
697         me = xmalloc (sizeof *me);
698 # ifdef GETFSTYP                        /* SVR3.  */
699         me->me_devname = xstrdup (mnt.mt_dev);
700 # else
701         me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
702         strcpy (me->me_devname, "/dev/");
703         strcpy (me->me_devname + 5, mnt.mt_dev);
704 # endif
705         me->me_mountdir = xstrdup (mnt.mt_filsys);
706         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
707         me->me_type = "";
708         me->me_type_malloced = 0;
709 # ifdef GETFSTYP                        /* SVR3.  */
710         if (need_fs_type)
711           {
712             struct statfs fsd;
713             char typebuf[FSTYPSZ];
714
715             if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
716                 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
717               {
718                 me->me_type = xstrdup (typebuf);
719                 me->me_type_malloced = 1;
720               }
721           }
722 # endif
723         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
724         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
725
726         /* Add to the linked list. */
727         *mtail = me;
728         mtail = &me->me_next;
729       }
730
731     if (ferror (fp))
732       {
733         /* The last fread() call must have failed.  */
734         int saved_errno = errno;
735         fclose (fp);
736         errno = saved_errno;
737         goto free_then_fail;
738       }
739
740     if (fclose (fp) == EOF)
741       goto free_then_fail;
742   }
743 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
744
745 #ifdef MOUNTED_GETMNTTBL        /* DolphinOS goes its own way.  */
746   {
747     struct mntent **mnttbl = getmnttbl (), **ent;
748     for (ent = mnttbl; *ent; ent++)
749       {
750         me = xmalloc (sizeof *me);
751         me->me_devname = xstrdup ((*ent)->mt_resource);
752         me->me_mountdir = xstrdup ((*ent)->mt_directory);
753         me->me_type = xstrdup ((*ent)->mt_fstype);
754         me->me_type_malloced = 1;
755         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
756         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
757         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
758
759         /* Add to the linked list. */
760         *mtail = me;
761         mtail = &me->me_next;
762       }
763     endmnttbl ();
764   }
765 #endif
766
767 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
768   {
769     struct mnttab mnt;
770     char *table = MNTTAB;
771     FILE *fp;
772     int ret;
773     int lockfd = -1;
774
775 # if defined F_RDLCK && defined F_SETLKW
776     /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
777        e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
778        for this file name, we should use their macro name instead.
779        (Why not just lock MNTTAB directly?  We don't know.)  */
780 #  ifndef MNTTAB_LOCK
781 #   define MNTTAB_LOCK "/etc/.mnttab.lock"
782 #  endif
783     lockfd = open (MNTTAB_LOCK, O_RDONLY);
784     if (0 <= lockfd)
785       {
786         struct flock flock;
787         flock.l_type = F_RDLCK;
788         flock.l_whence = SEEK_SET;
789         flock.l_start = 0;
790         flock.l_len = 0;
791         while (fcntl (lockfd, F_SETLKW, &flock) == -1)
792           if (errno != EINTR)
793             {
794               int saved_errno = errno;
795               close (lockfd);
796               errno = saved_errno;
797               return NULL;
798             }
799       }
800     else if (errno != ENOENT)
801       return NULL;
802 # endif
803
804     errno = 0;
805     fp = fopen (table, "r");
806     if (fp == NULL)
807       ret = errno;
808     else
809       {
810         while ((ret = getmntent (fp, &mnt)) == 0)
811           {
812             me = xmalloc (sizeof *me);
813             me->me_devname = xstrdup (mnt.mnt_special);
814             me->me_mountdir = xstrdup (mnt.mnt_mountp);
815             me->me_type = xstrdup (mnt.mnt_fstype);
816             me->me_type_malloced = 1;
817             me->me_dummy = MNT_IGNORE (&mnt) != 0;
818             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
819             me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
820
821             /* Add to the linked list. */
822             *mtail = me;
823             mtail = &me->me_next;
824           }
825
826         ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
827       }
828
829     if (0 <= lockfd && close (lockfd) != 0)
830       ret = errno;
831
832     if (0 <= ret)
833       {
834         errno = ret;
835         goto free_then_fail;
836       }
837   }
838 #endif /* MOUNTED_GETMNTENT2.  */
839
840 #ifdef MOUNTED_VMOUNT           /* AIX.  */
841   {
842     int bufsize;
843     char *entries, *thisent;
844     struct vmount *vmp;
845     int n_entries;
846     int i;
847
848     /* Ask how many bytes to allocate for the mounted file system info.  */
849     if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
850       return NULL;
851     entries = xmalloc (bufsize);
852
853     /* Get the list of mounted file systems.  */
854     n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
855     if (n_entries < 0)
856       {
857         int saved_errno = errno;
858         free (entries);
859         errno = saved_errno;
860         return NULL;
861       }
862
863     for (i = 0, thisent = entries;
864          i < n_entries;
865          i++, thisent += vmp->vmt_length)
866       {
867         char *options, *ignore;
868
869         vmp = (struct vmount *) thisent;
870         me = xmalloc (sizeof *me);
871         if (vmp->vmt_flags & MNT_REMOTE)
872           {
873             char *host, *dir;
874
875             me->me_remote = 1;
876             /* Prepend the remote dirname.  */
877             host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
878             dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
879             me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
880             strcpy (me->me_devname, host);
881             strcat (me->me_devname, ":");
882             strcat (me->me_devname, dir);
883           }
884         else
885           {
886             me->me_remote = 0;
887             me->me_devname = xstrdup (thisent +
888                                       vmp->vmt_data[VMT_OBJECT].vmt_off);
889           }
890         me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
891         me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
892         me->me_type_malloced = 1;
893         options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
894         ignore = strstr (options, "ignore");
895         me->me_dummy = (ignore
896                         && (ignore == options || ignore[-1] == ',')
897                         && (ignore[sizeof "ignore" - 1] == ','
898                             || ignore[sizeof "ignore" - 1] == '\0'));
899         me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
900
901         /* Add to the linked list. */
902         *mtail = me;
903         mtail = &me->me_next;
904       }
905     free (entries);
906   }
907 #endif /* MOUNTED_VMOUNT. */
908
909 #ifdef MOUNTED_INTERIX_STATVFS
910   {
911     DIR *dirp = opendir ("/dev/fs");
912     char node[9 + NAME_MAX];
913
914     if (!dirp)
915       goto free_then_fail;
916
917     while (1)
918       {
919         struct statvfs dev;
920         struct dirent entry;
921         struct dirent *result;
922
923         if (readdir_r (dirp, &entry, &result) || result == NULL)
924           break;
925
926         strcpy (node, "/dev/fs/");
927         strcat (node, entry.d_name);
928
929         if (statvfs (node, &dev) == 0)
930           {
931             me = xmalloc (sizeof *me);
932             me->me_devname = xstrdup (dev.f_mntfromname);
933             me->me_mountdir = xstrdup (dev.f_mntonname);
934             me->me_type = xstrdup (dev.f_fstypename);
935             me->me_type_malloced = 1;
936             me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
937             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
938             me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
939
940             /* Add to the linked list. */
941             *mtail = me;
942             mtail = &me->me_next;
943           }
944       }
945   }
946 #endif /* MOUNTED_INTERIX_STATVFS */
947
948   *mtail = NULL;
949   return mount_list;
950
951
952  free_then_fail:
953   {
954     int saved_errno = errno;
955     *mtail = NULL;
956
957     while (mount_list)
958       {
959         me = mount_list->me_next;
960         free (mount_list->me_devname);
961         free (mount_list->me_mountdir);
962         if (mount_list->me_type_malloced)
963           free (mount_list->me_type);
964         free (mount_list);
965         mount_list = me;
966       }
967
968     errno = saved_errno;
969     return NULL;
970   }
971 }