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