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