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