1/* mountlist.c -- return a list of mounted file systems
2
3   Copyright (C) 1991, 1992, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
4   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5
6   This program is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19#include <config.h>
20
21#include "mountlist.h"
22
23#include <limits.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "xalloc.h"
29
30#include <errno.h>
31
32#include <fcntl.h>
33
34#include <unistd.h>
35
36#if HAVE_SYS_PARAM_H
37# include <sys/param.h>
38#endif
39
40#if defined MOUNTED_GETFSSTAT	/* OSF_1 and Darwin1.3.x */
41# if HAVE_SYS_UCRED_H
42#  include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
43		      NGROUPS is used as an array dimension in ucred.h */
44#  include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
45# endif
46# if HAVE_SYS_MOUNT_H
47#  include <sys/mount.h>
48# endif
49# if HAVE_SYS_FS_TYPES_H
50#  include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
51# endif
52# if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
53#  define FS_TYPE(Ent) ((Ent).f_fstypename)
54# else
55#  define FS_TYPE(Ent) mnt_names[(Ent).f_type]
56# endif
57#endif /* MOUNTED_GETFSSTAT */
58
59#ifdef MOUNTED_GETMNTENT1	/* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
60# include <mntent.h>
61# if !defined MOUNTED
62#  if defined _PATH_MOUNTED	/* GNU libc  */
63#   define MOUNTED _PATH_MOUNTED
64#  endif
65#  if defined MNT_MNTTAB	/* HP-UX.  */
66#   define MOUNTED MNT_MNTTAB
67#  endif
68#  if defined MNTTABNAME	/* Dynix.  */
69#   define MOUNTED MNTTABNAME
70#  endif
71# endif
72#endif
73
74#ifdef MOUNTED_GETMNTINFO	/* 4.4BSD.  */
75# include <sys/mount.h>
76#endif
77
78#ifdef MOUNTED_GETMNTINFO2	/* NetBSD 3.0.  */
79# include <sys/statvfs.h>
80#endif
81
82#ifdef MOUNTED_GETMNT		/* Ultrix.  */
83# include <sys/mount.h>
84# include <sys/fs_types.h>
85#endif
86
87#ifdef MOUNTED_FS_STAT_DEV	/* BeOS.  */
88# include <fs_info.h>
89# include <dirent.h>
90#endif
91
92#ifdef MOUNTED_FREAD		/* SVR2.  */
93# include <mnttab.h>
94#endif
95
96#ifdef MOUNTED_FREAD_FSTYP	/* SVR3.  */
97# include <mnttab.h>
98# include <sys/fstyp.h>
99# include <sys/statfs.h>
100#endif
101
102#ifdef MOUNTED_LISTMNTENT
103# include <mntent.h>
104#endif
105
106#ifdef MOUNTED_GETMNTENT2	/* SVR4.  */
107# include <sys/mnttab.h>
108#endif
109
110#ifdef MOUNTED_VMOUNT		/* AIX.  */
111# include <fshelp.h>
112# include <sys/vfs.h>
113#endif
114
115#ifdef DOLPHIN
116/* So special that it's not worth putting this in autoconf.  */
117# undef MOUNTED_FREAD_FSTYP
118# define MOUNTED_GETMNTTBL
119#endif
120
121#if HAVE_SYS_MNTENT_H
122/* This is to get MNTOPT_IGNORE on e.g. SVR4.  */
123# include <sys/mntent.h>
124#endif
125
126#undef MNT_IGNORE
127#if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
128# define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
129#else
130# define MNT_IGNORE(M) 0
131#endif
132
133#if USE_UNLOCKED_IO
134# include "unlocked-io.h"
135#endif
136
137#ifndef SIZE_MAX
138# define SIZE_MAX ((size_t) -1)
139#endif
140
141/* The results of open() in this file are not used with fchdir,
142   therefore save some unnecessary work in fchdir.c.  */
143#undef open
144#undef close
145
146/* The results of opendir() in this file are not used with dirfd and fchdir,
147   therefore save some unnecessary work in fchdir.c.  */
148#undef opendir
149#undef closedir
150
151#ifndef ME_DUMMY
152# define ME_DUMMY(Fs_name, Fs_type)		\
153    (strcmp (Fs_type, "autofs") == 0		\
154     || strcmp (Fs_type, "none") == 0		\
155     || strcmp (Fs_type, "proc") == 0		\
156     || strcmp (Fs_type, "subfs") == 0		\
157     /* for NetBSD 3.0 */			\
158     || strcmp (Fs_type, "kernfs") == 0		\
159     /* for Irix 6.5 */				\
160     || strcmp (Fs_type, "ignore") == 0)
161#endif
162
163#ifndef ME_REMOTE
164/* A file system is `remote' if its Fs_name contains a `:'
165   or if (it is of type (smbfs or cifs) and its Fs_name starts with `//').  */
166# define ME_REMOTE(Fs_name, Fs_type)		\
167    (strchr (Fs_name, ':') != NULL		\
168     || ((Fs_name)[0] == '/'			\
169	 && (Fs_name)[1] == '/'			\
170	 && (strcmp (Fs_type, "smbfs") == 0	\
171	     || strcmp (Fs_type, "cifs") == 0)))
172#endif
173
174#if MOUNTED_GETMNTINFO
175
176# if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
177static char *
178fstype_to_string (short int t)
179{
180  switch (t)
181    {
182#  ifdef MOUNT_PC
183    case MOUNT_PC:
184      return "pc";
185#  endif
186#  ifdef MOUNT_MFS
187    case MOUNT_MFS:
188      return "mfs";
189#  endif
190#  ifdef MOUNT_LO
191    case MOUNT_LO:
192      return "lo";
193#  endif
194#  ifdef MOUNT_TFS
195    case MOUNT_TFS:
196      return "tfs";
197#  endif
198#  ifdef MOUNT_TMP
199    case MOUNT_TMP:
200      return "tmp";
201#  endif
202#  ifdef MOUNT_UFS
203   case MOUNT_UFS:
204     return "ufs" ;
205#  endif
206#  ifdef MOUNT_NFS
207   case MOUNT_NFS:
208     return "nfs" ;
209#  endif
210#  ifdef MOUNT_MSDOS
211   case MOUNT_MSDOS:
212     return "msdos" ;
213#  endif
214#  ifdef MOUNT_LFS
215   case MOUNT_LFS:
216     return "lfs" ;
217#  endif
218#  ifdef MOUNT_LOFS
219   case MOUNT_LOFS:
220     return "lofs" ;
221#  endif
222#  ifdef MOUNT_FDESC
223   case MOUNT_FDESC:
224     return "fdesc" ;
225#  endif
226#  ifdef MOUNT_PORTAL
227   case MOUNT_PORTAL:
228     return "portal" ;
229#  endif
230#  ifdef MOUNT_NULL
231   case MOUNT_NULL:
232     return "null" ;
233#  endif
234#  ifdef MOUNT_UMAP
235   case MOUNT_UMAP:
236     return "umap" ;
237#  endif
238#  ifdef MOUNT_KERNFS
239   case MOUNT_KERNFS:
240     return "kernfs" ;
241#  endif
242#  ifdef MOUNT_PROCFS
243   case MOUNT_PROCFS:
244     return "procfs" ;
245#  endif
246#  ifdef MOUNT_AFS
247   case MOUNT_AFS:
248     return "afs" ;
249#  endif
250#  ifdef MOUNT_CD9660
251   case MOUNT_CD9660:
252     return "cd9660" ;
253#  endif
254#  ifdef MOUNT_UNION
255   case MOUNT_UNION:
256     return "union" ;
257#  endif
258#  ifdef MOUNT_DEVFS
259   case MOUNT_DEVFS:
260     return "devfs" ;
261#  endif
262#  ifdef MOUNT_EXT2FS
263   case MOUNT_EXT2FS:
264     return "ext2fs" ;
265#  endif
266    default:
267      return "?";
268    }
269}
270# endif
271
272static char *
273fsp_to_string (const struct statfs *fsp)
274{
275# if HAVE_STRUCT_STATFS_F_FSTYPENAME
276  return (char *) (fsp->f_fstypename);
277# else
278  return fstype_to_string (fsp->f_type);
279# endif
280}
281
282#endif /* MOUNTED_GETMNTINFO */
283
284#ifdef MOUNTED_VMOUNT		/* AIX.  */
285static char *
286fstype_to_string (int t)
287{
288  struct vfs_ent *e;
289
290  e = getvfsbytype (t);
291  if (!e || !e->vfsent_name)
292    return "none";
293  else
294    return e->vfsent_name;
295}
296#endif /* MOUNTED_VMOUNT */
297
298
299#if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
300
301/* Return the device number from MOUNT_OPTIONS, if possible.
302   Otherwise return (dev_t) -1.  */
303
304static dev_t
305dev_from_mount_options (char const *mount_options)
306{
307  /* GNU/Linux allows file system implementations to define their own
308     meaning for "dev=" mount options, so don't trust the meaning
309     here.  */
310# ifndef __linux__
311
312  static char const dev_pattern[] = ",dev=";
313  char const *devopt = strstr (mount_options, dev_pattern);
314
315  if (devopt)
316    {
317      char const *optval = devopt + sizeof dev_pattern - 1;
318      char *optvalend;
319      unsigned long int dev;
320      errno = 0;
321      dev = strtoul (optval, &optvalend, 16);
322      if (optval != optvalend
323	  && (*optvalend == '\0' || *optvalend == ',')
324	  && ! (dev == ULONG_MAX && errno == ERANGE)
325	  && dev == (dev_t) dev)
326	return dev;
327    }
328
329# endif
330
331  return -1;
332}
333
334#endif
335
336/* Return a list of the currently mounted file systems, or NULL on error.
337   Add each entry to the tail of the list so that they stay in order.
338   If NEED_FS_TYPE is true, ensure that the file system type fields in
339   the returned list are valid.  Otherwise, they might not be.  */
340
341struct mount_entry *
342read_file_system_list (bool need_fs_type)
343{
344  struct mount_entry *mount_list;
345  struct mount_entry *me;
346  struct mount_entry **mtail = &mount_list;
347
348#ifdef MOUNTED_LISTMNTENT
349  {
350    struct tabmntent *mntlist, *p;
351    struct mntent *mnt;
352    struct mount_entry *me;
353
354    /* the third and fourth arguments could be used to filter mounts,
355       but Crays doesn't seem to have any mounts that we want to
356       remove. Specifically, automount create normal NFS mounts.
357       */
358
359    if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
360      return NULL;
361    for (p = mntlist; p; p = p->next) {
362      mnt = p->ment;
363      me = xmalloc (sizeof *me);
364      me->me_devname = xstrdup (mnt->mnt_fsname);
365      me->me_mountdir = xstrdup (mnt->mnt_dir);
366      me->me_type = xstrdup (mnt->mnt_type);
367      me->me_type_malloced = 1;
368      me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
369      me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
370      me->me_dev = -1;
371      *mtail = me;
372      mtail = &me->me_next;
373    }
374    freemntlist (mntlist);
375  }
376#endif
377
378#ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
379  {
380    struct mntent *mnt;
381    char *table = MOUNTED;
382    FILE *fp;
383
384    fp = setmntent (table, "r");
385    if (fp == NULL)
386      return NULL;
387
388    while ((mnt = getmntent (fp)))
389      {
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 = dev_from_mount_options (mnt->mnt_opts);
398
399	/* Add to the linked list. */
400	*mtail = me;
401	mtail = &me->me_next;
402      }
403
404    if (endmntent (fp) == 0)
405      goto free_then_fail;
406  }
407#endif /* MOUNTED_GETMNTENT1. */
408
409#ifdef MOUNTED_GETMNTINFO	/* 4.4BSD.  */
410  {
411    struct statfs *fsp;
412    int entries;
413
414    entries = getmntinfo (&fsp, MNT_NOWAIT);
415    if (entries < 0)
416      return NULL;
417    for (; entries-- > 0; fsp++)
418      {
419	char *fs_type = fsp_to_string (fsp);
420
421	me = xmalloc (sizeof *me);
422	me->me_devname = xstrdup (fsp->f_mntfromname);
423	me->me_mountdir = xstrdup (fsp->f_mntonname);
424	me->me_type = fs_type;
425	me->me_type_malloced = 0;
426	me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
427	me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
428	me->me_dev = (dev_t) -1;	/* Magic; means not known yet. */
429
430	/* Add to the linked list. */
431	*mtail = me;
432	mtail = &me->me_next;
433      }
434  }
435#endif /* MOUNTED_GETMNTINFO */
436
437#ifdef MOUNTED_GETMNTINFO2	/* NetBSD 3.0.  */
438  {
439    struct statvfs *fsp;
440    int entries;
441
442    entries = getmntinfo (&fsp, MNT_NOWAIT);
443    if (entries < 0)
444      return NULL;
445    for (; entries-- > 0; 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 = xstrdup (fsp->f_fstypename);
451	me->me_type_malloced = 1;
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_GETMNTINFO2 */
462
463#ifdef MOUNTED_GETMNT		/* Ultrix.  */
464  {
465    int offset = 0;
466    int val;
467    struct fs_data fsd;
468
469    while (errno = 0,
470	   0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
471			      (char *) 0)))
472      {
473	me = xmalloc (sizeof *me);
474	me->me_devname = xstrdup (fsd.fd_req.devname);
475	me->me_mountdir = xstrdup (fsd.fd_req.path);
476	me->me_type = gt_names[fsd.fd_req.fstype];
477	me->me_type_malloced = 0;
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 = fsd.fd_req.dev;
481
482	/* Add to the linked list. */
483	*mtail = me;
484	mtail = &me->me_next;
485      }
486    if (val < 0)
487      goto free_then_fail;
488  }
489#endif /* MOUNTED_GETMNT. */
490
491#if defined MOUNTED_FS_STAT_DEV /* BeOS */
492  {
493    /* The next_dev() and fs_stat_dev() system calls give the list of
494       all file systems, including the information returned by statvfs()
495       (fs type, total blocks, free blocks etc.), but without the mount
496       point. But on BeOS all file systems except / are mounted in the
497       rootfs, directly under /.
498       The directory name of the mount point is often, but not always,
499       identical to the volume name of the device.
500       We therefore get the list of subdirectories of /, and the list
501       of all file systems, and match the two lists.  */
502
503    DIR *dirp;
504    struct rootdir_entry
505      {
506        char *name;
507        dev_t dev;
508        ino_t ino;
509        struct rootdir_entry *next;
510      };
511    struct rootdir_entry *rootdir_list;
512    struct rootdir_entry **rootdir_tail;
513    int32 pos;
514    dev_t dev;
515    fs_info fi;
516
517    /* All volumes are mounted in the rootfs, directly under /. */
518    rootdir_list = NULL;
519    rootdir_tail = &rootdir_list;
520    dirp = opendir ("/");
521    if (dirp)
522      {
523        struct dirent *d;
524
525        while ((d = readdir (dirp)) != NULL)
526          {
527            char *name;
528            struct stat statbuf;
529
530            if (strcmp (d->d_name, "..") == 0)
531              continue;
532
533            if (strcmp (d->d_name, ".") == 0)
534              name = xstrdup ("/");
535            else
536              {
537                name = xmalloc (1 + strlen (d->d_name) + 1);
538                name[0] = '/';
539                strcpy (name + 1, d->d_name);
540              }
541
542            if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
543              {
544		struct rootdir_entry *re = xmalloc (sizeof *re);
545                re->name = name;
546                re->dev = statbuf.st_dev;
547                re->ino = statbuf.st_ino;
548
549                /* Add to the linked list.  */
550                *rootdir_tail = re;
551                rootdir_tail = &re->next;
552              }
553            else
554              free (name);
555          }
556        closedir (dirp);
557      }
558    *rootdir_tail = NULL;
559
560    for (pos = 0; (dev = next_dev (&pos)) >= 0; )
561      if (fs_stat_dev (dev, &fi) >= 0)
562        {
563          /* Note: fi.dev == dev. */
564          struct rootdir_entry *re;
565
566          for (re = rootdir_list; re; re = re->next)
567            if (re->dev == fi.dev && re->ino == fi.root)
568              break;
569
570	  me = xmalloc (sizeof *me);
571          me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
572          me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
573          me->me_type = xstrdup (fi.fsh_name);
574	  me->me_type_malloced = 1;
575          me->me_dev = fi.dev;
576          me->me_dummy = 0;
577          me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
578
579          /* Add to the linked list. */
580          *mtail = me;
581          mtail = &me->me_next;
582        }
583    *mtail = NULL;
584
585    while (rootdir_list != NULL)
586      {
587        struct rootdir_entry *re = rootdir_list;
588        rootdir_list = re->next;
589        free (re->name);
590        free (re);
591      }
592  }
593#endif /* MOUNTED_FS_STAT_DEV */
594
595#if defined MOUNTED_GETFSSTAT	/* __alpha running OSF_1 */
596  {
597    int numsys, counter;
598    size_t bufsize;
599    struct statfs *stats;
600
601    numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
602    if (numsys < 0)
603      return (NULL);
604    if (SIZE_MAX / sizeof *stats <= numsys)
605      xalloc_die ();
606
607    bufsize = (1 + numsys) * sizeof *stats;
608    stats = xmalloc (bufsize);
609    numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
610
611    if (numsys < 0)
612      {
613	free (stats);
614	return (NULL);
615      }
616
617    for (counter = 0; counter < numsys; counter++)
618      {
619	me = xmalloc (sizeof *me);
620	me->me_devname = xstrdup (stats[counter].f_mntfromname);
621	me->me_mountdir = xstrdup (stats[counter].f_mntonname);
622	me->me_type = xstrdup (FS_TYPE (stats[counter]));
623	me->me_type_malloced = 1;
624	me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
625	me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
626	me->me_dev = (dev_t) -1;	/* Magic; means not known yet. */
627
628	/* Add to the linked list. */
629	*mtail = me;
630	mtail = &me->me_next;
631      }
632
633    free (stats);
634  }
635#endif /* MOUNTED_GETFSSTAT */
636
637#if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23].  */
638  {
639    struct mnttab mnt;
640    char *table = "/etc/mnttab";
641    FILE *fp;
642
643    fp = fopen (table, "r");
644    if (fp == NULL)
645      return NULL;
646
647    while (fread (&mnt, sizeof mnt, 1, fp) > 0)
648      {
649	me = xmalloc (sizeof *me);
650# ifdef GETFSTYP			/* SVR3.  */
651	me->me_devname = xstrdup (mnt.mt_dev);
652# else
653	me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
654	strcpy (me->me_devname, "/dev/");
655	strcpy (me->me_devname + 5, mnt.mt_dev);
656# endif
657	me->me_mountdir = xstrdup (mnt.mt_filsys);
658	me->me_dev = (dev_t) -1;	/* Magic; means not known yet. */
659	me->me_type = "";
660	me->me_type_malloced = 0;
661# ifdef GETFSTYP			/* SVR3.  */
662	if (need_fs_type)
663	  {
664	    struct statfs fsd;
665	    char typebuf[FSTYPSZ];
666
667	    if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
668		&& sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
669	      {
670		me->me_type = xstrdup (typebuf);
671		me->me_type_malloced = 1;
672	      }
673	  }
674# endif
675	me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
676	me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
677
678	/* Add to the linked list. */
679	*mtail = me;
680	mtail = &me->me_next;
681      }
682
683    if (ferror (fp))
684      {
685	/* The last fread() call must have failed.  */
686	int saved_errno = errno;
687	fclose (fp);
688	errno = saved_errno;
689	goto free_then_fail;
690      }
691
692    if (fclose (fp) == EOF)
693      goto free_then_fail;
694  }
695#endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
696
697#ifdef MOUNTED_GETMNTTBL	/* DolphinOS goes its own way.  */
698  {
699    struct mntent **mnttbl = getmnttbl (), **ent;
700    for (ent=mnttbl;*ent;ent++)
701      {
702	me = xmalloc (sizeof *me);
703	me->me_devname = xstrdup ( (*ent)->mt_resource);
704	me->me_mountdir = xstrdup ( (*ent)->mt_directory);
705	me->me_type = xstrdup ((*ent)->mt_fstype);
706	me->me_type_malloced = 1;
707	me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
708	me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
709	me->me_dev = (dev_t) -1;	/* Magic; means not known yet. */
710
711	/* Add to the linked list. */
712	*mtail = me;
713	mtail = &me->me_next;
714      }
715    endmnttbl ();
716  }
717#endif
718
719#ifdef MOUNTED_GETMNTENT2	/* SVR4.  */
720  {
721    struct mnttab mnt;
722    char *table = MNTTAB;
723    FILE *fp;
724    int ret;
725    int lockfd = -1;
726
727# if defined F_RDLCK && defined F_SETLKW
728    /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
729       e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
730       for this file name, we should use their macro name instead.
731       (Why not just lock MNTTAB directly?  We don't know.)  */
732#  ifndef MNTTAB_LOCK
733#   define MNTTAB_LOCK "/etc/.mnttab.lock"
734#  endif
735    lockfd = open (MNTTAB_LOCK, O_RDONLY);
736    if (0 <= lockfd)
737      {
738	struct flock flock;
739	flock.l_type = F_RDLCK;
740	flock.l_whence = SEEK_SET;
741	flock.l_start = 0;
742	flock.l_len = 0;
743	while (fcntl (lockfd, F_SETLKW, &flock) == -1)
744	  if (errno != EINTR)
745	    {
746	      int saved_errno = errno;
747	      close (lockfd);
748	      errno = saved_errno;
749	      return NULL;
750	    }
751      }
752    else if (errno != ENOENT)
753      return NULL;
754# endif
755
756    errno = 0;
757    fp = fopen (table, "r");
758    if (fp == NULL)
759      ret = errno;
760    else
761      {
762	while ((ret = getmntent (fp, &mnt)) == 0)
763	  {
764	    me = xmalloc (sizeof *me);
765	    me->me_devname = xstrdup (mnt.mnt_special);
766	    me->me_mountdir = xstrdup (mnt.mnt_mountp);
767	    me->me_type = xstrdup (mnt.mnt_fstype);
768	    me->me_type_malloced = 1;
769	    me->me_dummy = MNT_IGNORE (&mnt) != 0;
770	    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
771	    me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
772
773	    /* Add to the linked list. */
774	    *mtail = me;
775	    mtail = &me->me_next;
776	  }
777
778	ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
779      }
780
781    if (0 <= lockfd && close (lockfd) != 0)
782      ret = errno;
783
784    if (0 <= ret)
785      {
786	errno = ret;
787	goto free_then_fail;
788      }
789  }
790#endif /* MOUNTED_GETMNTENT2.  */
791
792#ifdef MOUNTED_VMOUNT		/* AIX.  */
793  {
794    int bufsize;
795    char *entries, *thisent;
796    struct vmount *vmp;
797    int n_entries;
798    int i;
799
800    /* Ask how many bytes to allocate for the mounted file system info.  */
801    if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
802      return NULL;
803    entries = xmalloc (bufsize);
804
805    /* Get the list of mounted file systems.  */
806    n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
807    if (n_entries < 0)
808      {
809	int saved_errno = errno;
810	free (entries);
811	errno = saved_errno;
812	return NULL;
813      }
814
815    for (i = 0, thisent = entries;
816	 i < n_entries;
817	 i++, thisent += vmp->vmt_length)
818      {
819	char *options, *ignore;
820
821	vmp = (struct vmount *) thisent;
822	me = xmalloc (sizeof *me);
823	if (vmp->vmt_flags & MNT_REMOTE)
824	  {
825	    char *host, *dir;
826
827	    me->me_remote = 1;
828	    /* Prepend the remote dirname.  */
829	    host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
830	    dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
831	    me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
832	    strcpy (me->me_devname, host);
833	    strcat (me->me_devname, ":");
834	    strcat (me->me_devname, dir);
835	  }
836	else
837	  {
838	    me->me_remote = 0;
839	    me->me_devname = xstrdup (thisent +
840				      vmp->vmt_data[VMT_OBJECT].vmt_off);
841	  }
842	me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
843	me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
844	me->me_type_malloced = 1;
845	options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
846	ignore = strstr (options, "ignore");
847	me->me_dummy = (ignore
848			&& (ignore == options || ignore[-1] == ',')
849			&& (ignore[sizeof "ignore" - 1] == ','
850			    || ignore[sizeof "ignore" - 1] == '\0'));
851	me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */
852
853	/* Add to the linked list. */
854	*mtail = me;
855	mtail = &me->me_next;
856      }
857    free (entries);
858  }
859#endif /* MOUNTED_VMOUNT. */
860
861  *mtail = NULL;
862  return mount_list;
863
864
865 free_then_fail:
866  {
867    int saved_errno = errno;
868    *mtail = NULL;
869
870    while (mount_list)
871      {
872	me = mount_list->me_next;
873	free (mount_list->me_devname);
874	free (mount_list->me_mountdir);
875	if (mount_list->me_type_malloced)
876	  free (mount_list->me_type);
877	free (mount_list);
878	mount_list = me;
879      }
880
881    errno = saved_errno;
882    return NULL;
883  }
884}
885