uchar C++ tests: Fix build error on FreeBSD 12.
[gnulib.git] / lib / mountlist.c
blobca1be63f21ec44f5bee8b1e5400ba391005c5934
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 type (afs or auristorfs)
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, "smb3") == 0 \
235 || strcmp (Fs_type, "cifs") == 0)) \
236 || strcmp (Fs_type, "afs") == 0 \
237 || strcmp (Fs_type, "auristorfs") == 0 \
238 || strcmp ("-hosts", Fs_name) == 0)
239 #endif
241 #if MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
243 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
244 static char *
245 fstype_to_string (short int t)
247 switch (t)
249 # ifdef MOUNT_PC
250 case MOUNT_PC:
251 return "pc";
252 # endif
253 # ifdef MOUNT_MFS
254 case MOUNT_MFS:
255 return "mfs";
256 # endif
257 # ifdef MOUNT_LO
258 case MOUNT_LO:
259 return "lo";
260 # endif
261 # ifdef MOUNT_TFS
262 case MOUNT_TFS:
263 return "tfs";
264 # endif
265 # ifdef MOUNT_TMP
266 case MOUNT_TMP:
267 return "tmp";
268 # endif
269 # ifdef MOUNT_UFS
270 case MOUNT_UFS:
271 return "ufs" ;
272 # endif
273 # ifdef MOUNT_NFS
274 case MOUNT_NFS:
275 return "nfs" ;
276 # endif
277 # ifdef MOUNT_MSDOS
278 case MOUNT_MSDOS:
279 return "msdos" ;
280 # endif
281 # ifdef MOUNT_LFS
282 case MOUNT_LFS:
283 return "lfs" ;
284 # endif
285 # ifdef MOUNT_LOFS
286 case MOUNT_LOFS:
287 return "lofs" ;
288 # endif
289 # ifdef MOUNT_FDESC
290 case MOUNT_FDESC:
291 return "fdesc" ;
292 # endif
293 # ifdef MOUNT_PORTAL
294 case MOUNT_PORTAL:
295 return "portal" ;
296 # endif
297 # ifdef MOUNT_NULL
298 case MOUNT_NULL:
299 return "null" ;
300 # endif
301 # ifdef MOUNT_UMAP
302 case MOUNT_UMAP:
303 return "umap" ;
304 # endif
305 # ifdef MOUNT_KERNFS
306 case MOUNT_KERNFS:
307 return "kernfs" ;
308 # endif
309 # ifdef MOUNT_PROCFS
310 case MOUNT_PROCFS:
311 return "procfs" ;
312 # endif
313 # ifdef MOUNT_AFS
314 case MOUNT_AFS:
315 return "afs" ;
316 # endif
317 # ifdef MOUNT_CD9660
318 case MOUNT_CD9660:
319 return "cd9660" ;
320 # endif
321 # ifdef MOUNT_UNION
322 case MOUNT_UNION:
323 return "union" ;
324 # endif
325 # ifdef MOUNT_DEVFS
326 case MOUNT_DEVFS:
327 return "devfs" ;
328 # endif
329 # ifdef MOUNT_EXT2FS
330 case MOUNT_EXT2FS:
331 return "ext2fs" ;
332 # endif
333 default:
334 return "?";
337 # endif
339 static char *
340 fsp_to_string (const struct statfs *fsp)
342 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
343 return (char *) (fsp->f_fstypename);
344 # else
345 return fstype_to_string (fsp->f_type);
346 # endif
349 #endif /* MOUNTED_GETMNTINFO */
351 #ifdef MOUNTED_VMOUNT /* AIX */
352 static char *
353 fstype_to_string (int t)
355 struct vfs_ent *e;
357 e = getvfsbytype (t);
358 if (!e || !e->vfsent_name)
359 return "none";
360 else
361 return e->vfsent_name;
363 #endif /* MOUNTED_VMOUNT */
366 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
368 /* Return the device number from MOUNT_OPTIONS, if possible.
369 Otherwise return (dev_t) -1. */
370 static dev_t
371 dev_from_mount_options (char const *mount_options)
373 /* GNU/Linux allows file system implementations to define their own
374 meaning for "dev=" mount options, so don't trust the meaning
375 here. */
376 # ifndef __linux__
378 static char const dev_pattern[] = ",dev=";
379 char const *devopt = strstr (mount_options, dev_pattern);
381 if (devopt)
383 char const *optval = devopt + sizeof dev_pattern - 1;
384 char *optvalend;
385 unsigned long int dev;
386 errno = 0;
387 dev = strtoul (optval, &optvalend, 16);
388 if (optval != optvalend
389 && (*optvalend == '\0' || *optvalend == ',')
390 && ! (dev == ULONG_MAX && errno == ERANGE)
391 && dev == (dev_t) dev)
392 return dev;
395 # endif
396 (void) mount_options;
397 return -1;
400 #endif
402 #if defined MOUNTED_GETMNTENT1 && (defined __linux__ || defined __ANDROID__) /* GNU/Linux, Android */
404 /* Unescape the paths in mount tables.
405 STR is updated in place. */
407 static void
408 unescape_tab (char *str)
410 size_t i, j = 0;
411 size_t len = strlen (str) + 1;
412 for (i = 0; i < len; i++)
414 if (str[i] == '\\' && (i + 4 < len)
415 && str[i + 1] >= '0' && str[i + 1] <= '3'
416 && str[i + 2] >= '0' && str[i + 2] <= '7'
417 && str[i + 3] >= '0' && str[i + 3] <= '7')
419 str[j++] = (str[i + 1] - '0') * 64 +
420 (str[i + 2] - '0') * 8 +
421 (str[i + 3] - '0');
422 i += 3;
424 else
425 str[j++] = str[i];
429 /* Find the next space in STR, terminate the string there in place,
430 and return that position. Otherwise return NULL. */
432 static char *
433 terminate_at_blank (char *str)
435 char *s = strchr (str, ' ');
436 if (s)
437 *s = '\0';
438 return s;
440 #endif
442 /* Return a list of the currently mounted file systems, or NULL on error.
443 Add each entry to the tail of the list so that they stay in order.
444 If NEED_FS_TYPE is true, ensure that the file system type fields in
445 the returned list are valid. Otherwise, they might not be. */
447 struct mount_entry *
448 read_file_system_list (bool need_fs_type)
450 struct mount_entry *mount_list;
451 struct mount_entry *me;
452 struct mount_entry **mtail = &mount_list;
453 (void) need_fs_type;
455 #ifdef MOUNTED_GETMNTENT1 /* glibc, HP-UX, IRIX, Cygwin, Android,
456 also (obsolete) 4.3BSD, SunOS */
458 FILE *fp;
460 # if defined __linux__ || defined __ANDROID__
461 /* Try parsing mountinfo first, as that make device IDs available.
462 Note we could use libmount routines to simplify this parsing a little
463 (and that code is in previous versions of this function), however
464 libmount depends on libselinux which pulls in many dependencies. */
465 char const *mountinfo = "/proc/self/mountinfo";
466 fp = fopen (mountinfo, "re");
467 if (fp != NULL)
469 char *line = NULL;
470 size_t buf_size = 0;
472 while (getline (&line, &buf_size, fp) != -1)
474 unsigned int devmaj, devmin;
475 int rc, mntroot_s;
477 rc = sscanf(line, "%*u " /* id - discarded */
478 "%*u " /* parent - discarded */
479 "%u:%u " /* dev major:minor */
480 "%n", /* mountroot (start) */
481 &devmaj, &devmin,
482 &mntroot_s);
484 if (rc != 2 && rc != 3) /* 3 if %n included in count. */
485 continue;
487 /* find end of MNTROOT. */
488 char *mntroot = line + mntroot_s;
489 char *blank = terminate_at_blank (mntroot);
490 if (! blank)
491 continue;
493 /* find end of TARGET. */
494 char *target = blank + 1;
495 blank = terminate_at_blank (target);
496 if (! blank)
497 continue;
499 /* skip optional fields, terminated by " - " */
500 char *dash = strstr (blank + 1, " - ");
501 if (! dash)
502 continue;
504 /* advance past the " - " separator. */
505 char *fstype = dash + 3;
506 blank = terminate_at_blank (fstype);
507 if (! blank)
508 continue;
510 /* find end of SOURCE. */
511 char *source = blank + 1;
512 if (! terminate_at_blank (source))
513 continue;
515 /* manipulate the sub-strings in place. */
516 unescape_tab (source);
517 unescape_tab (target);
518 unescape_tab (mntroot);
519 unescape_tab (fstype);
521 me = xmalloc (sizeof *me);
523 me->me_devname = xstrdup (source);
524 me->me_mountdir = xstrdup (target);
525 me->me_mntroot = xstrdup (mntroot);
526 me->me_type = xstrdup (fstype);
527 me->me_type_malloced = 1;
528 me->me_dev = makedev (devmaj, devmin);
529 /* we pass "false" for the "Bind" option as that's only
530 significant when the Fs_type is "none" which will not be
531 the case when parsing "/proc/self/mountinfo", and only
532 applies for static /etc/mtab files. */
533 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, false);
534 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
536 /* Add to the linked list. */
537 *mtail = me;
538 mtail = &me->me_next;
541 free (line);
543 if (ferror (fp))
545 int saved_errno = errno;
546 fclose (fp);
547 errno = saved_errno;
548 goto free_then_fail;
551 if (fclose (fp) == EOF)
552 goto free_then_fail;
554 else /* fallback to /proc/self/mounts (/etc/mtab). */
555 # endif /* __linux __ || __ANDROID__ */
557 struct mntent *mnt;
558 char const *table = MOUNTED;
560 fp = setmntent (table, "r");
561 if (fp == NULL)
562 return NULL;
564 while ((mnt = getmntent (fp)))
566 bool bind = hasmntopt (mnt, "bind");
568 me = xmalloc (sizeof *me);
569 me->me_devname = xstrdup (mnt->mnt_fsname);
570 me->me_mountdir = xstrdup (mnt->mnt_dir);
571 me->me_mntroot = NULL;
572 me->me_type = xstrdup (mnt->mnt_type);
573 me->me_type_malloced = 1;
574 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, bind);
575 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
576 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
578 /* Add to the linked list. */
579 *mtail = me;
580 mtail = &me->me_next;
583 if (endmntent (fp) == 0)
584 goto free_then_fail;
587 #endif /* MOUNTED_GETMNTENT1. */
589 #ifdef MOUNTED_GETMNTINFO /* Mac OS X, FreeBSD, OpenBSD, also (obsolete) 4.4BSD */
591 struct statfs *fsp;
592 int entries;
594 entries = getmntinfo (&fsp, MNT_NOWAIT);
595 if (entries < 0)
596 return NULL;
597 for (; entries-- > 0; fsp++)
599 char *fs_type = fsp_to_string (fsp);
601 me = xmalloc (sizeof *me);
602 me->me_devname = xstrdup (fsp->f_mntfromname);
603 me->me_mountdir = xstrdup (fsp->f_mntonname);
604 me->me_mntroot = NULL;
605 me->me_type = fs_type;
606 me->me_type_malloced = 0;
607 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
608 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
609 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
611 /* Add to the linked list. */
612 *mtail = me;
613 mtail = &me->me_next;
616 #endif /* MOUNTED_GETMNTINFO */
618 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD, Minix */
620 struct statvfs *fsp;
621 int entries;
623 entries = getmntinfo (&fsp, MNT_NOWAIT);
624 if (entries < 0)
625 return NULL;
626 for (; entries-- > 0; fsp++)
628 me = xmalloc (sizeof *me);
629 me->me_devname = xstrdup (fsp->f_mntfromname);
630 me->me_mountdir = xstrdup (fsp->f_mntonname);
631 me->me_mntroot = NULL;
632 me->me_type = xstrdup (fsp->f_fstypename);
633 me->me_type_malloced = 1;
634 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
635 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
636 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
638 /* Add to the linked list. */
639 *mtail = me;
640 mtail = &me->me_next;
643 #endif /* MOUNTED_GETMNTINFO2 */
645 #if defined MOUNTED_FS_STAT_DEV /* Haiku, also (obsolete) BeOS */
647 /* The next_dev() and fs_stat_dev() system calls give the list of
648 all file systems, including the information returned by statvfs()
649 (fs type, total blocks, free blocks etc.), but without the mount
650 point. But on BeOS all file systems except / are mounted in the
651 rootfs, directly under /.
652 The directory name of the mount point is often, but not always,
653 identical to the volume name of the device.
654 We therefore get the list of subdirectories of /, and the list
655 of all file systems, and match the two lists. */
657 DIR *dirp;
658 struct rootdir_entry
660 char *name;
661 dev_t dev;
662 ino_t ino;
663 struct rootdir_entry *next;
665 struct rootdir_entry *rootdir_list;
666 struct rootdir_entry **rootdir_tail;
667 int32 pos;
668 dev_t dev;
669 fs_info fi;
671 /* All volumes are mounted in the rootfs, directly under /. */
672 rootdir_list = NULL;
673 rootdir_tail = &rootdir_list;
674 dirp = opendir ("/");
675 if (dirp)
677 struct dirent *d;
679 while ((d = readdir (dirp)) != NULL)
681 char *name;
682 struct stat statbuf;
684 if (strcmp (d->d_name, "..") == 0)
685 continue;
687 if (strcmp (d->d_name, ".") == 0)
688 name = xstrdup ("/");
689 else
691 name = xmalloc (1 + strlen (d->d_name) + 1);
692 name[0] = '/';
693 strcpy (name + 1, d->d_name);
696 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
698 struct rootdir_entry *re = xmalloc (sizeof *re);
699 re->name = name;
700 re->dev = statbuf.st_dev;
701 re->ino = statbuf.st_ino;
703 /* Add to the linked list. */
704 *rootdir_tail = re;
705 rootdir_tail = &re->next;
707 else
708 free (name);
710 closedir (dirp);
712 *rootdir_tail = NULL;
714 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
715 if (fs_stat_dev (dev, &fi) >= 0)
717 /* Note: fi.dev == dev. */
718 struct rootdir_entry *re;
720 for (re = rootdir_list; re; re = re->next)
721 if (re->dev == fi.dev && re->ino == fi.root)
722 break;
724 me = xmalloc (sizeof *me);
725 me->me_devname = xstrdup (fi.device_name[0] != '\0'
726 ? fi.device_name : fi.fsh_name);
727 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
728 me->me_mntroot = NULL;
729 me->me_type = xstrdup (fi.fsh_name);
730 me->me_type_malloced = 1;
731 me->me_dev = fi.dev;
732 me->me_dummy = 0;
733 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
735 /* Add to the linked list. */
736 *mtail = me;
737 mtail = &me->me_next;
739 *mtail = NULL;
741 while (rootdir_list != NULL)
743 struct rootdir_entry *re = rootdir_list;
744 rootdir_list = re->next;
745 free (re->name);
746 free (re);
749 #endif /* MOUNTED_FS_STAT_DEV */
751 #if defined MOUNTED_GETFSSTAT /* OSF/1, also (obsolete) Apple Darwin 1.3 */
753 int numsys, counter;
754 size_t bufsize;
755 struct statfs *stats;
757 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
758 if (numsys < 0)
759 return NULL;
760 if (SIZE_MAX / sizeof *stats <= numsys)
761 xalloc_die ();
763 bufsize = (1 + numsys) * sizeof *stats;
764 stats = xmalloc (bufsize);
765 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
767 if (numsys < 0)
769 free (stats);
770 return NULL;
773 for (counter = 0; counter < numsys; counter++)
775 me = xmalloc (sizeof *me);
776 me->me_devname = xstrdup (stats[counter].f_mntfromname);
777 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
778 me->me_mntroot = NULL;
779 me->me_type = xstrdup (FS_TYPE (stats[counter]));
780 me->me_type_malloced = 1;
781 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
782 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
783 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
785 /* Add to the linked list. */
786 *mtail = me;
787 mtail = &me->me_next;
790 free (stats);
792 #endif /* MOUNTED_GETFSSTAT */
794 #if defined MOUNTED_FREAD_FSTYP /* (obsolete) SVR3 */
796 struct mnttab mnt;
797 char *table = "/etc/mnttab";
798 FILE *fp;
800 fp = fopen (table, "re");
801 if (fp == NULL)
802 return NULL;
804 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
806 me = xmalloc (sizeof *me);
807 me->me_devname = xstrdup (mnt.mt_dev);
808 me->me_mountdir = xstrdup (mnt.mt_filsys);
809 me->me_mntroot = NULL;
810 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
811 me->me_type = "";
812 me->me_type_malloced = 0;
813 if (need_fs_type)
815 struct statfs fsd;
816 char typebuf[FSTYPSZ];
818 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
819 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
821 me->me_type = xstrdup (typebuf);
822 me->me_type_malloced = 1;
825 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
826 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
828 /* Add to the linked list. */
829 *mtail = me;
830 mtail = &me->me_next;
833 if (ferror (fp))
835 /* The last fread() call must have failed. */
836 int saved_errno = errno;
837 fclose (fp);
838 errno = saved_errno;
839 goto free_then_fail;
842 if (fclose (fp) == EOF)
843 goto free_then_fail;
845 #endif /* MOUNTED_FREAD_FSTYP. */
847 #ifdef MOUNTED_GETEXTMNTENT /* Solaris >= 8 */
849 struct extmnttab mnt;
850 const char *table = MNTTAB;
851 FILE *fp;
852 int ret;
854 /* No locking is needed, because the contents of /etc/mnttab is generated
855 by the kernel. */
857 errno = 0;
858 fp = fopen (table, "re");
859 if (fp == NULL)
860 ret = errno;
861 else
863 while ((ret = getextmntent (fp, &mnt, 1)) == 0)
865 me = xmalloc (sizeof *me);
866 me->me_devname = xstrdup (mnt.mnt_special);
867 me->me_mountdir = xstrdup (mnt.mnt_mountp);
868 me->me_mntroot = NULL;
869 me->me_type = xstrdup (mnt.mnt_fstype);
870 me->me_type_malloced = 1;
871 me->me_dummy = MNT_IGNORE (&mnt) != 0;
872 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
873 me->me_dev = makedev (mnt.mnt_major, mnt.mnt_minor);
875 /* Add to the linked list. */
876 *mtail = me;
877 mtail = &me->me_next;
880 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
881 /* Here ret = -1 means success, ret >= 0 means failure. */
884 if (0 <= ret)
886 errno = ret;
887 goto free_then_fail;
890 #endif /* MOUNTED_GETEXTMNTENT */
892 #ifdef MOUNTED_GETMNTENT2 /* Solaris < 8, also (obsolete) SVR4 */
894 struct mnttab mnt;
895 const char *table = MNTTAB;
896 FILE *fp;
897 int ret;
898 int lockfd = -1;
900 # if defined F_RDLCK && defined F_SETLKW
901 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
902 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
903 for this file name, we should use their macro name instead.
904 (Why not just lock MNTTAB directly? We don't know.) */
905 # ifndef MNTTAB_LOCK
906 # define MNTTAB_LOCK "/etc/.mnttab.lock"
907 # endif
908 lockfd = open (MNTTAB_LOCK, O_RDONLY | O_CLOEXEC);
909 if (0 <= lockfd)
911 struct flock flock;
912 flock.l_type = F_RDLCK;
913 flock.l_whence = SEEK_SET;
914 flock.l_start = 0;
915 flock.l_len = 0;
916 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
917 if (errno != EINTR)
919 int saved_errno = errno;
920 close (lockfd);
921 errno = saved_errno;
922 return NULL;
925 else if (errno != ENOENT)
926 return NULL;
927 # endif
929 errno = 0;
930 fp = fopen (table, "re");
931 if (fp == NULL)
932 ret = errno;
933 else
935 while ((ret = getmntent (fp, &mnt)) == 0)
937 me = xmalloc (sizeof *me);
938 me->me_devname = xstrdup (mnt.mnt_special);
939 me->me_mountdir = xstrdup (mnt.mnt_mountp);
940 me->me_mntroot = NULL;
941 me->me_type = xstrdup (mnt.mnt_fstype);
942 me->me_type_malloced = 1;
943 me->me_dummy = MNT_IGNORE (&mnt) != 0;
944 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
945 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
947 /* Add to the linked list. */
948 *mtail = me;
949 mtail = &me->me_next;
952 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
953 /* Here ret = -1 means success, ret >= 0 means failure. */
956 if (0 <= lockfd && close (lockfd) != 0)
957 ret = errno;
959 if (0 <= ret)
961 errno = ret;
962 goto free_then_fail;
965 #endif /* MOUNTED_GETMNTENT2. */
967 #ifdef MOUNTED_VMOUNT /* AIX */
969 int bufsize;
970 void *entries;
971 char *thisent;
972 struct vmount *vmp;
973 int n_entries;
974 int i;
976 /* Ask how many bytes to allocate for the mounted file system info. */
977 entries = &bufsize;
978 if (mntctl (MCTL_QUERY, sizeof bufsize, entries) != 0)
979 return NULL;
980 entries = xmalloc (bufsize);
982 /* Get the list of mounted file systems. */
983 n_entries = mntctl (MCTL_QUERY, bufsize, entries);
984 if (n_entries < 0)
986 int saved_errno = errno;
987 free (entries);
988 errno = saved_errno;
989 return NULL;
992 for (i = 0, thisent = entries;
993 i < n_entries;
994 i++, thisent += vmp->vmt_length)
996 char *options, *ignore;
998 vmp = (struct vmount *) thisent;
999 me = xmalloc (sizeof *me);
1000 if (vmp->vmt_flags & MNT_REMOTE)
1002 char *host, *dir;
1004 me->me_remote = 1;
1005 /* Prepend the remote dirname. */
1006 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
1007 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
1008 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
1009 strcpy (me->me_devname, host);
1010 strcat (me->me_devname, ":");
1011 strcat (me->me_devname, dir);
1013 else
1015 me->me_remote = 0;
1016 me->me_devname = xstrdup (thisent +
1017 vmp->vmt_data[VMT_OBJECT].vmt_off);
1019 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
1020 me->me_mntroot = NULL;
1021 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
1022 me->me_type_malloced = 1;
1023 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
1024 ignore = strstr (options, "ignore");
1025 me->me_dummy = (ignore
1026 && (ignore == options || ignore[-1] == ',')
1027 && (ignore[sizeof "ignore" - 1] == ','
1028 || ignore[sizeof "ignore" - 1] == '\0'));
1029 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
1031 /* Add to the linked list. */
1032 *mtail = me;
1033 mtail = &me->me_next;
1035 free (entries);
1037 #endif /* MOUNTED_VMOUNT. */
1039 #ifdef MOUNTED_INTERIX_STATVFS /* Interix */
1041 DIR *dirp = opendir ("/dev/fs");
1042 char node[9 + NAME_MAX];
1044 if (!dirp)
1045 goto free_then_fail;
1047 while (1)
1049 struct statvfs dev;
1050 struct dirent entry;
1051 struct dirent *result;
1053 /* FIXME: readdir_r is planned to be withdrawn from POSIX and
1054 marked obsolescent in glibc. Use readdir instead. */
1055 if (readdir_r (dirp, &entry, &result) || result == NULL)
1056 break;
1058 strcpy (node, "/dev/fs/");
1059 strcat (node, entry.d_name);
1061 if (statvfs (node, &dev) == 0)
1063 me = xmalloc (sizeof *me);
1064 me->me_devname = xstrdup (dev.f_mntfromname);
1065 me->me_mountdir = xstrdup (dev.f_mntonname);
1066 me->me_mntroot = NULL;
1067 me->me_type = xstrdup (dev.f_fstypename);
1068 me->me_type_malloced = 1;
1069 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
1070 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
1071 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
1073 /* Add to the linked list. */
1074 *mtail = me;
1075 mtail = &me->me_next;
1078 closedir (dirp);
1080 #endif /* MOUNTED_INTERIX_STATVFS */
1082 *mtail = NULL;
1083 return mount_list;
1086 free_then_fail: _GL_UNUSED_LABEL
1088 int saved_errno = errno;
1089 *mtail = NULL;
1091 while (mount_list)
1093 me = mount_list->me_next;
1094 free_mount_entry (mount_list);
1095 mount_list = me;
1098 errno = saved_errno;
1099 return NULL;
1103 /* Free a mount entry as returned from read_file_system_list (). */
1105 void free_mount_entry (struct mount_entry *me)
1107 free (me->me_devname);
1108 free (me->me_mountdir);
1109 free (me->me_mntroot);
1110 if (me->me_type_malloced)
1111 free (me->me_type);
1112 free (me);