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