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