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