1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991, 1992, 1997-2008 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 <http://www.gnu.org/licenses/>. */
20 #include "mountlist.h"
36 # include <sys/param.h>
39 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
41 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
42 NGROUPS is used as an array dimension in ucred.h */
43 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
46 # include <sys/mount.h>
48 # if HAVE_SYS_FS_TYPES_H
49 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
51 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
52 # define FS_TYPE(Ent) ((Ent).f_fstypename)
54 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
56 #endif /* MOUNTED_GETFSSTAT */
58 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
61 # if defined _PATH_MOUNTED /* GNU libc */
62 # define MOUNTED _PATH_MOUNTED
64 # if defined MNT_MNTTAB /* HP-UX. */
65 # define MOUNTED MNT_MNTTAB
67 # if defined MNTTABNAME /* Dynix. */
68 # define MOUNTED MNTTABNAME
73 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
74 # include <sys/mount.h>
77 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
78 # include <sys/statvfs.h>
81 #ifdef MOUNTED_GETMNT /* Ultrix. */
82 # include <sys/mount.h>
83 # include <sys/fs_types.h>
86 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
91 #ifdef MOUNTED_FREAD /* SVR2. */
95 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
97 # include <sys/fstyp.h>
98 # include <sys/statfs.h>
101 #ifdef MOUNTED_LISTMNTENT
105 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
106 # include <sys/mnttab.h>
109 #ifdef MOUNTED_VMOUNT /* AIX. */
111 # include <sys/vfs.h>
115 /* So special that it's not worth putting this in autoconf. */
116 # undef MOUNTED_FREAD_FSTYP
117 # define MOUNTED_GETMNTTBL
120 #if HAVE_SYS_MNTENT_H
121 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
122 # include <sys/mntent.h>
126 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
127 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
129 # define MNT_IGNORE(M) 0
133 # include "unlocked-io.h"
137 # define SIZE_MAX ((size_t) -1)
140 /* The results of open() in this file are not used with fchdir,
141 therefore save some unnecessary work in fchdir.c. */
145 /* The results of opendir() in this file are not used with dirfd and fchdir,
146 therefore save some unnecessary work in fchdir.c. */
151 # define ME_DUMMY(Fs_name, Fs_type) \
152 (strcmp (Fs_type, "autofs") == 0 \
153 || strcmp (Fs_type, "none") == 0 \
154 || strcmp (Fs_type, "proc") == 0 \
155 || strcmp (Fs_type, "subfs") == 0 \
156 /* for NetBSD 3.0 */ \
157 || strcmp (Fs_type, "kernfs") == 0 \
159 || strcmp (Fs_type, "ignore") == 0)
163 /* A file system is `remote' if its Fs_name contains a `:'
164 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
165 # define ME_REMOTE(Fs_name, Fs_type) \
166 (strchr (Fs_name, ':') != NULL \
167 || ((Fs_name)[0] == '/' \
168 && (Fs_name)[1] == '/' \
169 && (strcmp (Fs_type, "smbfs") == 0 \
170 || strcmp (Fs_type, "cifs") == 0)))
173 #if MOUNTED_GETMNTINFO
175 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
177 fstype_to_string (short int t)
272 fsp_to_string (const struct statfs *fsp)
274 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
275 return (char *) (fsp->f_fstypename);
277 return fstype_to_string (fsp->f_type);
281 #endif /* MOUNTED_GETMNTINFO */
283 #ifdef MOUNTED_VMOUNT /* AIX. */
285 fstype_to_string (int t)
289 e = getvfsbytype (t);
290 if (!e || !e->vfsent_name)
293 return e->vfsent_name;
295 #endif /* MOUNTED_VMOUNT */
298 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
302 # define UNUSED_PARAM _UNUSED_PARAMETER_
304 # define UNUSED_PARAM
307 /* Return the device number from MOUNT_OPTIONS, if possible.
308 Otherwise return (dev_t) -1. */
310 dev_from_mount_options (char const *mount_options UNUSED_PARAM)
312 /* GNU/Linux allows file system implementations to define their own
313 meaning for "dev=" mount options, so don't trust the meaning
317 static char const dev_pattern[] = ",dev=";
318 char const *devopt = strstr (mount_options, dev_pattern);
322 char const *optval = devopt + sizeof dev_pattern - 1;
324 unsigned long int dev;
326 dev = strtoul (optval, &optvalend, 16);
327 if (optval != optvalend
328 && (*optvalend == '\0' || *optvalend == ',')
329 && ! (dev == ULONG_MAX && errno == ERANGE)
330 && dev == (dev_t) dev)
343 # define UNUSED_PARAM
345 # define UNUSED_PARAM _UNUSED_PARAMETER_
348 /* Return a list of the currently mounted file systems, or NULL on error.
349 Add each entry to the tail of the list so that they stay in order.
350 If NEED_FS_TYPE is true, ensure that the file system type fields in
351 the returned list are valid. Otherwise, they might not be. */
354 read_file_system_list (bool need_fs_type UNUSED_PARAM)
356 struct mount_entry *mount_list;
357 struct mount_entry *me;
358 struct mount_entry **mtail = &mount_list;
360 #ifdef MOUNTED_LISTMNTENT
362 struct tabmntent *mntlist, *p;
364 struct mount_entry *me;
366 /* the third and fourth arguments could be used to filter mounts,
367 but Crays doesn't seem to have any mounts that we want to
368 remove. Specifically, automount create normal NFS mounts.
371 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
373 for (p = mntlist; p; p = p->next) {
375 me = xmalloc (sizeof *me);
376 me->me_devname = xstrdup (mnt->mnt_fsname);
377 me->me_mountdir = xstrdup (mnt->mnt_dir);
378 me->me_type = xstrdup (mnt->mnt_type);
379 me->me_type_malloced = 1;
380 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
381 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
384 mtail = &me->me_next;
386 freemntlist (mntlist);
390 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
393 char const *table = MOUNTED;
396 fp = setmntent (table, "r");
400 while ((mnt = getmntent (fp)))
402 me = xmalloc (sizeof *me);
403 me->me_devname = xstrdup (mnt->mnt_fsname);
404 me->me_mountdir = xstrdup (mnt->mnt_dir);
405 me->me_type = xstrdup (mnt->mnt_type);
406 me->me_type_malloced = 1;
407 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
408 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
409 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
411 /* Add to the linked list. */
413 mtail = &me->me_next;
416 if (endmntent (fp) == 0)
419 #endif /* MOUNTED_GETMNTENT1. */
421 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
426 entries = getmntinfo (&fsp, MNT_NOWAIT);
429 for (; entries-- > 0; fsp++)
431 char *fs_type = fsp_to_string (fsp);
433 me = xmalloc (sizeof *me);
434 me->me_devname = xstrdup (fsp->f_mntfromname);
435 me->me_mountdir = xstrdup (fsp->f_mntonname);
436 me->me_type = fs_type;
437 me->me_type_malloced = 0;
438 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
439 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
440 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
442 /* Add to the linked list. */
444 mtail = &me->me_next;
447 #endif /* MOUNTED_GETMNTINFO */
449 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
454 entries = getmntinfo (&fsp, MNT_NOWAIT);
457 for (; entries-- > 0; fsp++)
459 me = xmalloc (sizeof *me);
460 me->me_devname = xstrdup (fsp->f_mntfromname);
461 me->me_mountdir = xstrdup (fsp->f_mntonname);
462 me->me_type = xstrdup (fsp->f_fstypename);
463 me->me_type_malloced = 1;
464 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
465 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
466 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
468 /* Add to the linked list. */
470 mtail = &me->me_next;
473 #endif /* MOUNTED_GETMNTINFO2 */
475 #ifdef MOUNTED_GETMNT /* Ultrix. */
482 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
485 me = xmalloc (sizeof *me);
486 me->me_devname = xstrdup (fsd.fd_req.devname);
487 me->me_mountdir = xstrdup (fsd.fd_req.path);
488 me->me_type = gt_names[fsd.fd_req.fstype];
489 me->me_type_malloced = 0;
490 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
491 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
492 me->me_dev = fsd.fd_req.dev;
494 /* Add to the linked list. */
496 mtail = &me->me_next;
501 #endif /* MOUNTED_GETMNT. */
503 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
505 /* The next_dev() and fs_stat_dev() system calls give the list of
506 all file systems, including the information returned by statvfs()
507 (fs type, total blocks, free blocks etc.), but without the mount
508 point. But on BeOS all file systems except / are mounted in the
509 rootfs, directly under /.
510 The directory name of the mount point is often, but not always,
511 identical to the volume name of the device.
512 We therefore get the list of subdirectories of /, and the list
513 of all file systems, and match the two lists. */
521 struct rootdir_entry *next;
523 struct rootdir_entry *rootdir_list;
524 struct rootdir_entry **rootdir_tail;
529 /* All volumes are mounted in the rootfs, directly under /. */
531 rootdir_tail = &rootdir_list;
532 dirp = opendir ("/");
537 while ((d = readdir (dirp)) != NULL)
542 if (strcmp (d->d_name, "..") == 0)
545 if (strcmp (d->d_name, ".") == 0)
546 name = xstrdup ("/");
549 name = xmalloc (1 + strlen (d->d_name) + 1);
551 strcpy (name + 1, d->d_name);
554 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
556 struct rootdir_entry *re = xmalloc (sizeof *re);
558 re->dev = statbuf.st_dev;
559 re->ino = statbuf.st_ino;
561 /* Add to the linked list. */
563 rootdir_tail = &re->next;
570 *rootdir_tail = NULL;
572 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
573 if (fs_stat_dev (dev, &fi) >= 0)
575 /* Note: fi.dev == dev. */
576 struct rootdir_entry *re;
578 for (re = rootdir_list; re; re = re->next)
579 if (re->dev == fi.dev && re->ino == fi.root)
582 me = xmalloc (sizeof *me);
583 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
584 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
585 me->me_type = xstrdup (fi.fsh_name);
586 me->me_type_malloced = 1;
589 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
591 /* Add to the linked list. */
593 mtail = &me->me_next;
597 while (rootdir_list != NULL)
599 struct rootdir_entry *re = rootdir_list;
600 rootdir_list = re->next;
605 #endif /* MOUNTED_FS_STAT_DEV */
607 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
611 struct statfs *stats;
613 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
616 if (SIZE_MAX / sizeof *stats <= numsys)
619 bufsize = (1 + numsys) * sizeof *stats;
620 stats = xmalloc (bufsize);
621 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
629 for (counter = 0; counter < numsys; counter++)
631 me = xmalloc (sizeof *me);
632 me->me_devname = xstrdup (stats[counter].f_mntfromname);
633 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
634 me->me_type = xstrdup (FS_TYPE (stats[counter]));
635 me->me_type_malloced = 1;
636 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
637 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
638 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
640 /* Add to the linked list. */
642 mtail = &me->me_next;
647 #endif /* MOUNTED_GETFSSTAT */
649 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
652 char *table = "/etc/mnttab";
655 fp = fopen (table, "r");
659 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
661 me = xmalloc (sizeof *me);
662 # ifdef GETFSTYP /* SVR3. */
663 me->me_devname = xstrdup (mnt.mt_dev);
665 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
666 strcpy (me->me_devname, "/dev/");
667 strcpy (me->me_devname + 5, mnt.mt_dev);
669 me->me_mountdir = xstrdup (mnt.mt_filsys);
670 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
672 me->me_type_malloced = 0;
673 # ifdef GETFSTYP /* SVR3. */
677 char typebuf[FSTYPSZ];
679 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
680 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
682 me->me_type = xstrdup (typebuf);
683 me->me_type_malloced = 1;
687 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
688 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
690 /* Add to the linked list. */
692 mtail = &me->me_next;
697 /* The last fread() call must have failed. */
698 int saved_errno = errno;
704 if (fclose (fp) == EOF)
707 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
709 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
711 struct mntent **mnttbl = getmnttbl (), **ent;
712 for (ent=mnttbl;*ent;ent++)
714 me = xmalloc (sizeof *me);
715 me->me_devname = xstrdup ( (*ent)->mt_resource);
716 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
717 me->me_type = xstrdup ((*ent)->mt_fstype);
718 me->me_type_malloced = 1;
719 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
720 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
721 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
723 /* Add to the linked list. */
725 mtail = &me->me_next;
731 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
734 char *table = MNTTAB;
739 # if defined F_RDLCK && defined F_SETLKW
740 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
741 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
742 for this file name, we should use their macro name instead.
743 (Why not just lock MNTTAB directly? We don't know.) */
745 # define MNTTAB_LOCK "/etc/.mnttab.lock"
747 lockfd = open (MNTTAB_LOCK, O_RDONLY);
751 flock.l_type = F_RDLCK;
752 flock.l_whence = SEEK_SET;
755 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
758 int saved_errno = errno;
764 else if (errno != ENOENT)
769 fp = fopen (table, "r");
774 while ((ret = getmntent (fp, &mnt)) == 0)
776 me = xmalloc (sizeof *me);
777 me->me_devname = xstrdup (mnt.mnt_special);
778 me->me_mountdir = xstrdup (mnt.mnt_mountp);
779 me->me_type = xstrdup (mnt.mnt_fstype);
780 me->me_type_malloced = 1;
781 me->me_dummy = MNT_IGNORE (&mnt) != 0;
782 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
783 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
785 /* Add to the linked list. */
787 mtail = &me->me_next;
790 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
793 if (0 <= lockfd && close (lockfd) != 0)
802 #endif /* MOUNTED_GETMNTENT2. */
804 #ifdef MOUNTED_VMOUNT /* AIX. */
807 char *entries, *thisent;
812 /* Ask how many bytes to allocate for the mounted file system info. */
813 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
815 entries = xmalloc (bufsize);
817 /* Get the list of mounted file systems. */
818 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
821 int saved_errno = errno;
827 for (i = 0, thisent = entries;
829 i++, thisent += vmp->vmt_length)
831 char *options, *ignore;
833 vmp = (struct vmount *) thisent;
834 me = xmalloc (sizeof *me);
835 if (vmp->vmt_flags & MNT_REMOTE)
840 /* Prepend the remote dirname. */
841 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
842 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
843 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
844 strcpy (me->me_devname, host);
845 strcat (me->me_devname, ":");
846 strcat (me->me_devname, dir);
851 me->me_devname = xstrdup (thisent +
852 vmp->vmt_data[VMT_OBJECT].vmt_off);
854 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
855 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
856 me->me_type_malloced = 1;
857 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
858 ignore = strstr (options, "ignore");
859 me->me_dummy = (ignore
860 && (ignore == options || ignore[-1] == ',')
861 && (ignore[sizeof "ignore" - 1] == ','
862 || ignore[sizeof "ignore" - 1] == '\0'));
863 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
865 /* Add to the linked list. */
867 mtail = &me->me_next;
871 #endif /* MOUNTED_VMOUNT. */
879 int saved_errno = errno;
884 me = mount_list->me_next;
885 free (mount_list->me_devname);
886 free (mount_list->me_mountdir);
887 if (mount_list->me_type_malloced)
888 free (mount_list->me_type);