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