mbsnrtowcs: Work around Solaris 11.4 bug.
[gnulib.git] / lib / mountlist.c
blob845c3487622a2523ec456d9dbfd344243c8c1b90
1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2018 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, Dynix */
67 # include <mntent.h>
68 # include <sys/types.h>
69 # if !defined MOUNTED
70 # if defined _PATH_MOUNTED /* GNU libc */
71 # define MOUNTED _PATH_MOUNTED
72 # endif
73 # if defined MNT_MNTTAB /* HP-UX. */
74 # define MOUNTED MNT_MNTTAB
75 # endif
76 # if defined MNTTABNAME /* Dynix. */
77 # define MOUNTED MNTTABNAME
78 # endif
79 # endif
80 #endif
82 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
83 # include <sys/mount.h>
84 #endif
86 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
87 # include <sys/statvfs.h>
88 #endif
90 #ifdef MOUNTED_GETMNT /* (obsolete) Ultrix */
91 # include <sys/mount.h>
92 # include <sys/fs_types.h>
93 #endif
95 #ifdef MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
96 # include <fs_info.h>
97 # include <dirent.h>
98 #endif
100 #ifdef MOUNTED_FREAD /* (obsolete) SVR2 */
101 # include <mnttab.h>
102 #endif
104 #ifdef MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
105 # include <mnttab.h>
106 # include <sys/fstyp.h>
107 # include <sys/statfs.h>
108 #endif
110 #ifdef MOUNTED_LISTMNTENT /* (obsolete) Cray UNICOS 9 */
111 # include <mntent.h>
112 #endif
114 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
115 # include <sys/mnttab.h>
116 #endif
118 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
119 # include <sys/mnttab.h>
120 #endif
122 #ifdef MOUNTED_VMOUNT /* AIX */
123 # include <fshelp.h>
124 # include <sys/vfs.h>
125 #endif
127 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
128 # include <sys/statvfs.h>
129 # include <dirent.h>
130 #endif
132 #ifdef DOLPHIN
133 /* So special that it's not worth putting this in autoconf. */
134 # undef MOUNTED_FREAD_FSTYP
135 # define MOUNTED_GETMNTTBL
136 #endif
138 #if HAVE_SYS_MNTENT_H
139 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
140 # include <sys/mntent.h>
141 #endif
143 #ifndef HAVE_HASMNTOPT
144 # define hasmntopt(mnt, opt) ((char *) 0)
145 #endif
147 #undef MNT_IGNORE
148 #ifdef MNTOPT_IGNORE
149 # if defined __sun && defined __SVR4
150 /* Solaris defines hasmntopt(struct mnttab *, char *)
151 while it is otherwise hasmntopt(struct mnttab *, const char *). */
152 # define MNT_IGNORE(M) hasmntopt (M, (char *) MNTOPT_IGNORE)
153 # else
154 # define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
155 # endif
156 #else
157 # define MNT_IGNORE(M) 0
158 #endif
160 #if USE_UNLOCKED_IO
161 # include "unlocked-io.h"
162 #endif
164 /* The results of opendir() in this file are not used with dirfd and fchdir,
165 therefore save some unnecessary work in fchdir.c. */
166 #ifdef GNULIB_defined_opendir
167 # undef opendir
168 #endif
169 #ifdef GNULIB_defined_closedir
170 # undef closedir
171 #endif
173 #define ME_DUMMY_0(Fs_name, Fs_type) \
174 (strcmp (Fs_type, "autofs") == 0 \
175 || strcmp (Fs_type, "proc") == 0 \
176 || strcmp (Fs_type, "subfs") == 0 \
177 /* for Linux 2.6/3.x */ \
178 || strcmp (Fs_type, "debugfs") == 0 \
179 || strcmp (Fs_type, "devpts") == 0 \
180 || strcmp (Fs_type, "fusectl") == 0 \
181 || strcmp (Fs_type, "mqueue") == 0 \
182 || strcmp (Fs_type, "rpc_pipefs") == 0 \
183 || strcmp (Fs_type, "sysfs") == 0 \
184 /* FreeBSD, Linux 2.4 */ \
185 || strcmp (Fs_type, "devfs") == 0 \
186 /* for NetBSD 3.0 */ \
187 || strcmp (Fs_type, "kernfs") == 0 \
188 /* for Irix 6.5 */ \
189 || strcmp (Fs_type, "ignore") == 0)
191 /* Historically, we have marked as "dummy" any file system of type "none",
192 but now that programs like du need to know about bind-mounted directories,
193 we grant an exception to any with "bind" in its list of mount options.
194 I.e., those are *not* dummy entries. */
195 #ifdef MOUNTED_GETMNTENT1
196 # define ME_DUMMY(Fs_name, Fs_type, Bind) \
197 (ME_DUMMY_0 (Fs_name, Fs_type) \
198 || (strcmp (Fs_type, "none") == 0 && !Bind))
199 #else
200 # define ME_DUMMY(Fs_name, Fs_type) \
201 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
202 #endif
204 #ifdef __CYGWIN__
205 # include <windows.h>
206 # define ME_REMOTE me_remote
207 /* All cygwin mount points include ':' or start with '//'; so it
208 requires a native Windows call to determine remote disks. */
209 static bool
210 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
212 if (fs_name[0] && fs_name[1] == ':')
214 char drive[4];
215 sprintf (drive, "%c:\\", fs_name[0]);
216 switch (GetDriveType (drive))
218 case DRIVE_REMOVABLE:
219 case DRIVE_FIXED:
220 case DRIVE_CDROM:
221 case DRIVE_RAMDISK:
222 return false;
225 return true;
227 #endif
229 #ifndef ME_REMOTE
230 /* A file system is "remote" if its Fs_name contains a ':'
231 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
232 or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). */
233 # define ME_REMOTE(Fs_name, Fs_type) \
234 (strchr (Fs_name, ':') != NULL \
235 || ((Fs_name)[0] == '/' \
236 && (Fs_name)[1] == '/' \
237 && (strcmp (Fs_type, "smbfs") == 0 \
238 || strcmp (Fs_type, "cifs") == 0)) \
239 || (strcmp("-hosts", Fs_name) == 0))
240 #endif
242 #if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
244 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
245 static char *
246 fstype_to_string (short int t)
248 switch (t)
250 # ifdef MOUNT_PC
251 case MOUNT_PC:
252 return "pc";
253 # endif
254 # ifdef MOUNT_MFS
255 case MOUNT_MFS:
256 return "mfs";
257 # endif
258 # ifdef MOUNT_LO
259 case MOUNT_LO:
260 return "lo";
261 # endif
262 # ifdef MOUNT_TFS
263 case MOUNT_TFS:
264 return "tfs";
265 # endif
266 # ifdef MOUNT_TMP
267 case MOUNT_TMP:
268 return "tmp";
269 # endif
270 # ifdef MOUNT_UFS
271 case MOUNT_UFS:
272 return "ufs" ;
273 # endif
274 # ifdef MOUNT_NFS
275 case MOUNT_NFS:
276 return "nfs" ;
277 # endif
278 # ifdef MOUNT_MSDOS
279 case MOUNT_MSDOS:
280 return "msdos" ;
281 # endif
282 # ifdef MOUNT_LFS
283 case MOUNT_LFS:
284 return "lfs" ;
285 # endif
286 # ifdef MOUNT_LOFS
287 case MOUNT_LOFS:
288 return "lofs" ;
289 # endif
290 # ifdef MOUNT_FDESC
291 case MOUNT_FDESC:
292 return "fdesc" ;
293 # endif
294 # ifdef MOUNT_PORTAL
295 case MOUNT_PORTAL:
296 return "portal" ;
297 # endif
298 # ifdef MOUNT_NULL
299 case MOUNT_NULL:
300 return "null" ;
301 # endif
302 # ifdef MOUNT_UMAP
303 case MOUNT_UMAP:
304 return "umap" ;
305 # endif
306 # ifdef MOUNT_KERNFS
307 case MOUNT_KERNFS:
308 return "kernfs" ;
309 # endif
310 # ifdef MOUNT_PROCFS
311 case MOUNT_PROCFS:
312 return "procfs" ;
313 # endif
314 # ifdef MOUNT_AFS
315 case MOUNT_AFS:
316 return "afs" ;
317 # endif
318 # ifdef MOUNT_CD9660
319 case MOUNT_CD9660:
320 return "cd9660" ;
321 # endif
322 # ifdef MOUNT_UNION
323 case MOUNT_UNION:
324 return "union" ;
325 # endif
326 # ifdef MOUNT_DEVFS
327 case MOUNT_DEVFS:
328 return "devfs" ;
329 # endif
330 # ifdef MOUNT_EXT2FS
331 case MOUNT_EXT2FS:
332 return "ext2fs" ;
333 # endif
334 default:
335 return "?";
338 # endif
340 static char *
341 fsp_to_string (const struct statfs *fsp)
343 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
344 return (char *) (fsp->f_fstypename);
345 # else
346 return fstype_to_string (fsp->f_type);
347 # endif
350 #endif /* MOUNTED_GETMNTINFO */
352 #ifdef MOUNTED_VMOUNT /* AIX */
353 static char *
354 fstype_to_string (int t)
356 struct vfs_ent *e;
358 e = getvfsbytype (t);
359 if (!e || !e->vfsent_name)
360 return "none";
361 else
362 return e->vfsent_name;
364 #endif /* MOUNTED_VMOUNT */
367 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
369 /* Return the device number from MOUNT_OPTIONS, if possible.
370 Otherwise return (dev_t) -1. */
371 static dev_t
372 dev_from_mount_options (char const *mount_options)
374 /* GNU/Linux allows file system implementations to define their own
375 meaning for "dev=" mount options, so don't trust the meaning
376 here. */
377 # ifndef __linux__
379 static char const dev_pattern[] = ",dev=";
380 char const *devopt = strstr (mount_options, dev_pattern);
382 if (devopt)
384 char const *optval = devopt + sizeof dev_pattern - 1;
385 char *optvalend;
386 unsigned long int dev;
387 errno = 0;
388 dev = strtoul (optval, &optvalend, 16);
389 if (optval != optvalend
390 && (*optvalend == '\0' || *optvalend == ',')
391 && ! (dev == ULONG_MAX && errno == ERANGE)
392 && dev == (dev_t) dev)
393 return dev;
396 # endif
397 (void) mount_options;
398 return -1;
401 #endif
403 #if defined MOUNTED_GETMNTENT1 && defined __linux__ /* GNU/Linux, Android */
405 /* Unescape the paths in mount tables.
406 STR is updated in place. */
408 static void
409 unescape_tab (char *str)
411 size_t i, j = 0;
412 size_t len = strlen (str) + 1;
413 for (i = 0; i < len; i++)
415 if (str[i] == '\\' && (i + 4 < len)
416 && str[i + 1] >= '0' && str[i + 1] <= '3'
417 && str[i + 2] >= '0' && str[i + 2] <= '7'
418 && str[i + 3] >= '0' && str[i + 3] <= '7')
420 str[j++] = (str[i + 1] - '0') * 64 +
421 (str[i + 2] - '0') * 8 +
422 (str[i + 3] - '0');
423 i += 3;
425 else
426 str[j++] = str[i];
429 #endif
431 /* Return a list of the currently mounted file systems, or NULL on error.
432 Add each entry to the tail of the list so that they stay in order.
433 If NEED_FS_TYPE is true, ensure that the file system type fields in
434 the returned list are valid. Otherwise, they might not be. */
436 struct mount_entry *
437 read_file_system_list (bool need_fs_type)
439 struct mount_entry *mount_list;
440 struct mount_entry *me;
441 struct mount_entry **mtail = &mount_list;
442 (void) need_fs_type;
444 #ifdef MOUNTED_LISTMNTENT /* (obsolete) Cray UNICOS 9 */
446 struct tabmntent *mntlist, *p;
447 struct mntent *mnt;
449 /* the third and fourth arguments could be used to filter mounts,
450 but Crays doesn't seem to have any mounts that we want to
451 remove. Specifically, automount create normal NFS mounts.
454 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
455 return NULL;
456 for (p = mntlist; p; p = p->next)
458 mnt = p->ment;
459 me = xmalloc (sizeof *me);
460 me->me_devname = xstrdup (mnt->mnt_fsname);
461 me->me_mountdir = xstrdup (mnt->mnt_dir);
462 me->me_mntroot = NULL;
463 me->me_type = xstrdup (mnt->mnt_type);
464 me->me_type_malloced = 1;
465 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
466 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
467 me->me_dev = -1;
468 *mtail = me;
469 mtail = &me->me_next;
471 freemntlist (mntlist);
473 #endif
475 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
476 also (obsolete) 4.3BSD, SunOS, Dynix */
478 FILE *fp;
480 # ifdef __linux__
481 /* Try parsing mountinfo first, as that make device IDs available.
482 Note we could use libmount routines to simplify this parsing a little
483 (and that code is in previous versions of this function), however
484 libmount depends on libselinux which pulls in many dependencies. */
485 char const *mountinfo = "/proc/self/mountinfo";
486 fp = fopen (mountinfo, "r");
487 if (fp != NULL)
489 char *line = NULL;
490 size_t buf_size = 0;
492 while (getline (&line, &buf_size, fp) != -1)
494 unsigned int devmaj, devmin;
495 int target_s, target_e, type_s, type_e;
496 int source_s, source_e, mntroot_s, mntroot_e;
497 char test;
498 char *dash;
499 int rc;
501 rc = sscanf(line, "%*u " /* id - discarded */
502 "%*u " /* parent - discarded */
503 "%u:%u " /* dev major:minor */
504 "%n%*s%n " /* mountroot */
505 "%n%*s%n" /* target, start and end */
506 "%c", /* more data... */
507 &devmaj, &devmin,
508 &mntroot_s, &mntroot_e,
509 &target_s, &target_e,
510 &test);
512 if (rc != 3 && rc != 7) /* 7 if %n included in count. */
513 continue;
515 /* skip optional fields, terminated by " - " */
516 dash = strstr (line + target_e, " - ");
517 if (! dash)
518 continue;
520 rc = sscanf(dash, " - "
521 "%n%*s%n " /* FS type, start and end */
522 "%n%*s%n " /* source, start and end */
523 "%c", /* more data... */
524 &type_s, &type_e,
525 &source_s, &source_e,
526 &test);
527 if (rc != 1 && rc != 5) /* 5 if %n included in count. */
528 continue;
530 /* manipulate the sub-strings in place. */
531 line[mntroot_e] = '\0';
532 line[target_e] = '\0';
533 dash[type_e] = '\0';
534 dash[source_e] = '\0';
535 unescape_tab (dash + source_s);
536 unescape_tab (line + target_s);
537 unescape_tab (line + mntroot_s);
539 me = xmalloc (sizeof *me);
541 me->me_devname = xstrdup (dash + source_s);
542 me->me_mountdir = xstrdup (line + target_s);
543 me->me_mntroot = xstrdup (line + mntroot_s);
544 me->me_type = xstrdup (dash + type_s);
545 me->me_type_malloced = 1;
546 me->me_dev = makedev (devmaj, devmin);
547 /* we pass "false" for the "Bind" option as that's only
548 significant when the Fs_type is "none" which will not be
549 the case when parsing "/proc/self/mountinfo", and only
550 applies for static /etc/mtab files. */
551 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
552 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
554 /* Add to the linked list. */
555 *mtail = me;
556 mtail = &me->me_next;
559 free (line);
561 if (ferror (fp))
563 int saved_errno = errno;
564 fclose (fp);
565 errno = saved_errno;
566 goto free_then_fail;
569 if (fclose (fp) == EOF)
570 goto free_then_fail;
572 else /* fallback to /proc/self/mounts (/etc/mtab). */
573 # endif /* __linux __ */
575 struct mntent *mnt;
576 char const *table = MOUNTED;
578 fp = setmntent (table, "r");
579 if (fp == NULL)
580 return NULL;
582 while ((mnt = getmntent (fp)))
584 bool bind = hasmntopt (mnt, "bind");
586 me = xmalloc (sizeof *me);
587 me->me_devname = xstrdup (mnt->mnt_fsname);
588 me->me_mountdir = xstrdup (mnt->mnt_dir);
589 me->me_mntroot = NULL;
590 me->me_type = xstrdup (mnt->mnt_type);
591 me->me_type_malloced = 1;
592 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
593 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
594 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
596 /* Add to the linked list. */
597 *mtail = me;
598 mtail = &me->me_next;
601 if (endmntent (fp) == 0)
602 goto free_then_fail;
605 #endif /* MOUNTED_GETMNTENT1. */
607 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
609 struct statfs *fsp;
610 int entries;
612 entries = getmntinfo (&fsp, MNT_NOWAIT);
613 if (entries < 0)
614 return NULL;
615 for (; entries-- > 0; fsp++)
617 char *fs_type = fsp_to_string (fsp);
619 me = xmalloc (sizeof *me);
620 me->me_devname = xstrdup (fsp->f_mntfromname);
621 me->me_mountdir = xstrdup (fsp->f_mntonname);
622 me->me_mntroot = NULL;
623 me->me_type = fs_type;
624 me->me_type_malloced = 0;
625 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
626 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
627 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
629 /* Add to the linked list. */
630 *mtail = me;
631 mtail = &me->me_next;
634 #endif /* MOUNTED_GETMNTINFO */
636 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
638 struct statvfs *fsp;
639 int entries;
641 entries = getmntinfo (&fsp, MNT_NOWAIT);
642 if (entries < 0)
643 return NULL;
644 for (; entries-- > 0; fsp++)
646 me = xmalloc (sizeof *me);
647 me->me_devname = xstrdup (fsp->f_mntfromname);
648 me->me_mountdir = xstrdup (fsp->f_mntonname);
649 me->me_mntroot = NULL;
650 me->me_type = xstrdup (fsp->f_fstypename);
651 me->me_type_malloced = 1;
652 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
653 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
654 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
656 /* Add to the linked list. */
657 *mtail = me;
658 mtail = &me->me_next;
661 #endif /* MOUNTED_GETMNTINFO2 */
663 #ifdef MOUNTED_GETMNT /* (obsolete) Ultrix */
665 int offset = 0;
666 int val;
667 struct fs_data fsd;
669 while (errno = 0,
670 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
671 (char *) 0)))
673 me = xmalloc (sizeof *me);
674 me->me_devname = xstrdup (fsd.fd_req.devname);
675 me->me_mountdir = xstrdup (fsd.fd_req.path);
676 me->me_mntroot = NULL;
677 me->me_type = gt_names[fsd.fd_req.fstype];
678 me->me_type_malloced = 0;
679 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
680 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
681 me->me_dev = fsd.fd_req.dev;
683 /* Add to the linked list. */
684 *mtail = me;
685 mtail = &me->me_next;
687 if (val < 0)
688 goto free_then_fail;
690 #endif /* MOUNTED_GETMNT. */
692 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
694 /* The next_dev() and fs_stat_dev() system calls give the list of
695 all file systems, including the information returned by statvfs()
696 (fs type, total blocks, free blocks etc.), but without the mount
697 point. But on BeOS all file systems except / are mounted in the
698 rootfs, directly under /.
699 The directory name of the mount point is often, but not always,
700 identical to the volume name of the device.
701 We therefore get the list of subdirectories of /, and the list
702 of all file systems, and match the two lists. */
704 DIR *dirp;
705 struct rootdir_entry
707 char *name;
708 dev_t dev;
709 ino_t ino;
710 struct rootdir_entry *next;
712 struct rootdir_entry *rootdir_list;
713 struct rootdir_entry **rootdir_tail;
714 int32 pos;
715 dev_t dev;
716 fs_info fi;
718 /* All volumes are mounted in the rootfs, directly under /. */
719 rootdir_list = NULL;
720 rootdir_tail = &rootdir_list;
721 dirp = opendir ("/");
722 if (dirp)
724 struct dirent *d;
726 while ((d = readdir (dirp)) != NULL)
728 char *name;
729 struct stat statbuf;
731 if (strcmp (d->d_name, "..") == 0)
732 continue;
734 if (strcmp (d->d_name, ".") == 0)
735 name = xstrdup ("/");
736 else
738 name = xmalloc (1 + strlen (d->d_name) + 1);
739 name[0] = '/';
740 strcpy (name + 1, d->d_name);
743 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
745 struct rootdir_entry *re = xmalloc (sizeof *re);
746 re->name = name;
747 re->dev = statbuf.st_dev;
748 re->ino = statbuf.st_ino;
750 /* Add to the linked list. */
751 *rootdir_tail = re;
752 rootdir_tail = &re->next;
754 else
755 free (name);
757 closedir (dirp);
759 *rootdir_tail = NULL;
761 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
762 if (fs_stat_dev (dev, &fi) >= 0)
764 /* Note: fi.dev == dev. */
765 struct rootdir_entry *re;
767 for (re = rootdir_list; re; re = re->next)
768 if (re->dev == fi.dev && re->ino == fi.root)
769 break;
771 me = xmalloc (sizeof *me);
772 me->me_devname = xstrdup (fi.device_name[0] != '\0'
773 ? fi.device_name : fi.fsh_name);
774 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
775 me->me_mntroot = NULL;
776 me->me_type = xstrdup (fi.fsh_name);
777 me->me_type_malloced = 1;
778 me->me_dev = fi.dev;
779 me->me_dummy = 0;
780 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
782 /* Add to the linked list. */
783 *mtail = me;
784 mtail = &me->me_next;
786 *mtail = NULL;
788 while (rootdir_list != NULL)
790 struct rootdir_entry *re = rootdir_list;
791 rootdir_list = re->next;
792 free (re->name);
793 free (re);
796 #endif /* MOUNTED_FS_STAT_DEV */
798 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
800 int numsys, counter;
801 size_t bufsize;
802 struct statfs *stats;
804 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
805 if (numsys < 0)
806 return NULL;
807 if (SIZE_MAX / sizeof *stats <= numsys)
808 xalloc_die ();
810 bufsize = (1 + numsys) * sizeof *stats;
811 stats = xmalloc (bufsize);
812 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
814 if (numsys < 0)
816 free (stats);
817 return NULL;
820 for (counter = 0; counter < numsys; counter++)
822 me = xmalloc (sizeof *me);
823 me->me_devname = xstrdup (stats[counter].f_mntfromname);
824 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
825 me->me_mntroot = NULL;
826 me->me_type = xstrdup (FS_TYPE (stats[counter]));
827 me->me_type_malloced = 1;
828 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
829 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
830 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
832 /* Add to the linked list. */
833 *mtail = me;
834 mtail = &me->me_next;
837 free (stats);
839 #endif /* MOUNTED_GETFSSTAT */
841 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3, SVR2 */
843 struct mnttab mnt;
844 char *table = "/etc/mnttab";
845 FILE *fp;
847 fp = fopen (table, "r");
848 if (fp == NULL)
849 return NULL;
851 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
853 me = xmalloc (sizeof *me);
854 # ifdef GETFSTYP /* SVR3. */
855 me->me_devname = xstrdup (mnt.mt_dev);
856 # else
857 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
858 strcpy (me->me_devname, "/dev/");
859 strcpy (me->me_devname + 5, mnt.mt_dev);
860 # endif
861 me->me_mountdir = xstrdup (mnt.mt_filsys);
862 me->me_mntroot = NULL;
863 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
864 me->me_type = "";
865 me->me_type_malloced = 0;
866 # ifdef GETFSTYP /* SVR3. */
867 if (need_fs_type)
869 struct statfs fsd;
870 char typebuf[FSTYPSZ];
872 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
873 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
875 me->me_type = xstrdup (typebuf);
876 me->me_type_malloced = 1;
879 # endif
880 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
881 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
883 /* Add to the linked list. */
884 *mtail = me;
885 mtail = &me->me_next;
888 if (ferror (fp))
890 /* The last fread() call must have failed. */
891 int saved_errno = errno;
892 fclose (fp);
893 errno = saved_errno;
894 goto free_then_fail;
897 if (fclose (fp) == EOF)
898 goto free_then_fail;
900 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
902 #ifdef MOUNTED_GETMNTTBL /* (obsolete) DolphinOS */
904 struct mntent **mnttbl = getmnttbl (), **ent;
905 for (ent = mnttbl; *ent; ent++)
907 me = xmalloc (sizeof *me);
908 me->me_devname = xstrdup ((*ent)->mt_resource);
909 me->me_mountdir = xstrdup ((*ent)->mt_directory);
910 me->me_mntroot = NULL;
911 me->me_type = xstrdup ((*ent)->mt_fstype);
912 me->me_type_malloced = 1;
913 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
914 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
915 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
917 /* Add to the linked list. */
918 *mtail = me;
919 mtail = &me->me_next;
921 endmnttbl ();
923 #endif /* MOUNTED_GETMNTTBL */
925 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
927 struct extmnttab mnt;
928 const char *table = MNTTAB;
929 FILE *fp;
930 int ret;
932 /* No locking is needed, because the contents of /etc/mnttab is generated
933 by the kernel. */
935 errno = 0;
936 fp = fopen (table, "r");
937 if (fp == NULL)
938 ret = errno;
939 else
941 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
943 me = xmalloc (sizeof *me);
944 me->me_devname = xstrdup (mnt.mnt_special);
945 me->me_mountdir = xstrdup (mnt.mnt_mountp);
946 me->me_mntroot = NULL;
947 me->me_type = xstrdup (mnt.mnt_fstype);
948 me->me_type_malloced = 1;
949 me->me_dummy = MNT_IGNORE (&mnt) != 0;
950 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
951 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
953 /* Add to the linked list. */
954 *mtail = me;
955 mtail = &me->me_next;
958 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
959 /* Here ret = -1 means success, ret >= 0 means failure. */
962 if (0 <= ret)
964 errno = ret;
965 goto free_then_fail;
968 #endif /* MOUNTED_GETMNTTBL */
970 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
972 struct mnttab mnt;
973 const char *table = MNTTAB;
974 FILE *fp;
975 int ret;
976 int lockfd = -1;
978 # if defined F_RDLCK && defined F_SETLKW
979 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
980 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
981 for this file name, we should use their macro name instead.
982 (Why not just lock MNTTAB directly? We don't know.) */
983 # ifndef MNTTAB_LOCK
984 # define MNTTAB_LOCK "/etc/.mnttab.lock"
985 # endif
986 lockfd = open (MNTTAB_LOCK, O_RDONLY);
987 if (0 <= lockfd)
989 struct flock flock;
990 flock.l_type = F_RDLCK;
991 flock.l_whence = SEEK_SET;
992 flock.l_start = 0;
993 flock.l_len = 0;
994 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
995 if (errno != EINTR)
997 int saved_errno = errno;
998 close (lockfd);
999 errno = saved_errno;
1000 return NULL;
1003 else if (errno != ENOENT)
1004 return NULL;
1005 # endif
1007 errno = 0;
1008 fp = fopen (table, "r");
1009 if (fp == NULL)
1010 ret = errno;
1011 else
1013 while ((ret = getmntent (fp, &mnt)) == 0)
1015 me = xmalloc (sizeof *me);
1016 me->me_devname = xstrdup (mnt.mnt_special);
1017 me->me_mountdir = xstrdup (mnt.mnt_mountp);
1018 me->me_mntroot = NULL;
1019 me->me_type = xstrdup (mnt.mnt_fstype);
1020 me->me_type_malloced = 1;
1021 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1022 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1023 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1025 /* Add to the linked list. */
1026 *mtail = me;
1027 mtail = &me->me_next;
1030 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1031 /* Here ret = -1 means success, ret >= 0 means failure. */
1034 if (0 <= lockfd && close (lockfd) != 0)
1035 ret = errno;
1037 if (0 <= ret)
1039 errno = ret;
1040 goto free_then_fail;
1043 #endif /* MOUNTED_GETMNTENT2. */
1045 #ifdef MOUNTED_VMOUNT /* AIX */
1047 int bufsize;
1048 void *entries;
1049 char *thisent;
1050 struct vmount *vmp;
1051 int n_entries;
1052 int i;
1054 /* Ask how many bytes to allocate for the mounted file system info. */
1055 entries = &bufsize;
1056 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
1057 return NULL;
1058 entries = xmalloc (bufsize);
1060 /* Get the list of mounted file systems. */
1061 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
1062 if (n_entries < 0)
1064 int saved_errno = errno;
1065 free (entries);
1066 errno = saved_errno;
1067 return NULL;
1070 for (i = 0, thisent = entries;
1071 i < n_entries;
1072 i++, thisent += vmp->vmt_length)
1074 char *options, *ignore;
1076 vmp = (struct vmount *) thisent;
1077 me = xmalloc (sizeof *me);
1078 if (vmp->vmt_flags & MNT_REMOTE)
1080 char *host, *dir;
1082 me->me_remote = 1;
1083 /* Prepend the remote dirname. */
1084 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1085 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1086 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
1087 strcpy (me->me_devname, host);
1088 strcat (me->me_devname, ":");
1089 strcat (me->me_devname, dir);
1091 else
1093 me->me_remote = 0;
1094 me->me_devname = xstrdup (thisent +
1095 vmp->vmt_data[VMT_OBJECT].vmt_off);
1097 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1098 me->me_mntroot = NULL;
1099 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1100 me->me_type_malloced = 1;
1101 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1102 ignore = strstr (options, "ignore");
1103 me->me_dummy = (ignore
1104 && (ignore == options || ignore[-1] == ',')
1105 && (ignore[sizeof "ignore" - 1] == ','
1106 || ignore[sizeof "ignore" - 1] == '\0'));
1107 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
1109 /* Add to the linked list. */
1110 *mtail = me;
1111 mtail = &me->me_next;
1113 free (entries);
1115 #endif /* MOUNTED_VMOUNT. */
1117 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1119 DIR *dirp = opendir ("/dev/fs");
1120 char node[9 + NAME_MAX];
1122 if (!dirp)
1123 goto free_then_fail;
1125 while (1)
1127 struct statvfs dev;
1128 struct dirent entry;
1129 struct dirent *result;
1131 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1132 marked obsolescent in glibc. Use readdir instead. */
1133 if (readdir_r (dirp, &entry, &result) || result == NULL)
1134 break;
1136 strcpy (node, "/dev/fs/");
1137 strcat (node, entry.d_name);
1139 if (statvfs (node, &dev) == 0)
1141 me = xmalloc (sizeof *me);
1142 me->me_devname = xstrdup (dev.f_mntfromname);
1143 me->me_mountdir = xstrdup (dev.f_mntonname);
1144 me->me_mntroot = NULL;
1145 me->me_type = xstrdup (dev.f_fstypename);
1146 me->me_type_malloced = 1;
1147 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1148 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1149 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
1151 /* Add to the linked list. */
1152 *mtail = me;
1153 mtail = &me->me_next;
1156 closedir (dirp);
1158 #endif /* MOUNTED_INTERIX_STATVFS */
1160 *mtail = NULL;
1161 return mount_list;
1164 free_then_fail: _GL_UNUSED_LABEL
1166 int saved_errno = errno;
1167 *mtail = NULL;
1169 while (mount_list)
1171 me = mount_list->me_next;
1172 free_mount_entry (mount_list);
1173 mount_list = me;
1176 errno = saved_errno;
1177 return NULL;
1181 /* Free a mount entry as returned from read_file_system_list (). */
1183 void free_mount_entry (struct mount_entry *me)
1185 free (me->me_devname);
1186 free (me->me_mountdir);
1187 free (me->me_mntroot);
1188 if (me->me_type_malloced)
1189 free (me->me_type);
1190 free (me);