mountlist: Use Linux code on Android.
[gnulib.git] / lib / mountlist.c
blob9b54a2cf70c499d187f14be3a5f4ca1919753c66
1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2019 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];
421 #endif
423 /* Return a list of the currently mounted file systems, or NULL on error.
424 Add each entry to the tail of the list so that they stay in order.
425 If NEED_FS_TYPE is true, ensure that the file system type fields in
426 the returned list are valid. Otherwise, they might not be. */
428 struct mount_entry *
429 read_file_system_list (bool need_fs_type)
431 struct mount_entry *mount_list;
432 struct mount_entry *me;
433 struct mount_entry **mtail = &mount_list;
434 (void) need_fs_type;
436 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
437 also (obsolete) 4.3BSD, SunOS */
439 FILE *fp;
441 # if defined __linux__ || defined __ANDROID__
442 /* Try parsing mountinfo first, as that make device IDs available.
443 Note we could use libmount routines to simplify this parsing a little
444 (and that code is in previous versions of this function), however
445 libmount depends on libselinux which pulls in many dependencies. */
446 char const *mountinfo = "/proc/self/mountinfo";
447 fp = fopen (mountinfo, "r");
448 if (fp != NULL)
450 char *line = NULL;
451 size_t buf_size = 0;
453 while (getline (&line, &buf_size, fp) != -1)
455 unsigned int devmaj, devmin;
456 int target_s, target_e, type_s, type_e;
457 int source_s, source_e, mntroot_s, mntroot_e;
458 char test;
459 char *dash;
460 int rc;
462 rc = sscanf(line, "%*u " /* id - discarded */
463 "%*u " /* parent - discarded */
464 "%u:%u " /* dev major:minor */
465 "%n%*s%n " /* mountroot */
466 "%n%*s%n" /* target, start and end */
467 "%c", /* more data... */
468 &devmaj, &devmin,
469 &mntroot_s, &mntroot_e,
470 &target_s, &target_e,
471 &test);
473 if (rc != 3 && rc != 7) /* 7 if %n included in count. */
474 continue;
476 /* skip optional fields, terminated by " - " */
477 dash = strstr (line + target_e, " - ");
478 if (! dash)
479 continue;
481 rc = sscanf(dash, " - "
482 "%n%*s%n " /* FS type, start and end */
483 "%n%*s%n " /* source, start and end */
484 "%c", /* more data... */
485 &type_s, &type_e,
486 &source_s, &source_e,
487 &test);
488 if (rc != 1 && rc != 5) /* 5 if %n included in count. */
489 continue;
491 /* manipulate the sub-strings in place. */
492 line[mntroot_e] = '\0';
493 line[target_e] = '\0';
494 dash[type_e] = '\0';
495 dash[source_e] = '\0';
496 unescape_tab (dash + source_s);
497 unescape_tab (line + target_s);
498 unescape_tab (line + mntroot_s);
500 me = xmalloc (sizeof *me);
502 me->me_devname = xstrdup (dash + source_s);
503 me->me_mountdir = xstrdup (line + target_s);
504 me->me_mntroot = xstrdup (line + mntroot_s);
505 me->me_type = xstrdup (dash + type_s);
506 me->me_type_malloced = 1;
507 me->me_dev = makedev (devmaj, devmin);
508 /* we pass "false" for the "Bind" option as that's only
509 significant when the Fs_type is "none" which will not be
510 the case when parsing "/proc/self/mountinfo", and only
511 applies for static /etc/mtab files. */
512 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
513 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
515 /* Add to the linked list. */
516 *mtail = me;
517 mtail = &me->me_next;
520 free (line);
522 if (ferror (fp))
524 int saved_errno = errno;
525 fclose (fp);
526 errno = saved_errno;
527 goto free_then_fail;
530 if (fclose (fp) == EOF)
531 goto free_then_fail;
533 else /* fallback to /proc/self/mounts (/etc/mtab). */
534 # endif /* __linux __ || __ANDROID__ */
536 struct mntent *mnt;
537 char const *table = MOUNTED;
539 fp = setmntent (table, "r");
540 if (fp == NULL)
541 return NULL;
543 while ((mnt = getmntent (fp)))
545 bool bind = hasmntopt (mnt, "bind");
547 me = xmalloc (sizeof *me);
548 me->me_devname = xstrdup (mnt->mnt_fsname);
549 me->me_mountdir = xstrdup (mnt->mnt_dir);
550 me->me_mntroot = NULL;
551 me->me_type = xstrdup (mnt->mnt_type);
552 me->me_type_malloced = 1;
553 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
554 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
555 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
557 /* Add to the linked list. */
558 *mtail = me;
559 mtail = &me->me_next;
562 if (endmntent (fp) == 0)
563 goto free_then_fail;
566 #endif /* MOUNTED_GETMNTENT1. */
568 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
570 struct statfs *fsp;
571 int entries;
573 entries = getmntinfo (&fsp, MNT_NOWAIT);
574 if (entries < 0)
575 return NULL;
576 for (; entries-- > 0; fsp++)
578 char *fs_type = fsp_to_string (fsp);
580 me = xmalloc (sizeof *me);
581 me->me_devname = xstrdup (fsp->f_mntfromname);
582 me->me_mountdir = xstrdup (fsp->f_mntonname);
583 me->me_mntroot = NULL;
584 me->me_type = fs_type;
585 me->me_type_malloced = 0;
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 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
590 /* Add to the linked list. */
591 *mtail = me;
592 mtail = &me->me_next;
595 #endif /* MOUNTED_GETMNTINFO */
597 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
599 struct statvfs *fsp;
600 int entries;
602 entries = getmntinfo (&fsp, MNT_NOWAIT);
603 if (entries < 0)
604 return NULL;
605 for (; entries-- > 0; fsp++)
607 me = xmalloc (sizeof *me);
608 me->me_devname = xstrdup (fsp->f_mntfromname);
609 me->me_mountdir = xstrdup (fsp->f_mntonname);
610 me->me_mntroot = NULL;
611 me->me_type = xstrdup (fsp->f_fstypename);
612 me->me_type_malloced = 1;
613 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
614 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
615 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
617 /* Add to the linked list. */
618 *mtail = me;
619 mtail = &me->me_next;
622 #endif /* MOUNTED_GETMNTINFO2 */
624 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
626 /* The next_dev() and fs_stat_dev() system calls give the list of
627 all file systems, including the information returned by statvfs()
628 (fs type, total blocks, free blocks etc.), but without the mount
629 point. But on BeOS all file systems except / are mounted in the
630 rootfs, directly under /.
631 The directory name of the mount point is often, but not always,
632 identical to the volume name of the device.
633 We therefore get the list of subdirectories of /, and the list
634 of all file systems, and match the two lists. */
636 DIR *dirp;
637 struct rootdir_entry
639 char *name;
640 dev_t dev;
641 ino_t ino;
642 struct rootdir_entry *next;
644 struct rootdir_entry *rootdir_list;
645 struct rootdir_entry **rootdir_tail;
646 int32 pos;
647 dev_t dev;
648 fs_info fi;
650 /* All volumes are mounted in the rootfs, directly under /. */
651 rootdir_list = NULL;
652 rootdir_tail = &rootdir_list;
653 dirp = opendir ("/");
654 if (dirp)
656 struct dirent *d;
658 while ((d = readdir (dirp)) != NULL)
660 char *name;
661 struct stat statbuf;
663 if (strcmp (d->d_name, "..") == 0)
664 continue;
666 if (strcmp (d->d_name, ".") == 0)
667 name = xstrdup ("/");
668 else
670 name = xmalloc (1 + strlen (d->d_name) + 1);
671 name[0] = '/';
672 strcpy (name + 1, d->d_name);
675 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
677 struct rootdir_entry *re = xmalloc (sizeof *re);
678 re->name = name;
679 re->dev = statbuf.st_dev;
680 re->ino = statbuf.st_ino;
682 /* Add to the linked list. */
683 *rootdir_tail = re;
684 rootdir_tail = &re->next;
686 else
687 free (name);
689 closedir (dirp);
691 *rootdir_tail = NULL;
693 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
694 if (fs_stat_dev (dev, &fi) >= 0)
696 /* Note: fi.dev == dev. */
697 struct rootdir_entry *re;
699 for (re = rootdir_list; re; re = re->next)
700 if (re->dev == fi.dev && re->ino == fi.root)
701 break;
703 me = xmalloc (sizeof *me);
704 me->me_devname = xstrdup (fi.device_name[0] != '\0'
705 ? fi.device_name : fi.fsh_name);
706 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
707 me->me_mntroot = NULL;
708 me->me_type = xstrdup (fi.fsh_name);
709 me->me_type_malloced = 1;
710 me->me_dev = fi.dev;
711 me->me_dummy = 0;
712 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
714 /* Add to the linked list. */
715 *mtail = me;
716 mtail = &me->me_next;
718 *mtail = NULL;
720 while (rootdir_list != NULL)
722 struct rootdir_entry *re = rootdir_list;
723 rootdir_list = re->next;
724 free (re->name);
725 free (re);
728 #endif /* MOUNTED_FS_STAT_DEV */
730 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
732 int numsys, counter;
733 size_t bufsize;
734 struct statfs *stats;
736 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
737 if (numsys < 0)
738 return NULL;
739 if (SIZE_MAX / sizeof *stats <= numsys)
740 xalloc_die ();
742 bufsize = (1 + numsys) * sizeof *stats;
743 stats = xmalloc (bufsize);
744 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
746 if (numsys < 0)
748 free (stats);
749 return NULL;
752 for (counter = 0; counter < numsys; counter++)
754 me = xmalloc (sizeof *me);
755 me->me_devname = xstrdup (stats[counter].f_mntfromname);
756 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
757 me->me_mntroot = NULL;
758 me->me_type = xstrdup (FS_TYPE (stats[counter]));
759 me->me_type_malloced = 1;
760 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
761 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
762 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
764 /* Add to the linked list. */
765 *mtail = me;
766 mtail = &me->me_next;
769 free (stats);
771 #endif /* MOUNTED_GETFSSTAT */
773 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
775 struct mnttab mnt;
776 char *table = "/etc/mnttab";
777 FILE *fp;
779 fp = fopen (table, "r");
780 if (fp == NULL)
781 return NULL;
783 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
785 me = xmalloc (sizeof *me);
786 me->me_devname = xstrdup (mnt.mt_dev);
787 me->me_mountdir = xstrdup (mnt.mt_filsys);
788 me->me_mntroot = NULL;
789 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
790 me->me_type = "";
791 me->me_type_malloced = 0;
792 if (need_fs_type)
794 struct statfs fsd;
795 char typebuf[FSTYPSZ];
797 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
798 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
800 me->me_type = xstrdup (typebuf);
801 me->me_type_malloced = 1;
804 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
805 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
807 /* Add to the linked list. */
808 *mtail = me;
809 mtail = &me->me_next;
812 if (ferror (fp))
814 /* The last fread() call must have failed. */
815 int saved_errno = errno;
816 fclose (fp);
817 errno = saved_errno;
818 goto free_then_fail;
821 if (fclose (fp) == EOF)
822 goto free_then_fail;
824 #endif /* MOUNTED_FREAD_FSTYP. */
826 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
828 struct extmnttab mnt;
829 const char *table = MNTTAB;
830 FILE *fp;
831 int ret;
833 /* No locking is needed, because the contents of /etc/mnttab is generated
834 by the kernel. */
836 errno = 0;
837 fp = fopen (table, "r");
838 if (fp == NULL)
839 ret = errno;
840 else
842 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
844 me = xmalloc (sizeof *me);
845 me->me_devname = xstrdup (mnt.mnt_special);
846 me->me_mountdir = xstrdup (mnt.mnt_mountp);
847 me->me_mntroot = NULL;
848 me->me_type = xstrdup (mnt.mnt_fstype);
849 me->me_type_malloced = 1;
850 me->me_dummy = MNT_IGNORE (&mnt) != 0;
851 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
852 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
854 /* Add to the linked list. */
855 *mtail = me;
856 mtail = &me->me_next;
859 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
860 /* Here ret = -1 means success, ret >= 0 means failure. */
863 if (0 <= ret)
865 errno = ret;
866 goto free_then_fail;
869 #endif /* MOUNTED_GETEXTMNTENT */
871 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
873 struct mnttab mnt;
874 const char *table = MNTTAB;
875 FILE *fp;
876 int ret;
877 int lockfd = -1;
879 # if defined F_RDLCK && defined F_SETLKW
880 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
881 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
882 for this file name, we should use their macro name instead.
883 (Why not just lock MNTTAB directly? We don't know.) */
884 # ifndef MNTTAB_LOCK
885 # define MNTTAB_LOCK "/etc/.mnttab.lock"
886 # endif
887 lockfd = open (MNTTAB_LOCK, O_RDONLY);
888 if (0 <= lockfd)
890 struct flock flock;
891 flock.l_type = F_RDLCK;
892 flock.l_whence = SEEK_SET;
893 flock.l_start = 0;
894 flock.l_len = 0;
895 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
896 if (errno != EINTR)
898 int saved_errno = errno;
899 close (lockfd);
900 errno = saved_errno;
901 return NULL;
904 else if (errno != ENOENT)
905 return NULL;
906 # endif
908 errno = 0;
909 fp = fopen (table, "r");
910 if (fp == NULL)
911 ret = errno;
912 else
914 while ((ret = getmntent (fp, &mnt)) == 0)
916 me = xmalloc (sizeof *me);
917 me->me_devname = xstrdup (mnt.mnt_special);
918 me->me_mountdir = xstrdup (mnt.mnt_mountp);
919 me->me_mntroot = NULL;
920 me->me_type = xstrdup (mnt.mnt_fstype);
921 me->me_type_malloced = 1;
922 me->me_dummy = MNT_IGNORE (&mnt) != 0;
923 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
924 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
926 /* Add to the linked list. */
927 *mtail = me;
928 mtail = &me->me_next;
931 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
932 /* Here ret = -1 means success, ret >= 0 means failure. */
935 if (0 <= lockfd && close (lockfd) != 0)
936 ret = errno;
938 if (0 <= ret)
940 errno = ret;
941 goto free_then_fail;
944 #endif /* MOUNTED_GETMNTENT2. */
946 #ifdef MOUNTED_VMOUNT /* AIX */
948 int bufsize;
949 void *entries;
950 char *thisent;
951 struct vmount *vmp;
952 int n_entries;
953 int i;
955 /* Ask how many bytes to allocate for the mounted file system info. */
956 entries = &bufsize;
957 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
958 return NULL;
959 entries = xmalloc (bufsize);
961 /* Get the list of mounted file systems. */
962 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
963 if (n_entries < 0)
965 int saved_errno = errno;
966 free (entries);
967 errno = saved_errno;
968 return NULL;
971 for (i = 0, thisent = entries;
972 i < n_entries;
973 i++, thisent += vmp->vmt_length)
975 char *options, *ignore;
977 vmp = (struct vmount *) thisent;
978 me = xmalloc (sizeof *me);
979 if (vmp->vmt_flags & MNT_REMOTE)
981 char *host, *dir;
983 me->me_remote = 1;
984 /* Prepend the remote dirname. */
985 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
986 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
987 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
988 strcpy (me->me_devname, host);
989 strcat (me->me_devname, ":");
990 strcat (me->me_devname, dir);
992 else
994 me->me_remote = 0;
995 me->me_devname = xstrdup (thisent +
996 vmp->vmt_data[VMT_OBJECT].vmt_off);
998 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
999 me->me_mntroot = NULL;
1000 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1001 me->me_type_malloced = 1;
1002 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1003 ignore = strstr (options, "ignore");
1004 me->me_dummy = (ignore
1005 && (ignore == options || ignore[-1] == ',')
1006 && (ignore[sizeof "ignore" - 1] == ','
1007 || ignore[sizeof "ignore" - 1] == '\0'));
1008 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
1010 /* Add to the linked list. */
1011 *mtail = me;
1012 mtail = &me->me_next;
1014 free (entries);
1016 #endif /* MOUNTED_VMOUNT. */
1018 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1020 DIR *dirp = opendir ("/dev/fs");
1021 char node[9 + NAME_MAX];
1023 if (!dirp)
1024 goto free_then_fail;
1026 while (1)
1028 struct statvfs dev;
1029 struct dirent entry;
1030 struct dirent *result;
1032 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1033 marked obsolescent in glibc. Use readdir instead. */
1034 if (readdir_r (dirp, &entry, &result) || result == NULL)
1035 break;
1037 strcpy (node, "/dev/fs/");
1038 strcat (node, entry.d_name);
1040 if (statvfs (node, &dev) == 0)
1042 me = xmalloc (sizeof *me);
1043 me->me_devname = xstrdup (dev.f_mntfromname);
1044 me->me_mountdir = xstrdup (dev.f_mntonname);
1045 me->me_mntroot = NULL;
1046 me->me_type = xstrdup (dev.f_fstypename);
1047 me->me_type_malloced = 1;
1048 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1049 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1050 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
1052 /* Add to the linked list. */
1053 *mtail = me;
1054 mtail = &me->me_next;
1057 closedir (dirp);
1059 #endif /* MOUNTED_INTERIX_STATVFS */
1061 *mtail = NULL;
1062 return mount_list;
1065 free_then_fail: _GL_UNUSED_LABEL
1067 int saved_errno = errno;
1068 *mtail = NULL;
1070 while (mount_list)
1072 me = mount_list->me_next;
1073 free_mount_entry (mount_list);
1074 mount_list = me;
1077 errno = saved_errno;
1078 return NULL;
1082 /* Free a mount entry as returned from read_file_system_list (). */
1084 void free_mount_entry (struct mount_entry *me)
1086 free (me->me_devname);
1087 free (me->me_mountdir);
1088 free (me->me_mntroot);
1089 if (me->me_type_malloced)
1090 free (me->me_type);
1091 free (me);