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