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