mountlist: Remove support for SVR2.
[gnulib.git] / lib / mountlist.c
blobcd391202107d9ed071e9f3d05fd2707348cec4ea
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_FSTYP /* (obsolete) SVR3 */
101 # include <mnttab.h>
102 # include <sys/fstyp.h>
103 # include <sys/statfs.h>
104 #endif
106 #ifdef MOUNTED_LISTMNTENT /* (obsolete) Cray UNICOS 9 */
107 # include <mntent.h>
108 #endif
110 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
111 # include <sys/mnttab.h>
112 #endif
114 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
115 # include <sys/mnttab.h>
116 #endif
118 #ifdef MOUNTED_VMOUNT /* AIX */
119 # include <fshelp.h>
120 # include <sys/vfs.h>
121 #endif
123 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
124 # include <sys/statvfs.h>
125 # include <dirent.h>
126 #endif
128 #ifdef DOLPHIN
129 /* So special that it's not worth putting this in autoconf. */
130 # undef MOUNTED_FREAD_FSTYP
131 # define MOUNTED_GETMNTTBL
132 #endif
134 #if HAVE_SYS_MNTENT_H
135 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
136 # include <sys/mntent.h>
137 #endif
139 #ifndef HAVE_HASMNTOPT
140 # define hasmntopt(mnt, opt) ((char *) 0)
141 #endif
143 #undef MNT_IGNORE
144 #ifdef MNTOPT_IGNORE
145 # if defined __sun && defined __SVR4
146 /* Solaris defines hasmntopt(struct mnttab *, char *)
147 while it is otherwise hasmntopt(struct mnttab *, const char *). */
148 # define MNT_IGNORE(M) hasmntopt (M, (char *) MNTOPT_IGNORE)
149 # else
150 # define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
151 # endif
152 #else
153 # define MNT_IGNORE(M) 0
154 #endif
156 #if USE_UNLOCKED_IO
157 # include "unlocked-io.h"
158 #endif
160 /* The results of opendir() in this file are not used with dirfd and fchdir,
161 therefore save some unnecessary work in fchdir.c. */
162 #ifdef GNULIB_defined_opendir
163 # undef opendir
164 #endif
165 #ifdef GNULIB_defined_closedir
166 # undef closedir
167 #endif
169 #define ME_DUMMY_0(Fs_name, Fs_type) \
170 (strcmp (Fs_type, "autofs") == 0 \
171 || strcmp (Fs_type, "proc") == 0 \
172 || strcmp (Fs_type, "subfs") == 0 \
173 /* for Linux 2.6/3.x */ \
174 || strcmp (Fs_type, "debugfs") == 0 \
175 || strcmp (Fs_type, "devpts") == 0 \
176 || strcmp (Fs_type, "fusectl") == 0 \
177 || strcmp (Fs_type, "mqueue") == 0 \
178 || strcmp (Fs_type, "rpc_pipefs") == 0 \
179 || strcmp (Fs_type, "sysfs") == 0 \
180 /* FreeBSD, Linux 2.4 */ \
181 || strcmp (Fs_type, "devfs") == 0 \
182 /* for NetBSD 3.0 */ \
183 || strcmp (Fs_type, "kernfs") == 0 \
184 /* for Irix 6.5 */ \
185 || strcmp (Fs_type, "ignore") == 0)
187 /* Historically, we have marked as "dummy" any file system of type "none",
188 but now that programs like du need to know about bind-mounted directories,
189 we grant an exception to any with "bind" in its list of mount options.
190 I.e., those are *not* dummy entries. */
191 #ifdef MOUNTED_GETMNTENT1
192 # define ME_DUMMY(Fs_name, Fs_type, Bind) \
193 (ME_DUMMY_0 (Fs_name, Fs_type) \
194 || (strcmp (Fs_type, "none") == 0 && !Bind))
195 #else
196 # define ME_DUMMY(Fs_name, Fs_type) \
197 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
198 #endif
200 #ifdef __CYGWIN__
201 # include <windows.h>
202 # define ME_REMOTE me_remote
203 /* All cygwin mount points include ':' or start with '//'; so it
204 requires a native Windows call to determine remote disks. */
205 static bool
206 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
208 if (fs_name[0] && fs_name[1] == ':')
210 char drive[4];
211 sprintf (drive, "%c:\\", fs_name[0]);
212 switch (GetDriveType (drive))
214 case DRIVE_REMOVABLE:
215 case DRIVE_FIXED:
216 case DRIVE_CDROM:
217 case DRIVE_RAMDISK:
218 return false;
221 return true;
223 #endif
225 #ifndef ME_REMOTE
226 /* A file system is "remote" if its Fs_name contains a ':'
227 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//')
228 or Fs_name is equal to "-hosts" (used by autofs to mount remote fs). */
229 # define ME_REMOTE(Fs_name, Fs_type) \
230 (strchr (Fs_name, ':') != NULL \
231 || ((Fs_name)[0] == '/' \
232 && (Fs_name)[1] == '/' \
233 && (strcmp (Fs_type, "smbfs") == 0 \
234 || strcmp (Fs_type, "cifs") == 0)) \
235 || (strcmp("-hosts", Fs_name) == 0))
236 #endif
238 #if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
240 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
241 static char *
242 fstype_to_string (short int t)
244 switch (t)
246 # ifdef MOUNT_PC
247 case MOUNT_PC:
248 return "pc";
249 # endif
250 # ifdef MOUNT_MFS
251 case MOUNT_MFS:
252 return "mfs";
253 # endif
254 # ifdef MOUNT_LO
255 case MOUNT_LO:
256 return "lo";
257 # endif
258 # ifdef MOUNT_TFS
259 case MOUNT_TFS:
260 return "tfs";
261 # endif
262 # ifdef MOUNT_TMP
263 case MOUNT_TMP:
264 return "tmp";
265 # endif
266 # ifdef MOUNT_UFS
267 case MOUNT_UFS:
268 return "ufs" ;
269 # endif
270 # ifdef MOUNT_NFS
271 case MOUNT_NFS:
272 return "nfs" ;
273 # endif
274 # ifdef MOUNT_MSDOS
275 case MOUNT_MSDOS:
276 return "msdos" ;
277 # endif
278 # ifdef MOUNT_LFS
279 case MOUNT_LFS:
280 return "lfs" ;
281 # endif
282 # ifdef MOUNT_LOFS
283 case MOUNT_LOFS:
284 return "lofs" ;
285 # endif
286 # ifdef MOUNT_FDESC
287 case MOUNT_FDESC:
288 return "fdesc" ;
289 # endif
290 # ifdef MOUNT_PORTAL
291 case MOUNT_PORTAL:
292 return "portal" ;
293 # endif
294 # ifdef MOUNT_NULL
295 case MOUNT_NULL:
296 return "null" ;
297 # endif
298 # ifdef MOUNT_UMAP
299 case MOUNT_UMAP:
300 return "umap" ;
301 # endif
302 # ifdef MOUNT_KERNFS
303 case MOUNT_KERNFS:
304 return "kernfs" ;
305 # endif
306 # ifdef MOUNT_PROCFS
307 case MOUNT_PROCFS:
308 return "procfs" ;
309 # endif
310 # ifdef MOUNT_AFS
311 case MOUNT_AFS:
312 return "afs" ;
313 # endif
314 # ifdef MOUNT_CD9660
315 case MOUNT_CD9660:
316 return "cd9660" ;
317 # endif
318 # ifdef MOUNT_UNION
319 case MOUNT_UNION:
320 return "union" ;
321 # endif
322 # ifdef MOUNT_DEVFS
323 case MOUNT_DEVFS:
324 return "devfs" ;
325 # endif
326 # ifdef MOUNT_EXT2FS
327 case MOUNT_EXT2FS:
328 return "ext2fs" ;
329 # endif
330 default:
331 return "?";
334 # endif
336 static char *
337 fsp_to_string (const struct statfs *fsp)
339 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
340 return (char *) (fsp->f_fstypename);
341 # else
342 return fstype_to_string (fsp->f_type);
343 # endif
346 #endif /* MOUNTED_GETMNTINFO */
348 #ifdef MOUNTED_VMOUNT /* AIX */
349 static char *
350 fstype_to_string (int t)
352 struct vfs_ent *e;
354 e = getvfsbytype (t);
355 if (!e || !e->vfsent_name)
356 return "none";
357 else
358 return e->vfsent_name;
360 #endif /* MOUNTED_VMOUNT */
363 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
365 /* Return the device number from MOUNT_OPTIONS, if possible.
366 Otherwise return (dev_t) -1. */
367 static dev_t
368 dev_from_mount_options (char const *mount_options)
370 /* GNU/Linux allows file system implementations to define their own
371 meaning for "dev=" mount options, so don't trust the meaning
372 here. */
373 # ifndef __linux__
375 static char const dev_pattern[] = ",dev=";
376 char const *devopt = strstr (mount_options, dev_pattern);
378 if (devopt)
380 char const *optval = devopt + sizeof dev_pattern - 1;
381 char *optvalend;
382 unsigned long int dev;
383 errno = 0;
384 dev = strtoul (optval, &optvalend, 16);
385 if (optval != optvalend
386 && (*optvalend == '\0' || *optvalend == ',')
387 && ! (dev == ULONG_MAX && errno == ERANGE)
388 && dev == (dev_t) dev)
389 return dev;
392 # endif
393 (void) mount_options;
394 return -1;
397 #endif
399 #if defined MOUNTED_GETMNTENT1 && defined __linux__ /* GNU/Linux, Android */
401 /* Unescape the paths in mount tables.
402 STR is updated in place. */
404 static void
405 unescape_tab (char *str)
407 size_t i, j = 0;
408 size_t len = strlen (str) + 1;
409 for (i = 0; i < len; i++)
411 if (str[i] == '\\' && (i + 4 < len)
412 && str[i + 1] >= '0' && str[i + 1] <= '3'
413 && str[i + 2] >= '0' && str[i + 2] <= '7'
414 && str[i + 3] >= '0' && str[i + 3] <= '7')
416 str[j++] = (str[i + 1] - '0') * 64 +
417 (str[i + 2] - '0') * 8 +
418 (str[i + 3] - '0');
419 i += 3;
421 else
422 str[j++] = str[i];
425 #endif
427 /* Return a list of the currently mounted file systems, or NULL on error.
428 Add each entry to the tail of the list so that they stay in order.
429 If NEED_FS_TYPE is true, ensure that the file system type fields in
430 the returned list are valid. Otherwise, they might not be. */
432 struct mount_entry *
433 read_file_system_list (bool need_fs_type)
435 struct mount_entry *mount_list;
436 struct mount_entry *me;
437 struct mount_entry **mtail = &mount_list;
438 (void) need_fs_type;
440 #ifdef MOUNTED_LISTMNTENT /* (obsolete) Cray UNICOS 9 */
442 struct tabmntent *mntlist, *p;
443 struct mntent *mnt;
445 /* the third and fourth arguments could be used to filter mounts,
446 but Crays doesn't seem to have any mounts that we want to
447 remove. Specifically, automount create normal NFS mounts.
450 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
451 return NULL;
452 for (p = mntlist; p; p = p->next)
454 mnt = p->ment;
455 me = xmalloc (sizeof *me);
456 me->me_devname = xstrdup (mnt->mnt_fsname);
457 me->me_mountdir = xstrdup (mnt->mnt_dir);
458 me->me_mntroot = NULL;
459 me->me_type = xstrdup (mnt->mnt_type);
460 me->me_type_malloced = 1;
461 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
462 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
463 me->me_dev = -1;
464 *mtail = me;
465 mtail = &me->me_next;
467 freemntlist (mntlist);
469 #endif
471 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
472 also (obsolete) 4.3BSD, SunOS, Dynix */
474 FILE *fp;
476 # ifdef __linux__
477 /* Try parsing mountinfo first, as that make device IDs available.
478 Note we could use libmount routines to simplify this parsing a little
479 (and that code is in previous versions of this function), however
480 libmount depends on libselinux which pulls in many dependencies. */
481 char const *mountinfo = "/proc/self/mountinfo";
482 fp = fopen (mountinfo, "r");
483 if (fp != NULL)
485 char *line = NULL;
486 size_t buf_size = 0;
488 while (getline (&line, &buf_size, fp) != -1)
490 unsigned int devmaj, devmin;
491 int target_s, target_e, type_s, type_e;
492 int source_s, source_e, mntroot_s, mntroot_e;
493 char test;
494 char *dash;
495 int rc;
497 rc = sscanf(line, "%*u " /* id - discarded */
498 "%*u " /* parent - discarded */
499 "%u:%u " /* dev major:minor */
500 "%n%*s%n " /* mountroot */
501 "%n%*s%n" /* target, start and end */
502 "%c", /* more data... */
503 &devmaj, &devmin,
504 &mntroot_s, &mntroot_e,
505 &target_s, &target_e,
506 &test);
508 if (rc != 3 && rc != 7) /* 7 if %n included in count. */
509 continue;
511 /* skip optional fields, terminated by " - " */
512 dash = strstr (line + target_e, " - ");
513 if (! dash)
514 continue;
516 rc = sscanf(dash, " - "
517 "%n%*s%n " /* FS type, start and end */
518 "%n%*s%n " /* source, start and end */
519 "%c", /* more data... */
520 &type_s, &type_e,
521 &source_s, &source_e,
522 &test);
523 if (rc != 1 && rc != 5) /* 5 if %n included in count. */
524 continue;
526 /* manipulate the sub-strings in place. */
527 line[mntroot_e] = '\0';
528 line[target_e] = '\0';
529 dash[type_e] = '\0';
530 dash[source_e] = '\0';
531 unescape_tab (dash + source_s);
532 unescape_tab (line + target_s);
533 unescape_tab (line + mntroot_s);
535 me = xmalloc (sizeof *me);
537 me->me_devname = xstrdup (dash + source_s);
538 me->me_mountdir = xstrdup (line + target_s);
539 me->me_mntroot = xstrdup (line + mntroot_s);
540 me->me_type = xstrdup (dash + type_s);
541 me->me_type_malloced = 1;
542 me->me_dev = makedev (devmaj, devmin);
543 /* we pass "false" for the "Bind" option as that's only
544 significant when the Fs_type is "none" which will not be
545 the case when parsing "/proc/self/mountinfo", and only
546 applies for static /etc/mtab files. */
547 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
548 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
550 /* Add to the linked list. */
551 *mtail = me;
552 mtail = &me->me_next;
555 free (line);
557 if (ferror (fp))
559 int saved_errno = errno;
560 fclose (fp);
561 errno = saved_errno;
562 goto free_then_fail;
565 if (fclose (fp) == EOF)
566 goto free_then_fail;
568 else /* fallback to /proc/self/mounts (/etc/mtab). */
569 # endif /* __linux __ */
571 struct mntent *mnt;
572 char const *table = MOUNTED;
574 fp = setmntent (table, "r");
575 if (fp == NULL)
576 return NULL;
578 while ((mnt = getmntent (fp)))
580 bool bind = hasmntopt (mnt, "bind");
582 me = xmalloc (sizeof *me);
583 me->me_devname = xstrdup (mnt->mnt_fsname);
584 me->me_mountdir = xstrdup (mnt->mnt_dir);
585 me->me_mntroot = NULL;
586 me->me_type = xstrdup (mnt->mnt_type);
587 me->me_type_malloced = 1;
588 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
589 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
590 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
592 /* Add to the linked list. */
593 *mtail = me;
594 mtail = &me->me_next;
597 if (endmntent (fp) == 0)
598 goto free_then_fail;
601 #endif /* MOUNTED_GETMNTENT1. */
603 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
605 struct statfs *fsp;
606 int entries;
608 entries = getmntinfo (&fsp, MNT_NOWAIT);
609 if (entries < 0)
610 return NULL;
611 for (; entries-- > 0; fsp++)
613 char *fs_type = fsp_to_string (fsp);
615 me = xmalloc (sizeof *me);
616 me->me_devname = xstrdup (fsp->f_mntfromname);
617 me->me_mountdir = xstrdup (fsp->f_mntonname);
618 me->me_mntroot = NULL;
619 me->me_type = fs_type;
620 me->me_type_malloced = 0;
621 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
622 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
623 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
625 /* Add to the linked list. */
626 *mtail = me;
627 mtail = &me->me_next;
630 #endif /* MOUNTED_GETMNTINFO */
632 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
634 struct statvfs *fsp;
635 int entries;
637 entries = getmntinfo (&fsp, MNT_NOWAIT);
638 if (entries < 0)
639 return NULL;
640 for (; entries-- > 0; fsp++)
642 me = xmalloc (sizeof *me);
643 me->me_devname = xstrdup (fsp->f_mntfromname);
644 me->me_mountdir = xstrdup (fsp->f_mntonname);
645 me->me_mntroot = NULL;
646 me->me_type = xstrdup (fsp->f_fstypename);
647 me->me_type_malloced = 1;
648 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
649 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
650 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
652 /* Add to the linked list. */
653 *mtail = me;
654 mtail = &me->me_next;
657 #endif /* MOUNTED_GETMNTINFO2 */
659 #ifdef MOUNTED_GETMNT /* (obsolete) Ultrix */
661 int offset = 0;
662 int val;
663 struct fs_data fsd;
665 while (errno = 0,
666 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
667 (char *) 0)))
669 me = xmalloc (sizeof *me);
670 me->me_devname = xstrdup (fsd.fd_req.devname);
671 me->me_mountdir = xstrdup (fsd.fd_req.path);
672 me->me_mntroot = NULL;
673 me->me_type = gt_names[fsd.fd_req.fstype];
674 me->me_type_malloced = 0;
675 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
676 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
677 me->me_dev = fsd.fd_req.dev;
679 /* Add to the linked list. */
680 *mtail = me;
681 mtail = &me->me_next;
683 if (val < 0)
684 goto free_then_fail;
686 #endif /* MOUNTED_GETMNT. */
688 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
690 /* The next_dev() and fs_stat_dev() system calls give the list of
691 all file systems, including the information returned by statvfs()
692 (fs type, total blocks, free blocks etc.), but without the mount
693 point. But on BeOS all file systems except / are mounted in the
694 rootfs, directly under /.
695 The directory name of the mount point is often, but not always,
696 identical to the volume name of the device.
697 We therefore get the list of subdirectories of /, and the list
698 of all file systems, and match the two lists. */
700 DIR *dirp;
701 struct rootdir_entry
703 char *name;
704 dev_t dev;
705 ino_t ino;
706 struct rootdir_entry *next;
708 struct rootdir_entry *rootdir_list;
709 struct rootdir_entry **rootdir_tail;
710 int32 pos;
711 dev_t dev;
712 fs_info fi;
714 /* All volumes are mounted in the rootfs, directly under /. */
715 rootdir_list = NULL;
716 rootdir_tail = &rootdir_list;
717 dirp = opendir ("/");
718 if (dirp)
720 struct dirent *d;
722 while ((d = readdir (dirp)) != NULL)
724 char *name;
725 struct stat statbuf;
727 if (strcmp (d->d_name, "..") == 0)
728 continue;
730 if (strcmp (d->d_name, ".") == 0)
731 name = xstrdup ("/");
732 else
734 name = xmalloc (1 + strlen (d->d_name) + 1);
735 name[0] = '/';
736 strcpy (name + 1, d->d_name);
739 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
741 struct rootdir_entry *re = xmalloc (sizeof *re);
742 re->name = name;
743 re->dev = statbuf.st_dev;
744 re->ino = statbuf.st_ino;
746 /* Add to the linked list. */
747 *rootdir_tail = re;
748 rootdir_tail = &re->next;
750 else
751 free (name);
753 closedir (dirp);
755 *rootdir_tail = NULL;
757 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
758 if (fs_stat_dev (dev, &fi) >= 0)
760 /* Note: fi.dev == dev. */
761 struct rootdir_entry *re;
763 for (re = rootdir_list; re; re = re->next)
764 if (re->dev == fi.dev && re->ino == fi.root)
765 break;
767 me = xmalloc (sizeof *me);
768 me->me_devname = xstrdup (fi.device_name[0] != '\0'
769 ? fi.device_name : fi.fsh_name);
770 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
771 me->me_mntroot = NULL;
772 me->me_type = xstrdup (fi.fsh_name);
773 me->me_type_malloced = 1;
774 me->me_dev = fi.dev;
775 me->me_dummy = 0;
776 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
778 /* Add to the linked list. */
779 *mtail = me;
780 mtail = &me->me_next;
782 *mtail = NULL;
784 while (rootdir_list != NULL)
786 struct rootdir_entry *re = rootdir_list;
787 rootdir_list = re->next;
788 free (re->name);
789 free (re);
792 #endif /* MOUNTED_FS_STAT_DEV */
794 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
796 int numsys, counter;
797 size_t bufsize;
798 struct statfs *stats;
800 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
801 if (numsys < 0)
802 return NULL;
803 if (SIZE_MAX / sizeof *stats <= numsys)
804 xalloc_die ();
806 bufsize = (1 + numsys) * sizeof *stats;
807 stats = xmalloc (bufsize);
808 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
810 if (numsys < 0)
812 free (stats);
813 return NULL;
816 for (counter = 0; counter < numsys; counter++)
818 me = xmalloc (sizeof *me);
819 me->me_devname = xstrdup (stats[counter].f_mntfromname);
820 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
821 me->me_mntroot = NULL;
822 me->me_type = xstrdup (FS_TYPE (stats[counter]));
823 me->me_type_malloced = 1;
824 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
825 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
826 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
828 /* Add to the linked list. */
829 *mtail = me;
830 mtail = &me->me_next;
833 free (stats);
835 #endif /* MOUNTED_GETFSSTAT */
837 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
839 struct mnttab mnt;
840 char *table = "/etc/mnttab";
841 FILE *fp;
843 fp = fopen (table, "r");
844 if (fp == NULL)
845 return NULL;
847 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
849 me = xmalloc (sizeof *me);
850 me->me_devname = xstrdup (mnt.mt_dev);
851 me->me_mountdir = xstrdup (mnt.mt_filsys);
852 me->me_mntroot = NULL;
853 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
854 me->me_type = "";
855 me->me_type_malloced = 0;
856 if (need_fs_type)
858 struct statfs fsd;
859 char typebuf[FSTYPSZ];
861 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
862 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
864 me->me_type = xstrdup (typebuf);
865 me->me_type_malloced = 1;
868 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
869 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
871 /* Add to the linked list. */
872 *mtail = me;
873 mtail = &me->me_next;
876 if (ferror (fp))
878 /* The last fread() call must have failed. */
879 int saved_errno = errno;
880 fclose (fp);
881 errno = saved_errno;
882 goto free_then_fail;
885 if (fclose (fp) == EOF)
886 goto free_then_fail;
888 #endif /* MOUNTED_FREAD_FSTYP. */
890 #ifdef MOUNTED_GETMNTTBL /* (obsolete) DolphinOS */
892 struct mntent **mnttbl = getmnttbl (), **ent;
893 for (ent = mnttbl; *ent; ent++)
895 me = xmalloc (sizeof *me);
896 me->me_devname = xstrdup ((*ent)->mt_resource);
897 me->me_mountdir = xstrdup ((*ent)->mt_directory);
898 me->me_mntroot = NULL;
899 me->me_type = xstrdup ((*ent)->mt_fstype);
900 me->me_type_malloced = 1;
901 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
902 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
903 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
905 /* Add to the linked list. */
906 *mtail = me;
907 mtail = &me->me_next;
909 endmnttbl ();
911 #endif /* MOUNTED_GETMNTTBL */
913 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
915 struct extmnttab mnt;
916 const char *table = MNTTAB;
917 FILE *fp;
918 int ret;
920 /* No locking is needed, because the contents of /etc/mnttab is generated
921 by the kernel. */
923 errno = 0;
924 fp = fopen (table, "r");
925 if (fp == NULL)
926 ret = errno;
927 else
929 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
931 me = xmalloc (sizeof *me);
932 me->me_devname = xstrdup (mnt.mnt_special);
933 me->me_mountdir = xstrdup (mnt.mnt_mountp);
934 me->me_mntroot = NULL;
935 me->me_type = xstrdup (mnt.mnt_fstype);
936 me->me_type_malloced = 1;
937 me->me_dummy = MNT_IGNORE (&mnt) != 0;
938 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
939 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
941 /* Add to the linked list. */
942 *mtail = me;
943 mtail = &me->me_next;
946 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
947 /* Here ret = -1 means success, ret >= 0 means failure. */
950 if (0 <= ret)
952 errno = ret;
953 goto free_then_fail;
956 #endif /* MOUNTED_GETMNTTBL */
958 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
960 struct mnttab mnt;
961 const char *table = MNTTAB;
962 FILE *fp;
963 int ret;
964 int lockfd = -1;
966 # if defined F_RDLCK && defined F_SETLKW
967 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
968 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
969 for this file name, we should use their macro name instead.
970 (Why not just lock MNTTAB directly? We don't know.) */
971 # ifndef MNTTAB_LOCK
972 # define MNTTAB_LOCK "/etc/.mnttab.lock"
973 # endif
974 lockfd = open (MNTTAB_LOCK, O_RDONLY);
975 if (0 <= lockfd)
977 struct flock flock;
978 flock.l_type = F_RDLCK;
979 flock.l_whence = SEEK_SET;
980 flock.l_start = 0;
981 flock.l_len = 0;
982 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
983 if (errno != EINTR)
985 int saved_errno = errno;
986 close (lockfd);
987 errno = saved_errno;
988 return NULL;
991 else if (errno != ENOENT)
992 return NULL;
993 # endif
995 errno = 0;
996 fp = fopen (table, "r");
997 if (fp == NULL)
998 ret = errno;
999 else
1001 while ((ret = getmntent (fp, &mnt)) == 0)
1003 me = xmalloc (sizeof *me);
1004 me->me_devname = xstrdup (mnt.mnt_special);
1005 me->me_mountdir = xstrdup (mnt.mnt_mountp);
1006 me->me_mntroot = NULL;
1007 me->me_type = xstrdup (mnt.mnt_fstype);
1008 me->me_type_malloced = 1;
1009 me->me_dummy = MNT_IGNORE (&mnt) != 0;
1010 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1011 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
1013 /* Add to the linked list. */
1014 *mtail = me;
1015 mtail = &me->me_next;
1018 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
1019 /* Here ret = -1 means success, ret >= 0 means failure. */
1022 if (0 <= lockfd && close (lockfd) != 0)
1023 ret = errno;
1025 if (0 <= ret)
1027 errno = ret;
1028 goto free_then_fail;
1031 #endif /* MOUNTED_GETMNTENT2. */
1033 #ifdef MOUNTED_VMOUNT /* AIX */
1035 int bufsize;
1036 void *entries;
1037 char *thisent;
1038 struct vmount *vmp;
1039 int n_entries;
1040 int i;
1042 /* Ask how many bytes to allocate for the mounted file system info. */
1043 entries = &bufsize;
1044 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
1045 return NULL;
1046 entries = xmalloc (bufsize);
1048 /* Get the list of mounted file systems. */
1049 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
1050 if (n_entries < 0)
1052 int saved_errno = errno;
1053 free (entries);
1054 errno = saved_errno;
1055 return NULL;
1058 for (i = 0, thisent = entries;
1059 i < n_entries;
1060 i++, thisent += vmp->vmt_length)
1062 char *options, *ignore;
1064 vmp = (struct vmount *) thisent;
1065 me = xmalloc (sizeof *me);
1066 if (vmp->vmt_flags & MNT_REMOTE)
1068 char *host, *dir;
1070 me->me_remote = 1;
1071 /* Prepend the remote dirname. */
1072 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1073 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1074 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
1075 strcpy (me->me_devname, host);
1076 strcat (me->me_devname, ":");
1077 strcat (me->me_devname, dir);
1079 else
1081 me->me_remote = 0;
1082 me->me_devname = xstrdup (thisent +
1083 vmp->vmt_data[VMT_OBJECT].vmt_off);
1085 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1086 me->me_mntroot = NULL;
1087 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1088 me->me_type_malloced = 1;
1089 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1090 ignore = strstr (options, "ignore");
1091 me->me_dummy = (ignore
1092 && (ignore == options || ignore[-1] == ',')
1093 && (ignore[sizeof "ignore" - 1] == ','
1094 || ignore[sizeof "ignore" - 1] == '\0'));
1095 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
1097 /* Add to the linked list. */
1098 *mtail = me;
1099 mtail = &me->me_next;
1101 free (entries);
1103 #endif /* MOUNTED_VMOUNT. */
1105 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1107 DIR *dirp = opendir ("/dev/fs");
1108 char node[9 + NAME_MAX];
1110 if (!dirp)
1111 goto free_then_fail;
1113 while (1)
1115 struct statvfs dev;
1116 struct dirent entry;
1117 struct dirent *result;
1119 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1120 marked obsolescent in glibc. Use readdir instead. */
1121 if (readdir_r (dirp, &entry, &result) || result == NULL)
1122 break;
1124 strcpy (node, "/dev/fs/");
1125 strcat (node, entry.d_name);
1127 if (statvfs (node, &dev) == 0)
1129 me = xmalloc (sizeof *me);
1130 me->me_devname = xstrdup (dev.f_mntfromname);
1131 me->me_mountdir = xstrdup (dev.f_mntonname);
1132 me->me_mntroot = NULL;
1133 me->me_type = xstrdup (dev.f_fstypename);
1134 me->me_type_malloced = 1;
1135 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1136 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1137 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
1139 /* Add to the linked list. */
1140 *mtail = me;
1141 mtail = &me->me_next;
1144 closedir (dirp);
1146 #endif /* MOUNTED_INTERIX_STATVFS */
1148 *mtail = NULL;
1149 return mount_list;
1152 free_then_fail: _GL_UNUSED_LABEL
1154 int saved_errno = errno;
1155 *mtail = NULL;
1157 while (mount_list)
1159 me = mount_list->me_next;
1160 free_mount_entry (mount_list);
1161 mount_list = me;
1164 errno = saved_errno;
1165 return NULL;
1169 /* Free a mount entry as returned from read_file_system_list (). */
1171 void free_mount_entry (struct mount_entry *me)
1173 free (me->me_devname);
1174 free (me->me_mountdir);
1175 free (me->me_mntroot);
1176 if (me->me_type_malloced)
1177 free (me->me_type);
1178 free (me);