execute, spawn-pipe: Make multithread-safe on native Windows.
[gnulib.git] / lib / mountlist.c
blob026d653306aaa415e9fc80a059a75cfefc7ba185
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 "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 #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 /* Don't assume that UNICODE is not defined. */
199 # undef GetDriveType
200 # define GetDriveType GetDriveTypeA
201 # define ME_REMOTE me_remote
202 /* All cygwin mount points include ':' or start with '//'; so it
203 requires a native Windows call to determine remote disks. */
204 static bool
205 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
207 if (fs_name[0] && fs_name[1] == ':')
209 char drive[4];
210 sprintf (drive, "%c:\\", fs_name[0]);
211 switch (GetDriveType (drive))
213 case DRIVE_REMOVABLE:
214 case DRIVE_FIXED:
215 case DRIVE_CDROM:
216 case DRIVE_RAMDISK:
217 return false;
220 return true;
222 #endif
224 #ifndef ME_REMOTE
225 /* A file system is "remote" if its Fs_name contains a ':'
226 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
227 or if it is of any other of the listed types
228 or Fs_name is equal to "-hosts" (used by autofs to mount remote fs).
229 "VM" file systems like prl_fs or vboxsf are not considered remote here. */
230 # define ME_REMOTE(Fs_name, Fs_type) \
231 (strchr (Fs_name, ':') != NULL \
232 || ((Fs_name)[0] == '/' \
233 && (Fs_name)[1] == '/' \
234 && (strcmp (Fs_type, "smbfs") == 0 \
235 || strcmp (Fs_type, "smb3") == 0 \
236 || strcmp (Fs_type, "cifs") == 0)) \
237 || strcmp (Fs_type, "acfs") == 0 \
238 || strcmp (Fs_type, "afs") == 0 \
239 || strcmp (Fs_type, "coda") == 0 \
240 || strcmp (Fs_type, "auristorfs") == 0 \
241 || strcmp (Fs_type, "fhgfs") == 0 \
242 || strcmp (Fs_type, "gpfs") == 0 \
243 || strcmp (Fs_type, "ibrix") == 0 \
244 || strcmp (Fs_type, "ocfs2") == 0 \
245 || strcmp (Fs_type, "vxfs") == 0 \
246 || strcmp ("-hosts", Fs_name) == 0)
247 #endif
249 #if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
251 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
252 static char *
253 fstype_to_string (short int t)
255 switch (t)
257 # ifdef MOUNT_PC
258 case MOUNT_PC:
259 return "pc";
260 # endif
261 # ifdef MOUNT_MFS
262 case MOUNT_MFS:
263 return "mfs";
264 # endif
265 # ifdef MOUNT_LO
266 case MOUNT_LO:
267 return "lo";
268 # endif
269 # ifdef MOUNT_TFS
270 case MOUNT_TFS:
271 return "tfs";
272 # endif
273 # ifdef MOUNT_TMP
274 case MOUNT_TMP:
275 return "tmp";
276 # endif
277 # ifdef MOUNT_UFS
278 case MOUNT_UFS:
279 return "ufs" ;
280 # endif
281 # ifdef MOUNT_NFS
282 case MOUNT_NFS:
283 return "nfs" ;
284 # endif
285 # ifdef MOUNT_MSDOS
286 case MOUNT_MSDOS:
287 return "msdos" ;
288 # endif
289 # ifdef MOUNT_LFS
290 case MOUNT_LFS:
291 return "lfs" ;
292 # endif
293 # ifdef MOUNT_LOFS
294 case MOUNT_LOFS:
295 return "lofs" ;
296 # endif
297 # ifdef MOUNT_FDESC
298 case MOUNT_FDESC:
299 return "fdesc" ;
300 # endif
301 # ifdef MOUNT_PORTAL
302 case MOUNT_PORTAL:
303 return "portal" ;
304 # endif
305 # ifdef MOUNT_NULL
306 case MOUNT_NULL:
307 return "null" ;
308 # endif
309 # ifdef MOUNT_UMAP
310 case MOUNT_UMAP:
311 return "umap" ;
312 # endif
313 # ifdef MOUNT_KERNFS
314 case MOUNT_KERNFS:
315 return "kernfs" ;
316 # endif
317 # ifdef MOUNT_PROCFS
318 case MOUNT_PROCFS:
319 return "procfs" ;
320 # endif
321 # ifdef MOUNT_AFS
322 case MOUNT_AFS:
323 return "afs" ;
324 # endif
325 # ifdef MOUNT_CD9660
326 case MOUNT_CD9660:
327 return "cd9660" ;
328 # endif
329 # ifdef MOUNT_UNION
330 case MOUNT_UNION:
331 return "union" ;
332 # endif
333 # ifdef MOUNT_DEVFS
334 case MOUNT_DEVFS:
335 return "devfs" ;
336 # endif
337 # ifdef MOUNT_EXT2FS
338 case MOUNT_EXT2FS:
339 return "ext2fs" ;
340 # endif
341 default:
342 return "?";
345 # endif
347 static char *
348 fsp_to_string (const struct statfs *fsp)
350 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
351 return (char *) (fsp->f_fstypename);
352 # else
353 return fstype_to_string (fsp->f_type);
354 # endif
357 #endif /* MOUNTED_GETMNTINFO */
359 #ifdef MOUNTED_VMOUNT /* AIX */
360 static char *
361 fstype_to_string (int t)
363 struct vfs_ent *e;
365 e = getvfsbytype (t);
366 if (!e || !e->vfsent_name)
367 return "none";
368 else
369 return e->vfsent_name;
371 #endif /* MOUNTED_VMOUNT */
374 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
376 /* Return the device number from MOUNT_OPTIONS, if possible.
377 Otherwise return (dev_t) -1. */
378 static dev_t
379 dev_from_mount_options (char const *mount_options)
381 /* GNU/Linux allows file system implementations to define their own
382 meaning for "dev=" mount options, so don't trust the meaning
383 here. */
384 # ifndef __linux__
386 static char const dev_pattern[] = ",dev=";
387 char const *devopt = strstr (mount_options, dev_pattern);
389 if (devopt)
391 char const *optval = devopt + sizeof dev_pattern - 1;
392 char *optvalend;
393 unsigned long int dev;
394 errno = 0;
395 dev = strtoul (optval, &optvalend, 16);
396 if (optval != optvalend
397 && (*optvalend == '\0' || *optvalend == ',')
398 && ! (dev == ULONG_MAX && errno == ERANGE)
399 && dev == (dev_t) dev)
400 return dev;
403 # endif
404 (void) mount_options;
405 return -1;
408 #endif
410 #if defined MOUNTED_GETMNTENT1 && (defined __linux__ || defined __ANDROID__) /* GNU/Linux, Android */
412 /* Unescape the paths in mount tables.
413 STR is updated in place. */
415 static void
416 unescape_tab (char *str)
418 size_t i, j = 0;
419 size_t len = strlen (str) + 1;
420 for (i = 0; i < len; i++)
422 if (str[i] == '\\' && (i + 4 < len)
423 && str[i + 1] >= '0' && str[i + 1] <= '3'
424 && str[i + 2] >= '0' && str[i + 2] <= '7'
425 && str[i + 3] >= '0' && str[i + 3] <= '7')
427 str[j++] = (str[i + 1] - '0') * 64 +
428 (str[i + 2] - '0') * 8 +
429 (str[i + 3] - '0');
430 i += 3;
432 else
433 str[j++] = str[i];
437 /* Find the next space in STR, terminate the string there in place,
438 and return that position. Otherwise return NULL. */
440 static char *
441 terminate_at_blank (char *str)
443 char *s = strchr (str, ' ');
444 if (s)
445 *s = '\0';
446 return s;
448 #endif
450 /* Return a list of the currently mounted file systems, or NULL on error.
451 Add each entry to the tail of the list so that they stay in order.
452 If NEED_FS_TYPE is true, ensure that the file system type fields in
453 the returned list are valid. Otherwise, they might not be. */
455 struct mount_entry *
456 read_file_system_list (bool need_fs_type)
458 struct mount_entry *mount_list;
459 struct mount_entry *me;
460 struct mount_entry **mtail = &mount_list;
461 (void) need_fs_type;
463 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
464 also (obsolete) 4.3BSD, SunOS */
466 FILE *fp;
468 # if defined __linux__ || defined __ANDROID__
469 /* Try parsing mountinfo first, as that make device IDs available.
470 Note we could use libmount routines to simplify this parsing a little
471 (and that code is in previous versions of this function), however
472 libmount depends on libselinux which pulls in many dependencies. */
473 char const *mountinfo = "/proc/self/mountinfo";
474 fp = fopen (mountinfo, "re");
475 if (fp != NULL)
477 char *line = NULL;
478 size_t buf_size = 0;
480 while (getline (&line, &buf_size, fp) != -1)
482 unsigned int devmaj, devmin;
483 int rc, mntroot_s;
485 rc = sscanf(line, "%*u " /* id - discarded */
486 "%*u " /* parent - discarded */
487 "%u:%u " /* dev major:minor */
488 "%n", /* mountroot (start) */
489 &devmaj, &devmin,
490 &mntroot_s);
492 if (rc != 2 && rc != 3) /* 3 if %n included in count. */
493 continue;
495 /* find end of MNTROOT. */
496 char *mntroot = line + mntroot_s;
497 char *blank = terminate_at_blank (mntroot);
498 if (! blank)
499 continue;
501 /* find end of TARGET. */
502 char *target = blank + 1;
503 blank = terminate_at_blank (target);
504 if (! blank)
505 continue;
507 /* skip optional fields, terminated by " - " */
508 char *dash = strstr (blank + 1, " - ");
509 if (! dash)
510 continue;
512 /* advance past the " - " separator. */
513 char *fstype = dash + 3;
514 blank = terminate_at_blank (fstype);
515 if (! blank)
516 continue;
518 /* find end of SOURCE. */
519 char *source = blank + 1;
520 if (! terminate_at_blank (source))
521 continue;
523 /* manipulate the sub-strings in place. */
524 unescape_tab (source);
525 unescape_tab (target);
526 unescape_tab (mntroot);
527 unescape_tab (fstype);
529 me = xmalloc (sizeof *me);
531 me->me_devname = xstrdup (source);
532 me->me_mountdir = xstrdup (target);
533 me->me_mntroot = xstrdup (mntroot);
534 me->me_type = xstrdup (fstype);
535 me->me_type_malloced = 1;
536 me->me_dev = makedev (devmaj, devmin);
537 /* we pass "false" for the "Bind" option as that's only
538 significant when the Fs_type is "none" which will not be
539 the case when parsing "/proc/self/mountinfo", and only
540 applies for static /etc/mtab files. */
541 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
542 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
544 /* Add to the linked list. */
545 *mtail = me;
546 mtail = &me->me_next;
549 free (line);
551 if (ferror (fp))
553 int saved_errno = errno;
554 fclose (fp);
555 errno = saved_errno;
556 goto free_then_fail;
559 if (fclose (fp) == EOF)
560 goto free_then_fail;
562 else /* fallback to /proc/self/mounts (/etc/mtab). */
563 # endif /* __linux __ || __ANDROID__ */
565 struct mntent *mnt;
566 char const *table = MOUNTED;
568 fp = setmntent (table, "r");
569 if (fp == NULL)
570 return NULL;
572 while ((mnt = getmntent (fp)))
574 bool bind = hasmntopt (mnt, "bind");
576 me = xmalloc (sizeof *me);
577 me->me_devname = xstrdup (mnt->mnt_fsname);
578 me->me_mountdir = xstrdup (mnt->mnt_dir);
579 me->me_mntroot = NULL;
580 me->me_type = xstrdup (mnt->mnt_type);
581 me->me_type_malloced = 1;
582 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
583 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
584 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
586 /* Add to the linked list. */
587 *mtail = me;
588 mtail = &me->me_next;
591 if (endmntent (fp) == 0)
592 goto free_then_fail;
595 #endif /* MOUNTED_GETMNTENT1. */
597 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
599 struct statfs *fsp;
600 int entries;
602 entries = getmntinfo (&fsp, MNT_NOWAIT);
603 if (entries < 0)
604 return NULL;
605 for (; entries-- > 0; fsp++)
607 char *fs_type = fsp_to_string (fsp);
609 me = xmalloc (sizeof *me);
610 me->me_devname = xstrdup (fsp->f_mntfromname);
611 me->me_mountdir = xstrdup (fsp->f_mntonname);
612 me->me_mntroot = NULL;
613 me->me_type = fs_type;
614 me->me_type_malloced = 0;
615 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
616 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
617 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
619 /* Add to the linked list. */
620 *mtail = me;
621 mtail = &me->me_next;
624 #endif /* MOUNTED_GETMNTINFO */
626 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
628 struct statvfs *fsp;
629 int entries;
631 entries = getmntinfo (&fsp, MNT_NOWAIT);
632 if (entries < 0)
633 return NULL;
634 for (; entries-- > 0; fsp++)
636 me = xmalloc (sizeof *me);
637 me->me_devname = xstrdup (fsp->f_mntfromname);
638 me->me_mountdir = xstrdup (fsp->f_mntonname);
639 me->me_mntroot = NULL;
640 me->me_type = xstrdup (fsp->f_fstypename);
641 me->me_type_malloced = 1;
642 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
643 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
644 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
646 /* Add to the linked list. */
647 *mtail = me;
648 mtail = &me->me_next;
651 #endif /* MOUNTED_GETMNTINFO2 */
653 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
655 /* The next_dev() and fs_stat_dev() system calls give the list of
656 all file systems, including the information returned by statvfs()
657 (fs type, total blocks, free blocks etc.), but without the mount
658 point. But on BeOS all file systems except / are mounted in the
659 rootfs, directly under /.
660 The directory name of the mount point is often, but not always,
661 identical to the volume name of the device.
662 We therefore get the list of subdirectories of /, and the list
663 of all file systems, and match the two lists. */
665 DIR *dirp;
666 struct rootdir_entry
668 char *name;
669 dev_t dev;
670 ino_t ino;
671 struct rootdir_entry *next;
673 struct rootdir_entry *rootdir_list;
674 struct rootdir_entry **rootdir_tail;
675 int32 pos;
676 dev_t dev;
677 fs_info fi;
679 /* All volumes are mounted in the rootfs, directly under /. */
680 rootdir_list = NULL;
681 rootdir_tail = &rootdir_list;
682 dirp = opendir ("/");
683 if (dirp)
685 struct dirent *d;
687 while ((d = readdir (dirp)) != NULL)
689 char *name;
690 struct stat statbuf;
692 if (strcmp (d->d_name, "..") == 0)
693 continue;
695 if (strcmp (d->d_name, ".") == 0)
696 name = xstrdup ("/");
697 else
699 name = xmalloc (1 + strlen (d->d_name) + 1);
700 name[0] = '/';
701 strcpy (name + 1, d->d_name);
704 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
706 struct rootdir_entry *re = xmalloc (sizeof *re);
707 re->name = name;
708 re->dev = statbuf.st_dev;
709 re->ino = statbuf.st_ino;
711 /* Add to the linked list. */
712 *rootdir_tail = re;
713 rootdir_tail = &re->next;
715 else
716 free (name);
718 closedir (dirp);
720 *rootdir_tail = NULL;
722 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
723 if (fs_stat_dev (dev, &fi) >= 0)
725 /* Note: fi.dev == dev. */
726 struct rootdir_entry *re;
728 for (re = rootdir_list; re; re = re->next)
729 if (re->dev == fi.dev && re->ino == fi.root)
730 break;
732 me = xmalloc (sizeof *me);
733 me->me_devname = xstrdup (fi.device_name[0] != '\0'
734 ? fi.device_name : fi.fsh_name);
735 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
736 me->me_mntroot = NULL;
737 me->me_type = xstrdup (fi.fsh_name);
738 me->me_type_malloced = 1;
739 me->me_dev = fi.dev;
740 me->me_dummy = 0;
741 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
743 /* Add to the linked list. */
744 *mtail = me;
745 mtail = &me->me_next;
747 *mtail = NULL;
749 while (rootdir_list != NULL)
751 struct rootdir_entry *re = rootdir_list;
752 rootdir_list = re->next;
753 free (re->name);
754 free (re);
757 #endif /* MOUNTED_FS_STAT_DEV */
759 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
761 int numsys, counter;
762 size_t bufsize;
763 struct statfs *stats;
765 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
766 if (numsys < 0)
767 return NULL;
768 if (SIZE_MAX / sizeof *stats <= numsys)
769 xalloc_die ();
771 bufsize = (1 + numsys) * sizeof *stats;
772 stats = xmalloc (bufsize);
773 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
775 if (numsys < 0)
777 free (stats);
778 return NULL;
781 for (counter = 0; counter < numsys; counter++)
783 me = xmalloc (sizeof *me);
784 me->me_devname = xstrdup (stats[counter].f_mntfromname);
785 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
786 me->me_mntroot = NULL;
787 me->me_type = xstrdup (FS_TYPE (stats[counter]));
788 me->me_type_malloced = 1;
789 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
790 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
791 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
793 /* Add to the linked list. */
794 *mtail = me;
795 mtail = &me->me_next;
798 free (stats);
800 #endif /* MOUNTED_GETFSSTAT */
802 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
804 struct mnttab mnt;
805 char *table = "/etc/mnttab";
806 FILE *fp;
808 fp = fopen (table, "re");
809 if (fp == NULL)
810 return NULL;
812 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
814 me = xmalloc (sizeof *me);
815 me->me_devname = xstrdup (mnt.mt_dev);
816 me->me_mountdir = xstrdup (mnt.mt_filsys);
817 me->me_mntroot = NULL;
818 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
819 me->me_type = "";
820 me->me_type_malloced = 0;
821 if (need_fs_type)
823 struct statfs fsd;
824 char typebuf[FSTYPSZ];
826 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
827 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
829 me->me_type = xstrdup (typebuf);
830 me->me_type_malloced = 1;
833 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
834 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
836 /* Add to the linked list. */
837 *mtail = me;
838 mtail = &me->me_next;
841 if (ferror (fp))
843 /* The last fread() call must have failed. */
844 int saved_errno = errno;
845 fclose (fp);
846 errno = saved_errno;
847 goto free_then_fail;
850 if (fclose (fp) == EOF)
851 goto free_then_fail;
853 #endif /* MOUNTED_FREAD_FSTYP. */
855 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
857 struct extmnttab mnt;
858 const char *table = MNTTAB;
859 FILE *fp;
860 int ret;
862 /* No locking is needed, because the contents of /etc/mnttab is generated
863 by the kernel. */
865 errno = 0;
866 fp = fopen (table, "re");
867 if (fp == NULL)
868 ret = errno;
869 else
871 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
873 me = xmalloc (sizeof *me);
874 me->me_devname = xstrdup (mnt.mnt_special);
875 me->me_mountdir = xstrdup (mnt.mnt_mountp);
876 me->me_mntroot = NULL;
877 me->me_type = xstrdup (mnt.mnt_fstype);
878 me->me_type_malloced = 1;
879 me->me_dummy = MNT_IGNORE (&mnt) != 0;
880 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
881 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
883 /* Add to the linked list. */
884 *mtail = me;
885 mtail = &me->me_next;
888 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
889 /* Here ret = -1 means success, ret >= 0 means failure. */
892 if (0 <= ret)
894 errno = ret;
895 goto free_then_fail;
898 #endif /* MOUNTED_GETEXTMNTENT */
900 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
902 struct mnttab mnt;
903 const char *table = MNTTAB;
904 FILE *fp;
905 int ret;
906 int lockfd = -1;
908 # if defined F_RDLCK && defined F_SETLKW
909 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
910 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
911 for this file name, we should use their macro name instead.
912 (Why not just lock MNTTAB directly? We don't know.) */
913 # ifndef MNTTAB_LOCK
914 # define MNTTAB_LOCK "/etc/.mnttab.lock"
915 # endif
916 lockfd = open (MNTTAB_LOCK, O_RDONLY | O_CLOEXEC);
917 if (0 <= lockfd)
919 struct flock flock;
920 flock.l_type = F_RDLCK;
921 flock.l_whence = SEEK_SET;
922 flock.l_start = 0;
923 flock.l_len = 0;
924 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
925 if (errno != EINTR)
927 int saved_errno = errno;
928 close (lockfd);
929 errno = saved_errno;
930 return NULL;
933 else if (errno != ENOENT)
934 return NULL;
935 # endif
937 errno = 0;
938 fp = fopen (table, "re");
939 if (fp == NULL)
940 ret = errno;
941 else
943 while ((ret = getmntent (fp, &mnt)) == 0)
945 me = xmalloc (sizeof *me);
946 me->me_devname = xstrdup (mnt.mnt_special);
947 me->me_mountdir = xstrdup (mnt.mnt_mountp);
948 me->me_mntroot = NULL;
949 me->me_type = xstrdup (mnt.mnt_fstype);
950 me->me_type_malloced = 1;
951 me->me_dummy = MNT_IGNORE (&mnt) != 0;
952 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
953 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
955 /* Add to the linked list. */
956 *mtail = me;
957 mtail = &me->me_next;
960 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
961 /* Here ret = -1 means success, ret >= 0 means failure. */
964 if (0 <= lockfd && close (lockfd) != 0)
965 ret = errno;
967 if (0 <= ret)
969 errno = ret;
970 goto free_then_fail;
973 #endif /* MOUNTED_GETMNTENT2. */
975 #ifdef MOUNTED_VMOUNT /* AIX */
977 int bufsize;
978 void *entries;
979 char *thisent;
980 struct vmount *vmp;
981 int n_entries;
982 int i;
984 /* Ask how many bytes to allocate for the mounted file system info. */
985 entries = &bufsize;
986 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
987 return NULL;
988 entries = xmalloc (bufsize);
990 /* Get the list of mounted file systems. */
991 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
992 if (n_entries < 0)
994 int saved_errno = errno;
995 free (entries);
996 errno = saved_errno;
997 return NULL;
1000 for (i = 0, thisent = entries;
1001 i < n_entries;
1002 i++, thisent += vmp->vmt_length)
1004 char *options, *ignore;
1006 vmp = (struct vmount *) thisent;
1007 me = xmalloc (sizeof *me);
1008 if (vmp->vmt_flags & MNT_REMOTE)
1010 char *host, *dir;
1012 me->me_remote = 1;
1013 /* Prepend the remote dirname. */
1014 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1015 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1016 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
1017 strcpy (me->me_devname, host);
1018 strcat (me->me_devname, ":");
1019 strcat (me->me_devname, dir);
1021 else
1023 me->me_remote = 0;
1024 me->me_devname = xstrdup (thisent +
1025 vmp->vmt_data[VMT_OBJECT].vmt_off);
1027 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1028 me->me_mntroot = NULL;
1029 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1030 me->me_type_malloced = 1;
1031 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1032 ignore = strstr (options, "ignore");
1033 me->me_dummy = (ignore
1034 && (ignore == options || ignore[-1] == ',')
1035 && (ignore[sizeof "ignore" - 1] == ','
1036 || ignore[sizeof "ignore" - 1] == '\0'));
1037 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
1039 /* Add to the linked list. */
1040 *mtail = me;
1041 mtail = &me->me_next;
1043 free (entries);
1045 #endif /* MOUNTED_VMOUNT. */
1047 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1049 DIR *dirp = opendir ("/dev/fs");
1050 char node[9 + NAME_MAX];
1052 if (!dirp)
1053 goto free_then_fail;
1055 while (1)
1057 struct statvfs dev;
1058 struct dirent entry;
1059 struct dirent *result;
1061 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1062 marked obsolescent in glibc. Use readdir instead. */
1063 if (readdir_r (dirp, &entry, &result) || result == NULL)
1064 break;
1066 strcpy (node, "/dev/fs/");
1067 strcat (node, entry.d_name);
1069 if (statvfs (node, &dev) == 0)
1071 me = xmalloc (sizeof *me);
1072 me->me_devname = xstrdup (dev.f_mntfromname);
1073 me->me_mountdir = xstrdup (dev.f_mntonname);
1074 me->me_mntroot = NULL;
1075 me->me_type = xstrdup (dev.f_fstypename);
1076 me->me_type_malloced = 1;
1077 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1078 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1079 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
1081 /* Add to the linked list. */
1082 *mtail = me;
1083 mtail = &me->me_next;
1086 closedir (dirp);
1088 #endif /* MOUNTED_INTERIX_STATVFS */
1090 *mtail = NULL;
1091 return mount_list;
1094 free_then_fail: _GL_UNUSED_LABEL
1096 int saved_errno = errno;
1097 *mtail = NULL;
1099 while (mount_list)
1101 me = mount_list->me_next;
1102 free_mount_entry (mount_list);
1103 mount_list = me;
1106 errno = saved_errno;
1107 return NULL;
1111 /* Free a mount entry as returned from read_file_system_list (). */
1113 void free_mount_entry (struct mount_entry *me)
1115 free (me->me_devname);
1116 free (me->me_mountdir);
1117 free (me->me_mntroot);
1118 if (me->me_type_malloced)
1119 free (me->me_type);
1120 free (me);