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