Ensure Automake does not drop ~~gnulib.m4.
[gnulib.git] / lib / mountlist.c
blob61063ce91cf92bce8cb72839b6a09e80055e7b1d
1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2020 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 #include <config.h>
20 #include "mountlist.h"
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h>
28 #include "xalloc.h"
30 #include <errno.h>
32 #include <fcntl.h>
34 #include <unistd.h>
36 #if HAVE_SYS_PARAM_H
37 # include <sys/param.h>
38 #endif
40 #if MAJOR_IN_MKDEV
41 # include <sys/mkdev.h>
42 #elif MAJOR_IN_SYSMACROS
43 # include <sys/sysmacros.h>
44 #endif
46 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
47 # if HAVE_SYS_UCRED_H
48 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
49 NGROUPS is used as an array dimension in ucred.h */
50 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
51 # endif
52 # if HAVE_SYS_MOUNT_H
53 # include <sys/mount.h>
54 # endif
55 # if HAVE_SYS_FS_TYPES_H
56 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
57 # endif
58 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
59 # define FS_TYPE(Ent) ((Ent).f_fstypename)
60 # else
61 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
62 # endif
63 #endif /* MOUNTED_GETFSSTAT */
65 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
66 also (obsolete) 4.3BSD, SunOS */
67 # include <mntent.h>
68 # include <sys/types.h>
69 # if defined __ANDROID__ /* Android */
70 /* Bionic versions from between 2014-01-09 and 2015-01-08 define MOUNTED to
71 an incorrect value; older Bionic versions don't define it at all. */
72 # undef MOUNTED
73 # define MOUNTED "/proc/mounts"
74 # elif !defined MOUNTED
75 # if defined _PATH_MOUNTED /* GNU libc */
76 # define MOUNTED _PATH_MOUNTED
77 # endif
78 # if defined MNT_MNTTAB /* HP-UX. */
79 # define MOUNTED MNT_MNTTAB
80 # endif
81 # endif
82 #endif
84 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
85 # include <sys/mount.h>
86 #endif
88 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
89 # include <sys/statvfs.h>
90 #endif
92 #ifdef MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
93 # include <fs_info.h>
94 # include <dirent.h>
95 #endif
97 #ifdef MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
98 # include <mnttab.h>
99 # include <sys/fstyp.h>
100 # include <sys/statfs.h>
101 #endif
103 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
104 # include <sys/mnttab.h>
105 #endif
107 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
108 # include <sys/mnttab.h>
109 #endif
111 #ifdef MOUNTED_VMOUNT /* AIX */
112 # include <fshelp.h>
113 # include <sys/vfs.h>
114 #endif
116 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
117 # include <sys/statvfs.h>
118 # include <dirent.h>
119 #endif
121 #if HAVE_SYS_MNTENT_H
122 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
123 # include <sys/mntent.h>
124 #endif
126 #ifdef MOUNTED_GETMNTENT1
127 # if !HAVE_SETMNTENT /* Android <= 4.4 */
128 # define setmntent(fp,mode) fopen (fp, mode)
129 # endif
130 # if !HAVE_ENDMNTENT /* Android <= 4.4 */
131 # define endmntent(fp) fclose (fp)
132 # endif
133 #endif
135 #ifndef HAVE_HASMNTOPT
136 # define hasmntopt(mnt, opt) ((char *) 0)
137 #endif
139 #undef MNT_IGNORE
140 #ifdef MNTOPT_IGNORE
141 # if defined __sun && defined __SVR4
142 /* Solaris defines hasmntopt(struct mnttab *, char *)
143 while it is otherwise hasmntopt(struct mnttab *, const char *). */
144 # define MNT_IGNORE(M) hasmntopt (M, (char *) MNTOPT_IGNORE)
145 # else
146 # define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
147 # endif
148 #else
149 # define MNT_IGNORE(M) 0
150 #endif
152 #if USE_UNLOCKED_IO
153 # include "unlocked-io.h"
154 #endif
156 /* The results of opendir() in this file are not used with dirfd and fchdir,
157 therefore save some unnecessary work in fchdir.c. */
158 #ifdef GNULIB_defined_opendir
159 # undef opendir
160 #endif
161 #ifdef GNULIB_defined_closedir
162 # undef closedir
163 #endif
165 #define ME_DUMMY_0(Fs_name, Fs_type) \
166 (strcmp (Fs_type, "autofs") == 0 \
167 || strcmp (Fs_type, "proc") == 0 \
168 || strcmp (Fs_type, "subfs") == 0 \
169 /* for Linux 2.6/3.x */ \
170 || strcmp (Fs_type, "debugfs") == 0 \
171 || strcmp (Fs_type, "devpts") == 0 \
172 || strcmp (Fs_type, "fusectl") == 0 \
173 || strcmp (Fs_type, "mqueue") == 0 \
174 || strcmp (Fs_type, "rpc_pipefs") == 0 \
175 || strcmp (Fs_type, "sysfs") == 0 \
176 /* FreeBSD, Linux 2.4 */ \
177 || strcmp (Fs_type, "devfs") == 0 \
178 /* for NetBSD 3.0 */ \
179 || strcmp (Fs_type, "kernfs") == 0 \
180 /* for Irix 6.5 */ \
181 || strcmp (Fs_type, "ignore") == 0)
183 /* Historically, we have marked as "dummy" any file system of type "none",
184 but now that programs like du need to know about bind-mounted directories,
185 we grant an exception to any with "bind" in its list of mount options.
186 I.e., those are *not* dummy entries. */
187 #ifdef MOUNTED_GETMNTENT1
188 # define ME_DUMMY(Fs_name, Fs_type, Bind) \
189 (ME_DUMMY_0 (Fs_name, Fs_type) \
190 || (strcmp (Fs_type, "none") == 0 && !Bind))
191 #else
192 # define ME_DUMMY(Fs_name, Fs_type) \
193 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
194 #endif
196 #ifdef __CYGWIN__
197 # include <windows.h>
198 # define ME_REMOTE me_remote
199 /* All cygwin mount points include ':' or start with '//'; so it
200 requires a native Windows call to determine remote disks. */
201 static bool
202 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
204 if (fs_name[0] && fs_name[1] == ':')
206 char drive[4];
207 sprintf (drive, "%c:\\", fs_name[0]);
208 switch (GetDriveType (drive))
210 case DRIVE_REMOVABLE:
211 case DRIVE_FIXED:
212 case DRIVE_CDROM:
213 case DRIVE_RAMDISK:
214 return false;
217 return true;
219 #endif
221 #ifndef ME_REMOTE
222 /* A file system is "remote" if its Fs_name contains a ':'
223 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
224 or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). */
225 # define ME_REMOTE(Fs_name, Fs_type) \
226 (strchr (Fs_name, ':') != NULL \
227 || ((Fs_name)[0] == '/' \
228 && (Fs_name)[1] == '/' \
229 && (strcmp (Fs_type, "smbfs") == 0 \
230 || strcmp (Fs_type, "cifs") == 0)) \
231 || (strcmp("-hosts", Fs_name) == 0))
232 #endif
234 #if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
236 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
237 static char *
238 fstype_to_string (short int t)
240 switch (t)
242 # ifdef MOUNT_PC
243 case MOUNT_PC:
244 return "pc";
245 # endif
246 # ifdef MOUNT_MFS
247 case MOUNT_MFS:
248 return "mfs";
249 # endif
250 # ifdef MOUNT_LO
251 case MOUNT_LO:
252 return "lo";
253 # endif
254 # ifdef MOUNT_TFS
255 case MOUNT_TFS:
256 return "tfs";
257 # endif
258 # ifdef MOUNT_TMP
259 case MOUNT_TMP:
260 return "tmp";
261 # endif
262 # ifdef MOUNT_UFS
263 case MOUNT_UFS:
264 return "ufs" ;
265 # endif
266 # ifdef MOUNT_NFS
267 case MOUNT_NFS:
268 return "nfs" ;
269 # endif
270 # ifdef MOUNT_MSDOS
271 case MOUNT_MSDOS:
272 return "msdos" ;
273 # endif
274 # ifdef MOUNT_LFS
275 case MOUNT_LFS:
276 return "lfs" ;
277 # endif
278 # ifdef MOUNT_LOFS
279 case MOUNT_LOFS:
280 return "lofs" ;
281 # endif
282 # ifdef MOUNT_FDESC
283 case MOUNT_FDESC:
284 return "fdesc" ;
285 # endif
286 # ifdef MOUNT_PORTAL
287 case MOUNT_PORTAL:
288 return "portal" ;
289 # endif
290 # ifdef MOUNT_NULL
291 case MOUNT_NULL:
292 return "null" ;
293 # endif
294 # ifdef MOUNT_UMAP
295 case MOUNT_UMAP:
296 return "umap" ;
297 # endif
298 # ifdef MOUNT_KERNFS
299 case MOUNT_KERNFS:
300 return "kernfs" ;
301 # endif
302 # ifdef MOUNT_PROCFS
303 case MOUNT_PROCFS:
304 return "procfs" ;
305 # endif
306 # ifdef MOUNT_AFS
307 case MOUNT_AFS:
308 return "afs" ;
309 # endif
310 # ifdef MOUNT_CD9660
311 case MOUNT_CD9660:
312 return "cd9660" ;
313 # endif
314 # ifdef MOUNT_UNION
315 case MOUNT_UNION:
316 return "union" ;
317 # endif
318 # ifdef MOUNT_DEVFS
319 case MOUNT_DEVFS:
320 return "devfs" ;
321 # endif
322 # ifdef MOUNT_EXT2FS
323 case MOUNT_EXT2FS:
324 return "ext2fs" ;
325 # endif
326 default:
327 return "?";
330 # endif
332 static char *
333 fsp_to_string (const struct statfs *fsp)
335 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
336 return (char *) (fsp->f_fstypename);
337 # else
338 return fstype_to_string (fsp->f_type);
339 # endif
342 #endif /* MOUNTED_GETMNTINFO */
344 #ifdef MOUNTED_VMOUNT /* AIX */
345 static char *
346 fstype_to_string (int t)
348 struct vfs_ent *e;
350 e = getvfsbytype (t);
351 if (!e || !e->vfsent_name)
352 return "none";
353 else
354 return e->vfsent_name;
356 #endif /* MOUNTED_VMOUNT */
359 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
361 /* Return the device number from MOUNT_OPTIONS, if possible.
362 Otherwise return (dev_t) -1. */
363 static dev_t
364 dev_from_mount_options (char const *mount_options)
366 /* GNU/Linux allows file system implementations to define their own
367 meaning for "dev=" mount options, so don't trust the meaning
368 here. */
369 # ifndef __linux__
371 static char const dev_pattern[] = ",dev=";
372 char const *devopt = strstr (mount_options, dev_pattern);
374 if (devopt)
376 char const *optval = devopt + sizeof dev_pattern - 1;
377 char *optvalend;
378 unsigned long int dev;
379 errno = 0;
380 dev = strtoul (optval, &optvalend, 16);
381 if (optval != optvalend
382 && (*optvalend == '\0' || *optvalend == ',')
383 && ! (dev == ULONG_MAX && errno == ERANGE)
384 && dev == (dev_t) dev)
385 return dev;
388 # endif
389 (void) mount_options;
390 return -1;
393 #endif
395 #if defined MOUNTED_GETMNTENT1 && (defined __linux__ || defined __ANDROID__) /* GNU/Linux, Android */
397 /* Unescape the paths in mount tables.
398 STR is updated in place. */
400 static void
401 unescape_tab (char *str)
403 size_t i, j = 0;
404 size_t len = strlen (str) + 1;
405 for (i = 0; i < len; i++)
407 if (str[i] == '\\' && (i + 4 < len)
408 && str[i + 1] >= '0' && str[i + 1] <= '3'
409 && str[i + 2] >= '0' && str[i + 2] <= '7'
410 && str[i + 3] >= '0' && str[i + 3] <= '7')
412 str[j++] = (str[i + 1] - '0') * 64 +
413 (str[i + 2] - '0') * 8 +
414 (str[i + 3] - '0');
415 i += 3;
417 else
418 str[j++] = str[i];
422 /* Find the next space in STR, terminate the string there in place,
423 and return that position. Otherwise return NULL. */
425 static char *
426 terminate_at_blank (char *str)
428 char *s = strchr (str, ' ');
429 if (s)
430 *s = '\0';
431 return s;
433 #endif
435 /* Return a list of the currently mounted file systems, or NULL on error.
436 Add each entry to the tail of the list so that they stay in order.
437 If NEED_FS_TYPE is true, ensure that the file system type fields in
438 the returned list are valid. Otherwise, they might not be. */
440 struct mount_entry *
441 read_file_system_list (bool need_fs_type)
443 struct mount_entry *mount_list;
444 struct mount_entry *me;
445 struct mount_entry **mtail = &mount_list;
446 (void) need_fs_type;
448 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
449 also (obsolete) 4.3BSD, SunOS */
451 FILE *fp;
453 # if defined __linux__ || defined __ANDROID__
454 /* Try parsing mountinfo first, as that make device IDs available.
455 Note we could use libmount routines to simplify this parsing a little
456 (and that code is in previous versions of this function), however
457 libmount depends on libselinux which pulls in many dependencies. */
458 char const *mountinfo = "/proc/self/mountinfo";
459 fp = fopen (mountinfo, "r");
460 if (fp != NULL)
462 char *line = NULL;
463 size_t buf_size = 0;
465 while (getline (&line, &buf_size, fp) != -1)
467 unsigned int devmaj, devmin;
468 int rc, mntroot_s;
470 rc = sscanf(line, "%*u " /* id - discarded */
471 "%*u " /* parent - discarded */
472 "%u:%u " /* dev major:minor */
473 "%n", /* mountroot (start) */
474 &devmaj, &devmin,
475 &mntroot_s);
477 if (rc != 2 && rc != 3) /* 3 if %n included in count. */
478 continue;
480 /* find end of MNTROOT. */
481 char *mntroot = line + mntroot_s;
482 char *blank = terminate_at_blank (mntroot);
483 if (! blank)
484 continue;
486 /* find end of TARGET. */
487 char *target = blank + 1;
488 blank = terminate_at_blank (target);
489 if (! blank)
490 continue;
492 /* skip optional fields, terminated by " - " */
493 char *dash = strstr (blank + 1, " - ");
494 if (! dash)
495 continue;
497 /* advance past the " - " separator. */
498 char *fstype = dash + 3;
499 blank = terminate_at_blank (fstype);
500 if (! blank)
501 continue;
503 /* find end of SOURCE. */
504 char *source = blank + 1;
505 if (! terminate_at_blank (source))
506 continue;
508 /* manipulate the sub-strings in place. */
509 unescape_tab (source);
510 unescape_tab (target);
511 unescape_tab (mntroot);
512 unescape_tab (fstype);
514 me = xmalloc (sizeof *me);
516 me->me_devname = xstrdup (source);
517 me->me_mountdir = xstrdup (target);
518 me->me_mntroot = xstrdup (mntroot);
519 me->me_type = xstrdup (fstype);
520 me->me_type_malloced = 1;
521 me->me_dev = makedev (devmaj, devmin);
522 /* we pass "false" for the "Bind" option as that's only
523 significant when the Fs_type is "none" which will not be
524 the case when parsing "/proc/self/mountinfo", and only
525 applies for static /etc/mtab files. */
526 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
527 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
529 /* Add to the linked list. */
530 *mtail = me;
531 mtail = &me->me_next;
534 free (line);
536 if (ferror (fp))
538 int saved_errno = errno;
539 fclose (fp);
540 errno = saved_errno;
541 goto free_then_fail;
544 if (fclose (fp) == EOF)
545 goto free_then_fail;
547 else /* fallback to /proc/self/mounts (/etc/mtab). */
548 # endif /* __linux __ || __ANDROID__ */
550 struct mntent *mnt;
551 char const *table = MOUNTED;
553 fp = setmntent (table, "r");
554 if (fp == NULL)
555 return NULL;
557 while ((mnt = getmntent (fp)))
559 bool bind = hasmntopt (mnt, "bind");
561 me = xmalloc (sizeof *me);
562 me->me_devname = xstrdup (mnt->mnt_fsname);
563 me->me_mountdir = xstrdup (mnt->mnt_dir);
564 me->me_mntroot = NULL;
565 me->me_type = xstrdup (mnt->mnt_type);
566 me->me_type_malloced = 1;
567 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
568 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
569 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
571 /* Add to the linked list. */
572 *mtail = me;
573 mtail = &me->me_next;
576 if (endmntent (fp) == 0)
577 goto free_then_fail;
580 #endif /* MOUNTED_GETMNTENT1. */
582 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
584 struct statfs *fsp;
585 int entries;
587 entries = getmntinfo (&fsp, MNT_NOWAIT);
588 if (entries < 0)
589 return NULL;
590 for (; entries-- > 0; fsp++)
592 char *fs_type = fsp_to_string (fsp);
594 me = xmalloc (sizeof *me);
595 me->me_devname = xstrdup (fsp->f_mntfromname);
596 me->me_mountdir = xstrdup (fsp->f_mntonname);
597 me->me_mntroot = NULL;
598 me->me_type = fs_type;
599 me->me_type_malloced = 0;
600 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
601 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
602 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
604 /* Add to the linked list. */
605 *mtail = me;
606 mtail = &me->me_next;
609 #endif /* MOUNTED_GETMNTINFO */
611 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
613 struct statvfs *fsp;
614 int entries;
616 entries = getmntinfo (&fsp, MNT_NOWAIT);
617 if (entries < 0)
618 return NULL;
619 for (; entries-- > 0; fsp++)
621 me = xmalloc (sizeof *me);
622 me->me_devname = xstrdup (fsp->f_mntfromname);
623 me->me_mountdir = xstrdup (fsp->f_mntonname);
624 me->me_mntroot = NULL;
625 me->me_type = xstrdup (fsp->f_fstypename);
626 me->me_type_malloced = 1;
627 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
628 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
629 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
631 /* Add to the linked list. */
632 *mtail = me;
633 mtail = &me->me_next;
636 #endif /* MOUNTED_GETMNTINFO2 */
638 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
640 /* The next_dev() and fs_stat_dev() system calls give the list of
641 all file systems, including the information returned by statvfs()
642 (fs type, total blocks, free blocks etc.), but without the mount
643 point. But on BeOS all file systems except / are mounted in the
644 rootfs, directly under /.
645 The directory name of the mount point is often, but not always,
646 identical to the volume name of the device.
647 We therefore get the list of subdirectories of /, and the list
648 of all file systems, and match the two lists. */
650 DIR *dirp;
651 struct rootdir_entry
653 char *name;
654 dev_t dev;
655 ino_t ino;
656 struct rootdir_entry *next;
658 struct rootdir_entry *rootdir_list;
659 struct rootdir_entry **rootdir_tail;
660 int32 pos;
661 dev_t dev;
662 fs_info fi;
664 /* All volumes are mounted in the rootfs, directly under /. */
665 rootdir_list = NULL;
666 rootdir_tail = &rootdir_list;
667 dirp = opendir ("/");
668 if (dirp)
670 struct dirent *d;
672 while ((d = readdir (dirp)) != NULL)
674 char *name;
675 struct stat statbuf;
677 if (strcmp (d->d_name, "..") == 0)
678 continue;
680 if (strcmp (d->d_name, ".") == 0)
681 name = xstrdup ("/");
682 else
684 name = xmalloc (1 + strlen (d->d_name) + 1);
685 name[0] = '/';
686 strcpy (name + 1, d->d_name);
689 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
691 struct rootdir_entry *re = xmalloc (sizeof *re);
692 re->name = name;
693 re->dev = statbuf.st_dev;
694 re->ino = statbuf.st_ino;
696 /* Add to the linked list. */
697 *rootdir_tail = re;
698 rootdir_tail = &re->next;
700 else
701 free (name);
703 closedir (dirp);
705 *rootdir_tail = NULL;
707 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
708 if (fs_stat_dev (dev, &fi) >= 0)
710 /* Note: fi.dev == dev. */
711 struct rootdir_entry *re;
713 for (re = rootdir_list; re; re = re->next)
714 if (re->dev == fi.dev && re->ino == fi.root)
715 break;
717 me = xmalloc (sizeof *me);
718 me->me_devname = xstrdup (fi.device_name[0] != '\0'
719 ? fi.device_name : fi.fsh_name);
720 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
721 me->me_mntroot = NULL;
722 me->me_type = xstrdup (fi.fsh_name);
723 me->me_type_malloced = 1;
724 me->me_dev = fi.dev;
725 me->me_dummy = 0;
726 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
728 /* Add to the linked list. */
729 *mtail = me;
730 mtail = &me->me_next;
732 *mtail = NULL;
734 while (rootdir_list != NULL)
736 struct rootdir_entry *re = rootdir_list;
737 rootdir_list = re->next;
738 free (re->name);
739 free (re);
742 #endif /* MOUNTED_FS_STAT_DEV */
744 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
746 int numsys, counter;
747 size_t bufsize;
748 struct statfs *stats;
750 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
751 if (numsys < 0)
752 return NULL;
753 if (SIZE_MAX / sizeof *stats <= numsys)
754 xalloc_die ();
756 bufsize = (1 + numsys) * sizeof *stats;
757 stats = xmalloc (bufsize);
758 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
760 if (numsys < 0)
762 free (stats);
763 return NULL;
766 for (counter = 0; counter < numsys; counter++)
768 me = xmalloc (sizeof *me);
769 me->me_devname = xstrdup (stats[counter].f_mntfromname);
770 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
771 me->me_mntroot = NULL;
772 me->me_type = xstrdup (FS_TYPE (stats[counter]));
773 me->me_type_malloced = 1;
774 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
775 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
776 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
778 /* Add to the linked list. */
779 *mtail = me;
780 mtail = &me->me_next;
783 free (stats);
785 #endif /* MOUNTED_GETFSSTAT */
787 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
789 struct mnttab mnt;
790 char *table = "/etc/mnttab";
791 FILE *fp;
793 fp = fopen (table, "r");
794 if (fp == NULL)
795 return NULL;
797 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
799 me = xmalloc (sizeof *me);
800 me->me_devname = xstrdup (mnt.mt_dev);
801 me->me_mountdir = xstrdup (mnt.mt_filsys);
802 me->me_mntroot = NULL;
803 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
804 me->me_type = "";
805 me->me_type_malloced = 0;
806 if (need_fs_type)
808 struct statfs fsd;
809 char typebuf[FSTYPSZ];
811 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
812 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
814 me->me_type = xstrdup (typebuf);
815 me->me_type_malloced = 1;
818 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
819 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
821 /* Add to the linked list. */
822 *mtail = me;
823 mtail = &me->me_next;
826 if (ferror (fp))
828 /* The last fread() call must have failed. */
829 int saved_errno = errno;
830 fclose (fp);
831 errno = saved_errno;
832 goto free_then_fail;
835 if (fclose (fp) == EOF)
836 goto free_then_fail;
838 #endif /* MOUNTED_FREAD_FSTYP. */
840 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
842 struct extmnttab mnt;
843 const char *table = MNTTAB;
844 FILE *fp;
845 int ret;
847 /* No locking is needed, because the contents of /etc/mnttab is generated
848 by the kernel. */
850 errno = 0;
851 fp = fopen (table, "r");
852 if (fp == NULL)
853 ret = errno;
854 else
856 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
858 me = xmalloc (sizeof *me);
859 me->me_devname = xstrdup (mnt.mnt_special);
860 me->me_mountdir = xstrdup (mnt.mnt_mountp);
861 me->me_mntroot = NULL;
862 me->me_type = xstrdup (mnt.mnt_fstype);
863 me->me_type_malloced = 1;
864 me->me_dummy = MNT_IGNORE (&mnt) != 0;
865 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
866 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
868 /* Add to the linked list. */
869 *mtail = me;
870 mtail = &me->me_next;
873 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
874 /* Here ret = -1 means success, ret >= 0 means failure. */
877 if (0 <= ret)
879 errno = ret;
880 goto free_then_fail;
883 #endif /* MOUNTED_GETEXTMNTENT */
885 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
887 struct mnttab mnt;
888 const char *table = MNTTAB;
889 FILE *fp;
890 int ret;
891 int lockfd = -1;
893 # if defined F_RDLCK && defined F_SETLKW
894 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
895 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
896 for this file name, we should use their macro name instead.
897 (Why not just lock MNTTAB directly? We don't know.) */
898 # ifndef MNTTAB_LOCK
899 # define MNTTAB_LOCK "/etc/.mnttab.lock"
900 # endif
901 lockfd = open (MNTTAB_LOCK, O_RDONLY);
902 if (0 <= lockfd)
904 struct flock flock;
905 flock.l_type = F_RDLCK;
906 flock.l_whence = SEEK_SET;
907 flock.l_start = 0;
908 flock.l_len = 0;
909 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
910 if (errno != EINTR)
912 int saved_errno = errno;
913 close (lockfd);
914 errno = saved_errno;
915 return NULL;
918 else if (errno != ENOENT)
919 return NULL;
920 # endif
922 errno = 0;
923 fp = fopen (table, "r");
924 if (fp == NULL)
925 ret = errno;
926 else
928 while ((ret = getmntent (fp, &mnt)) == 0)
930 me = xmalloc (sizeof *me);
931 me->me_devname = xstrdup (mnt.mnt_special);
932 me->me_mountdir = xstrdup (mnt.mnt_mountp);
933 me->me_mntroot = NULL;
934 me->me_type = xstrdup (mnt.mnt_fstype);
935 me->me_type_malloced = 1;
936 me->me_dummy = MNT_IGNORE (&mnt) != 0;
937 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
938 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
940 /* Add to the linked list. */
941 *mtail = me;
942 mtail = &me->me_next;
945 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
946 /* Here ret = -1 means success, ret >= 0 means failure. */
949 if (0 <= lockfd && close (lockfd) != 0)
950 ret = errno;
952 if (0 <= ret)
954 errno = ret;
955 goto free_then_fail;
958 #endif /* MOUNTED_GETMNTENT2. */
960 #ifdef MOUNTED_VMOUNT /* AIX */
962 int bufsize;
963 void *entries;
964 char *thisent;
965 struct vmount *vmp;
966 int n_entries;
967 int i;
969 /* Ask how many bytes to allocate for the mounted file system info. */
970 entries = &bufsize;
971 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
972 return NULL;
973 entries = xmalloc (bufsize);
975 /* Get the list of mounted file systems. */
976 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
977 if (n_entries < 0)
979 int saved_errno = errno;
980 free (entries);
981 errno = saved_errno;
982 return NULL;
985 for (i = 0, thisent = entries;
986 i < n_entries;
987 i++, thisent += vmp->vmt_length)
989 char *options, *ignore;
991 vmp = (struct vmount *) thisent;
992 me = xmalloc (sizeof *me);
993 if (vmp->vmt_flags & MNT_REMOTE)
995 char *host, *dir;
997 me->me_remote = 1;
998 /* Prepend the remote dirname. */
999 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1000 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1001 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
1002 strcpy (me->me_devname, host);
1003 strcat (me->me_devname, ":");
1004 strcat (me->me_devname, dir);
1006 else
1008 me->me_remote = 0;
1009 me->me_devname = xstrdup (thisent +
1010 vmp->vmt_data[VMT_OBJECT].vmt_off);
1012 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1013 me->me_mntroot = NULL;
1014 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1015 me->me_type_malloced = 1;
1016 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1017 ignore = strstr (options, "ignore");
1018 me->me_dummy = (ignore
1019 && (ignore == options || ignore[-1] == ',')
1020 && (ignore[sizeof "ignore" - 1] == ','
1021 || ignore[sizeof "ignore" - 1] == '\0'));
1022 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
1024 /* Add to the linked list. */
1025 *mtail = me;
1026 mtail = &me->me_next;
1028 free (entries);
1030 #endif /* MOUNTED_VMOUNT. */
1032 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1034 DIR *dirp = opendir ("/dev/fs");
1035 char node[9 + NAME_MAX];
1037 if (!dirp)
1038 goto free_then_fail;
1040 while (1)
1042 struct statvfs dev;
1043 struct dirent entry;
1044 struct dirent *result;
1046 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1047 marked obsolescent in glibc. Use readdir instead. */
1048 if (readdir_r (dirp, &entry, &result) || result == NULL)
1049 break;
1051 strcpy (node, "/dev/fs/");
1052 strcat (node, entry.d_name);
1054 if (statvfs (node, &dev) == 0)
1056 me = xmalloc (sizeof *me);
1057 me->me_devname = xstrdup (dev.f_mntfromname);
1058 me->me_mountdir = xstrdup (dev.f_mntonname);
1059 me->me_mntroot = NULL;
1060 me->me_type = xstrdup (dev.f_fstypename);
1061 me->me_type_malloced = 1;
1062 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1063 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1064 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
1066 /* Add to the linked list. */
1067 *mtail = me;
1068 mtail = &me->me_next;
1071 closedir (dirp);
1073 #endif /* MOUNTED_INTERIX_STATVFS */
1075 *mtail = NULL;
1076 return mount_list;
1079 free_then_fail: _GL_UNUSED_LABEL
1081 int saved_errno = errno;
1082 *mtail = NULL;
1084 while (mount_list)
1086 me = mount_list->me_next;
1087 free_mount_entry (mount_list);
1088 mount_list = me;
1091 errno = saved_errno;
1092 return NULL;
1096 /* Free a mount entry as returned from read_file_system_list (). */
1098 void free_mount_entry (struct mount_entry *me)
1100 free (me->me_devname);
1101 free (me->me_mountdir);
1102 free (me->me_mntroot);
1103 if (me->me_type_malloced)
1104 free (me->me_type);
1105 free (me);