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