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