In commentary, do not use ` to quote.
[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 #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       {
389         mnt = p->ment;
390         me = xmalloc (sizeof *me);
391         me->me_devname = xstrdup (mnt->mnt_fsname);
392         me->me_mountdir = xstrdup (mnt->mnt_dir);
393         me->me_type = xstrdup (mnt->mnt_type);
394         me->me_type_malloced = 1;
395         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
396         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
397         me->me_dev = -1;
398         *mtail = me;
399         mtail = &me->me_next;
400       }
401     freemntlist (mntlist);
402   }
403 #endif
404
405 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
406   {
407     struct mntent *mnt;
408     char const *table = MOUNTED;
409     FILE *fp;
410
411     fp = setmntent (table, "r");
412     if (fp == NULL)
413       return NULL;
414
415     while ((mnt = getmntent (fp)))
416       {
417         me = xmalloc (sizeof *me);
418         me->me_devname = xstrdup (mnt->mnt_fsname);
419         me->me_mountdir = xstrdup (mnt->mnt_dir);
420         me->me_type = xstrdup (mnt->mnt_type);
421         me->me_type_malloced = 1;
422         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
423         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
424         me->me_dev = dev_from_mount_options (mnt->mnt_opts);
425
426         /* Add to the linked list. */
427         *mtail = me;
428         mtail = &me->me_next;
429       }
430
431     if (endmntent (fp) == 0)
432       goto free_then_fail;
433   }
434 #endif /* MOUNTED_GETMNTENT1. */
435
436 #ifdef MOUNTED_GETMNTINFO       /* 4.4BSD.  */
437   {
438     struct statfs *fsp;
439     int entries;
440
441     entries = getmntinfo (&fsp, MNT_NOWAIT);
442     if (entries < 0)
443       return NULL;
444     for (; entries-- > 0; fsp++)
445       {
446         char *fs_type = fsp_to_string (fsp);
447
448         me = xmalloc (sizeof *me);
449         me->me_devname = xstrdup (fsp->f_mntfromname);
450         me->me_mountdir = xstrdup (fsp->f_mntonname);
451         me->me_type = fs_type;
452         me->me_type_malloced = 0;
453         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
454         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
455         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
456
457         /* Add to the linked list. */
458         *mtail = me;
459         mtail = &me->me_next;
460       }
461   }
462 #endif /* MOUNTED_GETMNTINFO */
463
464 #ifdef MOUNTED_GETMNTINFO2      /* NetBSD 3.0.  */
465   {
466     struct statvfs *fsp;
467     int entries;
468
469     entries = getmntinfo (&fsp, MNT_NOWAIT);
470     if (entries < 0)
471       return NULL;
472     for (; entries-- > 0; fsp++)
473       {
474         me = xmalloc (sizeof *me);
475         me->me_devname = xstrdup (fsp->f_mntfromname);
476         me->me_mountdir = xstrdup (fsp->f_mntonname);
477         me->me_type = xstrdup (fsp->f_fstypename);
478         me->me_type_malloced = 1;
479         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
480         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
481         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
482
483         /* Add to the linked list. */
484         *mtail = me;
485         mtail = &me->me_next;
486       }
487   }
488 #endif /* MOUNTED_GETMNTINFO2 */
489
490 #ifdef MOUNTED_GETMNT           /* Ultrix.  */
491   {
492     int offset = 0;
493     int val;
494     struct fs_data fsd;
495
496     while (errno = 0,
497            0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
498                               (char *) 0)))
499       {
500         me = xmalloc (sizeof *me);
501         me->me_devname = xstrdup (fsd.fd_req.devname);
502         me->me_mountdir = xstrdup (fsd.fd_req.path);
503         me->me_type = gt_names[fsd.fd_req.fstype];
504         me->me_type_malloced = 0;
505         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
506         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
507         me->me_dev = fsd.fd_req.dev;
508
509         /* Add to the linked list. */
510         *mtail = me;
511         mtail = &me->me_next;
512       }
513     if (val < 0)
514       goto free_then_fail;
515   }
516 #endif /* MOUNTED_GETMNT. */
517
518 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
519   {
520     /* The next_dev() and fs_stat_dev() system calls give the list of
521        all file systems, including the information returned by statvfs()
522        (fs type, total blocks, free blocks etc.), but without the mount
523        point. But on BeOS all file systems except / are mounted in the
524        rootfs, directly under /.
525        The directory name of the mount point is often, but not always,
526        identical to the volume name of the device.
527        We therefore get the list of subdirectories of /, and the list
528        of all file systems, and match the two lists.  */
529
530     DIR *dirp;
531     struct rootdir_entry
532       {
533         char *name;
534         dev_t dev;
535         ino_t ino;
536         struct rootdir_entry *next;
537       };
538     struct rootdir_entry *rootdir_list;
539     struct rootdir_entry **rootdir_tail;
540     int32 pos;
541     dev_t dev;
542     fs_info fi;
543
544     /* All volumes are mounted in the rootfs, directly under /. */
545     rootdir_list = NULL;
546     rootdir_tail = &rootdir_list;
547     dirp = opendir ("/");
548     if (dirp)
549       {
550         struct dirent *d;
551
552         while ((d = readdir (dirp)) != NULL)
553           {
554             char *name;
555             struct stat statbuf;
556
557             if (strcmp (d->d_name, "..") == 0)
558               continue;
559
560             if (strcmp (d->d_name, ".") == 0)
561               name = xstrdup ("/");
562             else
563               {
564                 name = xmalloc (1 + strlen (d->d_name) + 1);
565                 name[0] = '/';
566                 strcpy (name + 1, d->d_name);
567               }
568
569             if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
570               {
571                 struct rootdir_entry *re = xmalloc (sizeof *re);
572                 re->name = name;
573                 re->dev = statbuf.st_dev;
574                 re->ino = statbuf.st_ino;
575
576                 /* Add to the linked list.  */
577                 *rootdir_tail = re;
578                 rootdir_tail = &re->next;
579               }
580             else
581               free (name);
582           }
583         closedir (dirp);
584       }
585     *rootdir_tail = NULL;
586
587     for (pos = 0; (dev = next_dev (&pos)) >= 0; )
588       if (fs_stat_dev (dev, &fi) >= 0)
589         {
590           /* Note: fi.dev == dev. */
591           struct rootdir_entry *re;
592
593           for (re = rootdir_list; re; re = re->next)
594             if (re->dev == fi.dev && re->ino == fi.root)
595               break;
596
597           me = xmalloc (sizeof *me);
598           me->me_devname = xstrdup (fi.device_name[0] != '\0'
599                                     ? fi.device_name : fi.fsh_name);
600           me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
601           me->me_type = xstrdup (fi.fsh_name);
602           me->me_type_malloced = 1;
603           me->me_dev = fi.dev;
604           me->me_dummy = 0;
605           me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
606
607           /* Add to the linked list. */
608           *mtail = me;
609           mtail = &me->me_next;
610         }
611     *mtail = NULL;
612
613     while (rootdir_list != NULL)
614       {
615         struct rootdir_entry *re = rootdir_list;
616         rootdir_list = re->next;
617         free (re->name);
618         free (re);
619       }
620   }
621 #endif /* MOUNTED_FS_STAT_DEV */
622
623 #if defined MOUNTED_GETFSSTAT   /* __alpha running OSF_1 */
624   {
625     int numsys, counter;
626     size_t bufsize;
627     struct statfs *stats;
628
629     numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
630     if (numsys < 0)
631       return NULL;
632     if (SIZE_MAX / sizeof *stats <= numsys)
633       xalloc_die ();
634
635     bufsize = (1 + numsys) * sizeof *stats;
636     stats = xmalloc (bufsize);
637     numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
638
639     if (numsys < 0)
640       {
641         free (stats);
642         return NULL;
643       }
644
645     for (counter = 0; counter < numsys; counter++)
646       {
647         me = xmalloc (sizeof *me);
648         me->me_devname = xstrdup (stats[counter].f_mntfromname);
649         me->me_mountdir = xstrdup (stats[counter].f_mntonname);
650         me->me_type = xstrdup (FS_TYPE (stats[counter]));
651         me->me_type_malloced = 1;
652         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
653         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
654         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
655
656         /* Add to the linked list. */
657         *mtail = me;
658         mtail = &me->me_next;
659       }
660
661     free (stats);
662   }
663 #endif /* MOUNTED_GETFSSTAT */
664
665 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23].  */
666   {
667     struct mnttab mnt;
668     char *table = "/etc/mnttab";
669     FILE *fp;
670
671     fp = fopen (table, "r");
672     if (fp == NULL)
673       return NULL;
674
675     while (fread (&mnt, sizeof mnt, 1, fp) > 0)
676       {
677         me = xmalloc (sizeof *me);
678 # ifdef GETFSTYP                        /* SVR3.  */
679         me->me_devname = xstrdup (mnt.mt_dev);
680 # else
681         me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
682         strcpy (me->me_devname, "/dev/");
683         strcpy (me->me_devname + 5, mnt.mt_dev);
684 # endif
685         me->me_mountdir = xstrdup (mnt.mt_filsys);
686         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
687         me->me_type = "";
688         me->me_type_malloced = 0;
689 # ifdef GETFSTYP                        /* SVR3.  */
690         if (need_fs_type)
691           {
692             struct statfs fsd;
693             char typebuf[FSTYPSZ];
694
695             if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
696                 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
697               {
698                 me->me_type = xstrdup (typebuf);
699                 me->me_type_malloced = 1;
700               }
701           }
702 # endif
703         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
704         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
705
706         /* Add to the linked list. */
707         *mtail = me;
708         mtail = &me->me_next;
709       }
710
711     if (ferror (fp))
712       {
713         /* The last fread() call must have failed.  */
714         int saved_errno = errno;
715         fclose (fp);
716         errno = saved_errno;
717         goto free_then_fail;
718       }
719
720     if (fclose (fp) == EOF)
721       goto free_then_fail;
722   }
723 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
724
725 #ifdef MOUNTED_GETMNTTBL        /* DolphinOS goes its own way.  */
726   {
727     struct mntent **mnttbl = getmnttbl (), **ent;
728     for (ent = mnttbl; *ent; ent++)
729       {
730         me = xmalloc (sizeof *me);
731         me->me_devname = xstrdup ((*ent)->mt_resource);
732         me->me_mountdir = xstrdup ((*ent)->mt_directory);
733         me->me_type = xstrdup ((*ent)->mt_fstype);
734         me->me_type_malloced = 1;
735         me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
736         me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
737         me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
738
739         /* Add to the linked list. */
740         *mtail = me;
741         mtail = &me->me_next;
742       }
743     endmnttbl ();
744   }
745 #endif
746
747 #ifdef MOUNTED_GETMNTENT2       /* SVR4.  */
748   {
749     struct mnttab mnt;
750     char *table = MNTTAB;
751     FILE *fp;
752     int ret;
753     int lockfd = -1;
754
755 # if defined F_RDLCK && defined F_SETLKW
756     /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
757        e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
758        for this file name, we should use their macro name instead.
759        (Why not just lock MNTTAB directly?  We don't know.)  */
760 #  ifndef MNTTAB_LOCK
761 #   define MNTTAB_LOCK "/etc/.mnttab.lock"
762 #  endif
763     lockfd = open (MNTTAB_LOCK, O_RDONLY);
764     if (0 <= lockfd)
765       {
766         struct flock flock;
767         flock.l_type = F_RDLCK;
768         flock.l_whence = SEEK_SET;
769         flock.l_start = 0;
770         flock.l_len = 0;
771         while (fcntl (lockfd, F_SETLKW, &flock) == -1)
772           if (errno != EINTR)
773             {
774               int saved_errno = errno;
775               close (lockfd);
776               errno = saved_errno;
777               return NULL;
778             }
779       }
780     else if (errno != ENOENT)
781       return NULL;
782 # endif
783
784     errno = 0;
785     fp = fopen (table, "r");
786     if (fp == NULL)
787       ret = errno;
788     else
789       {
790         while ((ret = getmntent (fp, &mnt)) == 0)
791           {
792             me = xmalloc (sizeof *me);
793             me->me_devname = xstrdup (mnt.mnt_special);
794             me->me_mountdir = xstrdup (mnt.mnt_mountp);
795             me->me_type = xstrdup (mnt.mnt_fstype);
796             me->me_type_malloced = 1;
797             me->me_dummy = MNT_IGNORE (&mnt) != 0;
798             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
799             me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
800
801             /* Add to the linked list. */
802             *mtail = me;
803             mtail = &me->me_next;
804           }
805
806         ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
807       }
808
809     if (0 <= lockfd && close (lockfd) != 0)
810       ret = errno;
811
812     if (0 <= ret)
813       {
814         errno = ret;
815         goto free_then_fail;
816       }
817   }
818 #endif /* MOUNTED_GETMNTENT2.  */
819
820 #ifdef MOUNTED_VMOUNT           /* AIX.  */
821   {
822     int bufsize;
823     char *entries, *thisent;
824     struct vmount *vmp;
825     int n_entries;
826     int i;
827
828     /* Ask how many bytes to allocate for the mounted file system info.  */
829     if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
830       return NULL;
831     entries = xmalloc (bufsize);
832
833     /* Get the list of mounted file systems.  */
834     n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
835     if (n_entries < 0)
836       {
837         int saved_errno = errno;
838         free (entries);
839         errno = saved_errno;
840         return NULL;
841       }
842
843     for (i = 0, thisent = entries;
844          i < n_entries;
845          i++, thisent += vmp->vmt_length)
846       {
847         char *options, *ignore;
848
849         vmp = (struct vmount *) thisent;
850         me = xmalloc (sizeof *me);
851         if (vmp->vmt_flags & MNT_REMOTE)
852           {
853             char *host, *dir;
854
855             me->me_remote = 1;
856             /* Prepend the remote dirname.  */
857             host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
858             dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
859             me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
860             strcpy (me->me_devname, host);
861             strcat (me->me_devname, ":");
862             strcat (me->me_devname, dir);
863           }
864         else
865           {
866             me->me_remote = 0;
867             me->me_devname = xstrdup (thisent +
868                                       vmp->vmt_data[VMT_OBJECT].vmt_off);
869           }
870         me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
871         me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
872         me->me_type_malloced = 1;
873         options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
874         ignore = strstr (options, "ignore");
875         me->me_dummy = (ignore
876                         && (ignore == options || ignore[-1] == ',')
877                         && (ignore[sizeof "ignore" - 1] == ','
878                             || ignore[sizeof "ignore" - 1] == '\0'));
879         me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
880
881         /* Add to the linked list. */
882         *mtail = me;
883         mtail = &me->me_next;
884       }
885     free (entries);
886   }
887 #endif /* MOUNTED_VMOUNT. */
888
889 #ifdef MOUNTED_INTERIX_STATVFS
890   {
891     DIR *dirp = opendir ("/dev/fs");
892     char node[9 + NAME_MAX];
893
894     if (!dirp)
895       goto free_then_fail;
896
897     while (1)
898       {
899         struct statvfs dev;
900         struct dirent entry;
901         struct dirent *result;
902
903         if (readdir_r (dirp, &entry, &result) || result == NULL)
904           break;
905
906         strcpy (node, "/dev/fs/");
907         strcat (node, entry.d_name);
908
909         if (statvfs (node, &dev) == 0)
910           {
911             me = xmalloc (sizeof *me);
912             me->me_devname = xstrdup (dev.f_mntfromname);
913             me->me_mountdir = xstrdup (dev.f_mntonname);
914             me->me_type = xstrdup (dev.f_fstypename);
915             me->me_type_malloced = 1;
916             me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
917             me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
918             me->me_dev = (dev_t) -1;        /* Magic; means not known yet. */
919
920             /* Add to the linked list. */
921             *mtail = me;
922             mtail = &me->me_next;
923           }
924       }
925   }
926 #endif /* MOUNTED_INTERIX_STATVFS */
927
928   *mtail = NULL;
929   return mount_list;
930
931
932  free_then_fail:
933   {
934     int saved_errno = errno;
935     *mtail = NULL;
936
937     while (mount_list)
938       {
939         me = mount_list->me_next;
940         free (mount_list->me_devname);
941         free (mount_list->me_mountdir);
942         if (mount_list->me_type_malloced)
943           free (mount_list->me_type);
944         free (mount_list);
945         mount_list = me;
946       }
947
948     errno = saved_errno;
949     return NULL;
950   }
951 }