exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / mountlist.c
blob06300d6b73b03ec0646ef5cee010451326787427
1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2024 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 "e")
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 /* Each of the FILE streams in this file is only used in a single thread. */
153 #include "unlocked-io.h"
155 /* The results of opendir() in this file are not used with dirfd and fchdir,
156 therefore save some unnecessary work in fchdir.c. */
157 #ifdef GNULIB_defined_DIR
158 # undef DIR
159 # undef opendir
160 # undef closedir
161 # undef readdir
162 #else
163 # ifdef GNULIB_defined_opendir
164 # undef opendir
165 # endif
166 # ifdef GNULIB_defined_closedir
167 # undef closedir
168 # endif
169 #endif
171 #define ME_DUMMY_0(Fs_name, Fs_type) \
172 (strcmp (Fs_type, "autofs") == 0 \
173 || strcmp (Fs_type, "proc") == 0 \
174 || strcmp (Fs_type, "subfs") == 0 \
175 /* for Linux 2.6/3.x */ \
176 || strcmp (Fs_type, "debugfs") == 0 \
177 || strcmp (Fs_type, "devpts") == 0 \
178 || strcmp (Fs_type, "fusectl") == 0 \
179 || strcmp (Fs_type, "fuse.portal") == 0 \
180 || strcmp (Fs_type, "mqueue") == 0 \
181 || strcmp (Fs_type, "rpc_pipefs") == 0 \
182 || strcmp (Fs_type, "sysfs") == 0 \
183 /* FreeBSD, Linux 2.4 */ \
184 || strcmp (Fs_type, "devfs") == 0 \
185 /* for NetBSD 3.0 */ \
186 || strcmp (Fs_type, "kernfs") == 0 \
187 /* for Irix 6.5 */ \
188 || strcmp (Fs_type, "ignore") == 0)
190 /* Historically, we have marked as "dummy" any file system of type "none",
191 but now that programs like du need to know about bind-mounted directories,
192 we grant an exception to any with "bind" in its list of mount options.
193 I.e., those are *not* dummy entries. */
194 #ifdef MOUNTED_GETMNTENT1
195 # define ME_DUMMY(Fs_name, Fs_type, Bind) \
196 (ME_DUMMY_0 (Fs_name, Fs_type) \
197 || (strcmp (Fs_type, "none") == 0 && !Bind))
198 #else
199 # define ME_DUMMY(Fs_name, Fs_type) \
200 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
201 #endif
203 #ifdef __CYGWIN__
204 # include <windows.h>
205 /* Don't assume that UNICODE is not defined. */
206 # undef GetDriveType
207 # define GetDriveType GetDriveTypeA
208 # define ME_REMOTE me_remote
209 /* All cygwin mount points include ':' or start with '//'; so it
210 requires a native Windows call to determine remote disks. */
211 static bool
212 me_remote (char const *fs_name, _GL_UNUSED char const *fs_type)
214 if (fs_name[0] && fs_name[1] == ':')
216 char drive[4];
217 sprintf (drive, "%c:\\", fs_name[0]);
218 switch (GetDriveType (drive))
220 case DRIVE_REMOVABLE:
221 case DRIVE_FIXED:
222 case DRIVE_CDROM:
223 case DRIVE_RAMDISK:
224 return false;
227 return true;
229 #endif
231 #ifndef ME_REMOTE
232 /* A file system is "remote" if its Fs_name contains a ':'
233 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
234 or if it is of any other of the listed types
235 or Fs_name is equal to "-hosts" (used by autofs to mount remote fs).
236 "VM" file systems like prl_fs or vboxsf are not considered remote here. */
237 # define ME_REMOTE(Fs_name, Fs_type) \
238 (strchr (Fs_name, ':') != NULL \
239 || ((Fs_name)[0] == '/' \
240 && (Fs_name)[1] == '/' \
241 && (strcmp (Fs_type, "smbfs") == 0 \
242 || strcmp (Fs_type, "smb3") == 0 \
243 || strcmp (Fs_type, "cifs") == 0)) \
244 || strcmp (Fs_type, "acfs") == 0 \
245 || strcmp (Fs_type, "afs") == 0 \
246 || strcmp (Fs_type, "coda") == 0 \
247 || strcmp (Fs_type, "auristorfs") == 0 \
248 || strcmp (Fs_type, "fhgfs") == 0 \
249 || strcmp (Fs_type, "gpfs") == 0 \
250 || strcmp (Fs_type, "ibrix") == 0 \
251 || strcmp (Fs_type, "ocfs2") == 0 \
252 || strcmp (Fs_type, "vxfs") == 0 \
253 || strcmp ("-hosts", Fs_name) == 0)
254 #endif
256 #if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
258 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
259 static char *
260 fstype_to_string (short int t)
262 switch (t)
264 # ifdef MOUNT_PC
265 case MOUNT_PC:
266 return "pc";
267 # endif
268 # ifdef MOUNT_MFS
269 case MOUNT_MFS:
270 return "mfs";
271 # endif
272 # ifdef MOUNT_LO
273 case MOUNT_LO:
274 return "lo";
275 # endif
276 # ifdef MOUNT_TFS
277 case MOUNT_TFS:
278 return "tfs";
279 # endif
280 # ifdef MOUNT_TMP
281 case MOUNT_TMP:
282 return "tmp";
283 # endif
284 # ifdef MOUNT_UFS
285 case MOUNT_UFS:
286 return "ufs" ;
287 # endif
288 # ifdef MOUNT_NFS
289 case MOUNT_NFS:
290 return "nfs" ;
291 # endif
292 # ifdef MOUNT_MSDOS
293 case MOUNT_MSDOS:
294 return "msdos" ;
295 # endif
296 # ifdef MOUNT_LFS
297 case MOUNT_LFS:
298 return "lfs" ;
299 # endif
300 # ifdef MOUNT_LOFS
301 case MOUNT_LOFS:
302 return "lofs" ;
303 # endif
304 # ifdef MOUNT_FDESC
305 case MOUNT_FDESC:
306 return "fdesc" ;
307 # endif
308 # ifdef MOUNT_PORTAL
309 case MOUNT_PORTAL:
310 return "portal" ;
311 # endif
312 # ifdef MOUNT_NULL
313 case MOUNT_NULL:
314 return "null" ;
315 # endif
316 # ifdef MOUNT_UMAP
317 case MOUNT_UMAP:
318 return "umap" ;
319 # endif
320 # ifdef MOUNT_KERNFS
321 case MOUNT_KERNFS:
322 return "kernfs" ;
323 # endif
324 # ifdef MOUNT_PROCFS
325 case MOUNT_PROCFS:
326 return "procfs" ;
327 # endif
328 # ifdef MOUNT_AFS
329 case MOUNT_AFS:
330 return "afs" ;
331 # endif
332 # ifdef MOUNT_CD9660
333 case MOUNT_CD9660:
334 return "cd9660" ;
335 # endif
336 # ifdef MOUNT_UNION
337 case MOUNT_UNION:
338 return "union" ;
339 # endif
340 # ifdef MOUNT_DEVFS
341 case MOUNT_DEVFS:
342 return "devfs" ;
343 # endif
344 # ifdef MOUNT_EXT2FS
345 case MOUNT_EXT2FS:
346 return "ext2fs" ;
347 # endif
348 default:
349 return "?";
352 # endif
354 static char *
355 fsp_to_string (const struct statfs *fsp)
357 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
358 return (char *) (fsp->f_fstypename);
359 # else
360 return fstype_to_string (fsp->f_type);
361 # endif
364 #endif /* MOUNTED_GETMNTINFO */
366 #ifdef MOUNTED_VMOUNT /* AIX */
367 static char *
368 fstype_to_string (int t)
370 struct vfs_ent *e;
372 e = getvfsbytype (t);
373 if (!e || !e->vfsent_name)
374 return "none";
375 else
376 return e->vfsent_name;
378 #endif /* MOUNTED_VMOUNT */
381 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
383 /* Return the device number from MOUNT_OPTIONS, if possible.
384 Otherwise return (dev_t) -1. */
385 static dev_t
386 dev_from_mount_options (char const *mount_options)
388 /* GNU/Linux allows file system implementations to define their own
389 meaning for "dev=" mount options, so don't trust the meaning
390 here. */
391 # ifndef __linux__
393 static char const dev_pattern[] = ",dev=";
394 char const *devopt = strstr (mount_options, dev_pattern);
396 if (devopt)
398 char const *optval = devopt + sizeof dev_pattern - 1;
399 char *optvalend;
400 unsigned long int dev;
401 errno = 0;
402 dev = strtoul (optval, &optvalend, 16);
403 if (optval != optvalend
404 && (*optvalend == '\0' || *optvalend == ',')
405 && ! (dev == ULONG_MAX && errno == ERANGE)
406 && dev == (dev_t) dev)
407 return dev;
410 # endif
411 (void) mount_options;
412 return -1;
415 #endif
417 #if defined MOUNTED_GETMNTENT1 && (defined __linux__ || defined __ANDROID__) /* GNU/Linux, Android */
419 /* Unescape the paths in mount tables.
420 STR is updated in place. */
422 static void
423 unescape_tab (char *str)
425 size_t i, j = 0;
426 size_t len = strlen (str) + 1;
427 for (i = 0; i < len; i++)
429 if (str[i] == '\\' && (i + 4 < len)
430 && str[i + 1] >= '0' && str[i + 1] <= '3'
431 && str[i + 2] >= '0' && str[i + 2] <= '7'
432 && str[i + 3] >= '0' && str[i + 3] <= '7')
434 str[j++] = (str[i + 1] - '0') * 64 +
435 (str[i + 2] - '0') * 8 +
436 (str[i + 3] - '0');
437 i += 3;
439 else
440 str[j++] = str[i];
444 /* Find the next space in STR, terminate the string there in place,
445 and return that position. Otherwise return NULL. */
447 static char *
448 terminate_at_blank (char *str)
450 char *s = strchr (str, ' ');
451 if (s)
452 *s = '\0';
453 return s;
455 #endif
457 /* Return a list of the currently mounted file systems, or NULL on error.
458 Add each entry to the tail of the list so that they stay in order.
459 If NEED_FS_TYPE is true, ensure that the file system type fields in
460 the returned list are valid. Otherwise, they might not be. */
462 struct mount_entry *
463 read_file_system_list (bool need_fs_type)
465 struct mount_entry *mount_list;
466 struct mount_entry *me;
467 struct mount_entry **mtail = &mount_list;
468 (void) need_fs_type;
470 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
471 also (obsolete) 4.3BSD, SunOS */
473 FILE *fp;
475 # if defined __linux__ || defined __ANDROID__
476 /* Try parsing mountinfo first, as that make device IDs available.
477 Note we could use libmount routines to simplify this parsing a little
478 (and that code is in previous versions of this function), however
479 libmount depends on libselinux which pulls in many dependencies. */
480 char const *mountinfo = "/proc/self/mountinfo";
481 fp = fopen (mountinfo, "re");
482 if (fp != NULL)
484 char *line = NULL;
485 size_t buf_size = 0;
487 while (getline (&line, &buf_size, fp) != -1)
489 unsigned int devmaj, devmin;
490 int rc, mntroot_s;
492 rc = sscanf(line, "%*u " /* id - discarded */
493 "%*u " /* parent - discarded */
494 "%u:%u " /* dev major:minor */
495 "%n", /* mountroot (start) */
496 &devmaj, &devmin,
497 &mntroot_s);
499 if (rc != 2 && rc != 3) /* 3 if %n included in count. */
500 continue;
502 /* find end of MNTROOT. */
503 char *mntroot = line + mntroot_s;
504 char *blank = terminate_at_blank (mntroot);
505 if (! blank)
506 continue;
508 /* find end of TARGET. */
509 char *target = blank + 1;
510 blank = terminate_at_blank (target);
511 if (! blank)
512 continue;
514 /* skip optional fields, terminated by " - " */
515 char *dash = strstr (blank + 1, " - ");
516 if (! dash)
517 continue;
519 /* advance past the " - " separator. */
520 char *fstype = dash + 3;
521 blank = terminate_at_blank (fstype);
522 if (! blank)
523 continue;
525 /* find end of SOURCE. */
526 char *source = blank + 1;
527 if (! terminate_at_blank (source))
528 continue;
530 /* manipulate the sub-strings in place. */
531 unescape_tab (source);
532 unescape_tab (target);
533 unescape_tab (mntroot);
534 unescape_tab (fstype);
536 me = xmalloc (sizeof *me);
538 me->me_devname = xstrdup (source);
539 me->me_mountdir = xstrdup (target);
540 me->me_mntroot = xstrdup (mntroot);
541 me->me_type = xstrdup (fstype);
542 me->me_type_malloced = 1;
543 me->me_dev = makedev (devmaj, devmin);
544 /* we pass "false" for the "Bind" option as that's only
545 significant when the Fs_type is "none" which will not be
546 the case when parsing "/proc/self/mountinfo", and only
547 applies for static /etc/mtab files. */
548 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
549 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
551 /* Add to the linked list. */
552 *mtail = me;
553 mtail = &me->me_next;
556 free (line);
558 if (ferror (fp))
560 int saved_errno = errno;
561 fclose (fp);
562 errno = saved_errno;
563 goto free_then_fail;
566 if (fclose (fp) == EOF)
567 goto free_then_fail;
569 else /* fallback to /proc/self/mounts (/etc/mtab). */
570 # endif /* __linux __ || __ANDROID__ */
572 struct mntent *mnt;
573 char const *table = MOUNTED;
575 fp = setmntent (table, "r");
576 if (fp == NULL)
577 return NULL;
579 while ((mnt = getmntent (fp)))
581 bool bind = hasmntopt (mnt, "bind");
583 me = xmalloc (sizeof *me);
584 me->me_devname = xstrdup (mnt->mnt_fsname);
585 me->me_mountdir = xstrdup (mnt->mnt_dir);
586 me->me_mntroot = NULL;
587 me->me_type = xstrdup (mnt->mnt_type);
588 me->me_type_malloced = 1;
589 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
590 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
591 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
593 /* Add to the linked list. */
594 *mtail = me;
595 mtail = &me->me_next;
598 if (endmntent (fp) == 0)
599 goto free_then_fail;
602 #endif /* MOUNTED_GETMNTENT1. */
604 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
606 struct statfs *fsp;
607 int entries;
609 entries = getmntinfo (&fsp, MNT_NOWAIT);
610 if (entries < 0)
611 return NULL;
612 for (; entries-- > 0; fsp++)
614 char *fs_type = fsp_to_string (fsp);
616 me = xmalloc (sizeof *me);
617 me->me_devname = xstrdup (fsp->f_mntfromname);
618 me->me_mountdir = xstrdup (fsp->f_mntonname);
619 me->me_mntroot = NULL;
620 me->me_type = fs_type;
621 me->me_type_malloced = 0;
622 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
623 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
624 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
626 /* Add to the linked list. */
627 *mtail = me;
628 mtail = &me->me_next;
631 #endif /* MOUNTED_GETMNTINFO */
633 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
635 struct statvfs *fsp;
636 int entries;
638 entries = getmntinfo (&fsp, MNT_NOWAIT);
639 if (entries < 0)
640 return NULL;
641 for (; entries-- > 0; fsp++)
643 me = xmalloc (sizeof *me);
644 me->me_devname = xstrdup (fsp->f_mntfromname);
645 me->me_mountdir = xstrdup (fsp->f_mntonname);
646 me->me_mntroot = NULL;
647 me->me_type = xstrdup (fsp->f_fstypename);
648 me->me_type_malloced = 1;
649 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
650 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
651 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
653 /* Add to the linked list. */
654 *mtail = me;
655 mtail = &me->me_next;
658 #endif /* MOUNTED_GETMNTINFO2 */
660 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
662 /* The next_dev() and fs_stat_dev() system calls give the list of
663 all file systems, including the information returned by statvfs()
664 (fs type, total blocks, free blocks etc.), but without the mount
665 point. But on BeOS all file systems except / are mounted in the
666 rootfs, directly under /.
667 The directory name of the mount point is often, but not always,
668 identical to the volume name of the device.
669 We therefore get the list of subdirectories of /, and the list
670 of all file systems, and match the two lists. */
672 DIR *dirp;
673 struct rootdir_entry
675 char *name;
676 dev_t dev;
677 ino_t ino;
678 struct rootdir_entry *next;
680 struct rootdir_entry *rootdir_list;
681 struct rootdir_entry **rootdir_tail;
682 int32 pos;
683 dev_t dev;
684 fs_info fi;
686 /* All volumes are mounted in the rootfs, directly under /. */
687 rootdir_list = NULL;
688 rootdir_tail = &rootdir_list;
689 dirp = opendir ("/");
690 if (dirp)
692 struct dirent *d;
694 while ((d = readdir (dirp)) != NULL)
696 char *name;
697 struct stat statbuf;
699 if (strcmp (d->d_name, "..") == 0)
700 continue;
702 if (strcmp (d->d_name, ".") == 0)
703 name = xstrdup ("/");
704 else
706 name = xmalloc (1 + strlen (d->d_name) + 1);
707 name[0] = '/';
708 strcpy (name + 1, d->d_name);
711 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
713 struct rootdir_entry *re = xmalloc (sizeof *re);
714 re->name = name;
715 re->dev = statbuf.st_dev;
716 re->ino = statbuf.st_ino;
718 /* Add to the linked list. */
719 *rootdir_tail = re;
720 rootdir_tail = &re->next;
722 else
723 free (name);
725 closedir (dirp);
727 *rootdir_tail = NULL;
729 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
730 if (fs_stat_dev (dev, &fi) >= 0)
732 /* Note: fi.dev == dev. */
733 struct rootdir_entry *re;
735 for (re = rootdir_list; re; re = re->next)
736 if (re->dev == fi.dev && re->ino == fi.root)
737 break;
739 me = xmalloc (sizeof *me);
740 me->me_devname = xstrdup (fi.device_name[0] != '\0'
741 ? fi.device_name : fi.fsh_name);
742 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
743 me->me_mntroot = NULL;
744 me->me_type = xstrdup (fi.fsh_name);
745 me->me_type_malloced = 1;
746 me->me_dev = fi.dev;
747 me->me_dummy = 0;
748 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
750 /* Add to the linked list. */
751 *mtail = me;
752 mtail = &me->me_next;
754 *mtail = NULL;
756 while (rootdir_list != NULL)
758 struct rootdir_entry *re = rootdir_list;
759 rootdir_list = re->next;
760 free (re->name);
761 free (re);
764 #endif /* MOUNTED_FS_STAT_DEV */
766 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
768 int numsys, counter;
769 size_t bufsize;
770 struct statfs *stats;
772 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
773 if (numsys < 0)
774 return NULL;
775 if (SIZE_MAX / sizeof *stats <= numsys)
776 xalloc_die ();
778 bufsize = (1 + numsys) * sizeof *stats;
779 stats = xmalloc (bufsize);
780 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
782 if (numsys < 0)
784 free (stats);
785 return NULL;
788 for (counter = 0; counter < numsys; counter++)
790 me = xmalloc (sizeof *me);
791 me->me_devname = xstrdup (stats[counter].f_mntfromname);
792 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
793 me->me_mntroot = NULL;
794 me->me_type = xstrdup (FS_TYPE (stats[counter]));
795 me->me_type_malloced = 1;
796 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
797 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
798 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
800 /* Add to the linked list. */
801 *mtail = me;
802 mtail = &me->me_next;
805 free (stats);
807 #endif /* MOUNTED_GETFSSTAT */
809 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
811 struct mnttab mnt;
812 char *table = "/etc/mnttab";
813 FILE *fp;
815 fp = fopen (table, "re");
816 if (fp == NULL)
817 return NULL;
819 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
821 me = xmalloc (sizeof *me);
822 me->me_devname = xstrdup (mnt.mt_dev);
823 me->me_mountdir = xstrdup (mnt.mt_filsys);
824 me->me_mntroot = NULL;
825 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
826 me->me_type = "";
827 me->me_type_malloced = 0;
828 if (need_fs_type)
830 struct statfs fsd;
831 char typebuf[FSTYPSZ];
833 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
834 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
836 me->me_type = xstrdup (typebuf);
837 me->me_type_malloced = 1;
840 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
841 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
843 /* Add to the linked list. */
844 *mtail = me;
845 mtail = &me->me_next;
848 if (ferror (fp))
850 /* The last fread() call must have failed. */
851 int saved_errno = errno;
852 fclose (fp);
853 errno = saved_errno;
854 goto free_then_fail;
857 if (fclose (fp) == EOF)
858 goto free_then_fail;
860 #endif /* MOUNTED_FREAD_FSTYP. */
862 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
864 struct extmnttab mnt;
865 const char *table = MNTTAB;
866 FILE *fp;
867 int ret;
869 /* No locking is needed, because the contents of /etc/mnttab is generated
870 by the kernel. */
872 errno = 0;
873 fp = fopen (table, "re");
874 if (fp == NULL)
875 ret = errno;
876 else
878 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
880 me = xmalloc (sizeof *me);
881 me->me_devname = xstrdup (mnt.mnt_special);
882 me->me_mountdir = xstrdup (mnt.mnt_mountp);
883 me->me_mntroot = NULL;
884 me->me_type = xstrdup (mnt.mnt_fstype);
885 me->me_type_malloced = 1;
886 me->me_dummy = MNT_IGNORE (&mnt) != 0;
887 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
888 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
890 /* Add to the linked list. */
891 *mtail = me;
892 mtail = &me->me_next;
895 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
896 /* Here ret = -1 means success, ret >= 0 means failure. */
899 if (0 <= ret)
901 errno = ret;
902 goto free_then_fail;
905 #endif /* MOUNTED_GETEXTMNTENT */
907 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
909 struct mnttab mnt;
910 const char *table = MNTTAB;
911 FILE *fp;
912 int ret;
913 int lockfd = -1;
915 # if defined F_RDLCK && defined F_SETLKW
916 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
917 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
918 for this file name, we should use their macro name instead.
919 (Why not just lock MNTTAB directly? We don't know.) */
920 # ifndef MNTTAB_LOCK
921 # define MNTTAB_LOCK "/etc/.mnttab.lock"
922 # endif
923 lockfd = open (MNTTAB_LOCK, O_RDONLY | O_CLOEXEC);
924 if (0 <= lockfd)
926 struct flock flock;
927 flock.l_type = F_RDLCK;
928 flock.l_whence = SEEK_SET;
929 flock.l_start = 0;
930 flock.l_len = 0;
931 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
932 if (errno != EINTR)
934 int saved_errno = errno;
935 close (lockfd);
936 errno = saved_errno;
937 return NULL;
940 else if (errno != ENOENT)
941 return NULL;
942 # endif
944 errno = 0;
945 fp = fopen (table, "re");
946 if (fp == NULL)
947 ret = errno;
948 else
950 while ((ret = getmntent (fp, &mnt)) == 0)
952 me = xmalloc (sizeof *me);
953 me->me_devname = xstrdup (mnt.mnt_special);
954 me->me_mountdir = xstrdup (mnt.mnt_mountp);
955 me->me_mntroot = NULL;
956 me->me_type = xstrdup (mnt.mnt_fstype);
957 me->me_type_malloced = 1;
958 me->me_dummy = MNT_IGNORE (&mnt) != 0;
959 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
960 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
962 /* Add to the linked list. */
963 *mtail = me;
964 mtail = &me->me_next;
967 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
968 /* Here ret = -1 means success, ret >= 0 means failure. */
971 if (0 <= lockfd && close (lockfd) != 0)
972 ret = errno;
974 if (0 <= ret)
976 errno = ret;
977 goto free_then_fail;
980 #endif /* MOUNTED_GETMNTENT2. */
982 #ifdef MOUNTED_VMOUNT /* AIX */
984 int bufsize;
985 void *entries;
986 char *thisent;
987 struct vmount *vmp;
988 int n_entries;
989 int i;
991 /* Ask how many bytes to allocate for the mounted file system info. */
992 entries = &bufsize;
993 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
994 return NULL;
995 entries = xmalloc (bufsize);
997 /* Get the list of mounted file systems. */
998 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
999 if (n_entries < 0)
1001 free (entries);
1002 return NULL;
1005 for (i = 0, thisent = entries;
1006 i < n_entries;
1007 i++, thisent += vmp->vmt_length)
1009 char *options, *ignore;
1011 vmp = (struct vmount *) thisent;
1012 me = xmalloc (sizeof *me);
1013 if (vmp->vmt_flags & MNT_REMOTE)
1015 char *host, *dir;
1017 me->me_remote = 1;
1018 /* Prepend the remote dirname. */
1019 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1020 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1021 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
1022 strcpy (me->me_devname, host);
1023 strcat (me->me_devname, ":");
1024 strcat (me->me_devname, dir);
1026 else
1028 me->me_remote = 0;
1029 me->me_devname = xstrdup (thisent +
1030 vmp->vmt_data[VMT_OBJECT].vmt_off);
1032 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1033 me->me_mntroot = NULL;
1034 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1035 me->me_type_malloced = 1;
1036 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1037 ignore = strstr (options, "ignore");
1038 me->me_dummy = (ignore
1039 && (ignore == options || ignore[-1] == ',')
1040 && (ignore[sizeof "ignore" - 1] == ','
1041 || ignore[sizeof "ignore" - 1] == '\0'));
1042 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
1044 /* Add to the linked list. */
1045 *mtail = me;
1046 mtail = &me->me_next;
1048 free (entries);
1050 #endif /* MOUNTED_VMOUNT. */
1052 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1054 DIR *dirp = opendir ("/dev/fs");
1055 char node[9 + NAME_MAX];
1057 if (!dirp)
1058 goto free_then_fail;
1060 while (1)
1062 struct statvfs dev;
1063 struct dirent entry;
1064 struct dirent *result;
1066 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1067 marked obsolescent in glibc. Use readdir instead. */
1068 if (readdir_r (dirp, &entry, &result) || result == NULL)
1069 break;
1071 strcpy (node, "/dev/fs/");
1072 strcat (node, entry.d_name);
1074 if (statvfs (node, &dev) == 0)
1076 me = xmalloc (sizeof *me);
1077 me->me_devname = xstrdup (dev.f_mntfromname);
1078 me->me_mountdir = xstrdup (dev.f_mntonname);
1079 me->me_mntroot = NULL;
1080 me->me_type = xstrdup (dev.f_fstypename);
1081 me->me_type_malloced = 1;
1082 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1083 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1084 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
1086 /* Add to the linked list. */
1087 *mtail = me;
1088 mtail = &me->me_next;
1091 closedir (dirp);
1093 #endif /* MOUNTED_INTERIX_STATVFS */
1095 *mtail = NULL;
1096 return mount_list;
1099 free_then_fail: _GL_UNUSED_LABEL;
1101 int saved_errno = errno;
1102 *mtail = NULL;
1104 while (mount_list)
1106 me = mount_list->me_next;
1107 free_mount_entry (mount_list);
1108 mount_list = me;
1111 errno = saved_errno;
1112 return NULL;
1116 /* Free a mount entry as returned from read_file_system_list (). */
1118 void
1119 free_mount_entry (struct mount_entry *me)
1121 free (me->me_devname);
1122 free (me->me_mountdir);
1123 free (me->me_mntroot);
1124 if (me->me_type_malloced)
1125 free (me->me_type);
1126 free (me);