1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2012 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"
37 # include <sys/param.h>
40 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
42 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
43 NGROUPS is used as an array dimension in ucred.h */
44 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
47 # include <sys/mount.h>
49 # if HAVE_SYS_FS_TYPES_H
50 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
52 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
53 # define FS_TYPE(Ent) ((Ent).f_fstypename)
55 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
57 #endif /* MOUNTED_GETFSSTAT */
59 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
62 # if defined _PATH_MOUNTED /* GNU libc */
63 # define MOUNTED _PATH_MOUNTED
65 # if defined MNT_MNTTAB /* HP-UX. */
66 # define MOUNTED MNT_MNTTAB
68 # if defined MNTTABNAME /* Dynix. */
69 # define MOUNTED MNTTABNAME
74 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
75 # include <sys/mount.h>
78 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
79 # include <sys/statvfs.h>
82 #ifdef MOUNTED_GETMNT /* Ultrix. */
83 # include <sys/mount.h>
84 # include <sys/fs_types.h>
87 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
92 #ifdef MOUNTED_FREAD /* SVR2. */
96 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
98 # include <sys/fstyp.h>
99 # include <sys/statfs.h>
102 #ifdef MOUNTED_LISTMNTENT
106 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
107 # include <sys/mnttab.h>
110 #ifdef MOUNTED_VMOUNT /* AIX. */
112 # include <sys/vfs.h>
115 #ifdef MOUNTED_INTERIX_STATVFS /* Interix. */
116 # include <sys/statvfs.h>
121 /* So special that it's not worth putting this in autoconf. */
122 # undef MOUNTED_FREAD_FSTYP
123 # define MOUNTED_GETMNTTBL
126 #if HAVE_SYS_MNTENT_H
127 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
128 # include <sys/mntent.h>
132 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
133 # define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
135 # define MNT_IGNORE(M) 0
139 # include "unlocked-io.h"
142 /* The results of open() in this file are not used with fchdir,
143 therefore save some unnecessary work in fchdir.c. */
147 /* The results of opendir() in this file are not used with dirfd and fchdir,
148 therefore save some unnecessary work in fchdir.c. */
152 #define ME_DUMMY_0(Fs_name, Fs_type) \
153 (strcmp (Fs_type, "autofs") == 0 \
154 || strcmp (Fs_type, "proc") == 0 \
155 || strcmp (Fs_type, "subfs") == 0 \
156 /* for Linux 2.6/3.x */ \
157 || strcmp (Fs_type, "debugfs") == 0 \
158 || strcmp (Fs_type, "devpts") == 0 \
159 || strcmp (Fs_type, "devtmpfs") == 0 \
160 || strcmp (Fs_type, "fusectl") == 0 \
161 || strcmp (Fs_type, "mqueue") == 0 \
162 || strcmp (Fs_type, "rpc_pipefs") == 0 \
163 || strcmp (Fs_type, "sysfs") == 0 \
164 /* FreeBSD, Linux 2.4 */ \
165 || strcmp (Fs_type, "devfs") == 0 \
166 /* for NetBSD 3.0 */ \
167 || strcmp (Fs_type, "kernfs") == 0 \
169 || strcmp (Fs_type, "ignore") == 0)
171 /* Historically, we have marked as "dummy" any file system of type "none",
172 but now that programs like du need to know about bind-mounted directories,
173 we grant an exception to any with "bind" in its list of mount options.
174 I.e., those are *not* dummy entries. */
175 #ifdef MOUNTED_GETMNTENT1
176 # define ME_DUMMY(Fs_name, Fs_type, Fs_ent) \
177 (ME_DUMMY_0 (Fs_name, Fs_type) \
178 || (strcmp (Fs_type, "none") == 0 \
179 && !hasmntopt (Fs_ent, "bind")))
181 # define ME_DUMMY(Fs_name, Fs_type) \
182 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
186 # include <windows.h>
187 # define ME_REMOTE me_remote
188 /* All cygwin mount points include ':' or start with '//'; so it
189 requires a native Windows call to determine remote disks. */
191 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
193 if (fs_name[0] && fs_name[1] == ':')
196 sprintf (drive, "%c:\\", fs_name[0]);
197 switch (GetDriveType (drive))
199 case DRIVE_REMOVABLE:
211 /* A file system is "remote" if its Fs_name contains a ':'
212 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//'). */
213 # define ME_REMOTE(Fs_name, Fs_type) \
214 (strchr (Fs_name, ':') != NULL \
215 || ((Fs_name)[0] == '/' \
216 && (Fs_name)[1] == '/' \
217 && (strcmp (Fs_type, "smbfs") == 0 \
218 || strcmp (Fs_type, "cifs") == 0)))
221 #if MOUNTED_GETMNTINFO
223 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
225 fstype_to_string (short int t)
320 fsp_to_string (const struct statfs *fsp)
322 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
323 return (char *) (fsp->f_fstypename);
325 return fstype_to_string (fsp->f_type);
329 #endif /* MOUNTED_GETMNTINFO */
331 #ifdef MOUNTED_VMOUNT /* AIX. */
333 fstype_to_string (int t)
337 e = getvfsbytype (t);
338 if (!e || !e->vfsent_name)
341 return e->vfsent_name;
343 #endif /* MOUNTED_VMOUNT */
346 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
348 /* Return the device number from MOUNT_OPTIONS, if possible.
349 Otherwise return (dev_t) -1. */
351 dev_from_mount_options (char const *mount_options)
353 /* GNU/Linux allows file system implementations to define their own
354 meaning for "dev=" mount options, so don't trust the meaning
358 static char const dev_pattern[] = ",dev=";
359 char const *devopt = strstr (mount_options, dev_pattern);
363 char const *optval = devopt + sizeof dev_pattern - 1;
365 unsigned long int dev;
367 dev = strtoul (optval, &optvalend, 16);
368 if (optval != optvalend
369 && (*optvalend == '\0' || *optvalend == ',')
370 && ! (dev == ULONG_MAX && errno == ERANGE)
371 && dev == (dev_t) dev)
376 (void) mount_options;
382 /* Return a list of the currently mounted file systems, or NULL on error.
383 Add each entry to the tail of the list so that they stay in order.
384 If NEED_FS_TYPE is true, ensure that the file system type fields in
385 the returned list are valid. Otherwise, they might not be. */
388 read_file_system_list (bool need_fs_type)
390 struct mount_entry *mount_list;
391 struct mount_entry *me;
392 struct mount_entry **mtail = &mount_list;
395 #ifdef MOUNTED_LISTMNTENT
397 struct tabmntent *mntlist, *p;
399 struct mount_entry *me;
401 /* the third and fourth arguments could be used to filter mounts,
402 but Crays doesn't seem to have any mounts that we want to
403 remove. Specifically, automount create normal NFS mounts.
406 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
408 for (p = mntlist; p; p = p->next)
411 me = xmalloc (sizeof *me);
412 me->me_devname = xstrdup (mnt->mnt_fsname);
413 me->me_mountdir = xstrdup (mnt->mnt_dir);
414 me->me_type = xstrdup (mnt->mnt_type);
415 me->me_type_malloced = 1;
416 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
417 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
420 mtail = &me->me_next;
422 freemntlist (mntlist);
426 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
429 char const *table = MOUNTED;
432 fp = setmntent (table, "r");
436 while ((mnt = getmntent (fp)))
438 me = xmalloc (sizeof *me);
439 me->me_devname = xstrdup (mnt->mnt_fsname);
440 me->me_mountdir = xstrdup (mnt->mnt_dir);
441 me->me_type = xstrdup (mnt->mnt_type);
442 me->me_type_malloced = 1;
443 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, mnt);
444 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
445 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
447 /* Add to the linked list. */
449 mtail = &me->me_next;
452 if (endmntent (fp) == 0)
455 #endif /* MOUNTED_GETMNTENT1. */
457 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
462 entries = getmntinfo (&fsp, MNT_NOWAIT);
465 for (; entries-- > 0; fsp++)
467 char *fs_type = fsp_to_string (fsp);
469 me = xmalloc (sizeof *me);
470 me->me_devname = xstrdup (fsp->f_mntfromname);
471 me->me_mountdir = xstrdup (fsp->f_mntonname);
472 me->me_type = fs_type;
473 me->me_type_malloced = 0;
474 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
475 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
476 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
478 /* Add to the linked list. */
480 mtail = &me->me_next;
483 #endif /* MOUNTED_GETMNTINFO */
485 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
490 entries = getmntinfo (&fsp, MNT_NOWAIT);
493 for (; entries-- > 0; fsp++)
495 me = xmalloc (sizeof *me);
496 me->me_devname = xstrdup (fsp->f_mntfromname);
497 me->me_mountdir = xstrdup (fsp->f_mntonname);
498 me->me_type = xstrdup (fsp->f_fstypename);
499 me->me_type_malloced = 1;
500 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
501 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
502 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
504 /* Add to the linked list. */
506 mtail = &me->me_next;
509 #endif /* MOUNTED_GETMNTINFO2 */
511 #ifdef MOUNTED_GETMNT /* Ultrix. */
518 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
521 me = xmalloc (sizeof *me);
522 me->me_devname = xstrdup (fsd.fd_req.devname);
523 me->me_mountdir = xstrdup (fsd.fd_req.path);
524 me->me_type = gt_names[fsd.fd_req.fstype];
525 me->me_type_malloced = 0;
526 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
527 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
528 me->me_dev = fsd.fd_req.dev;
530 /* Add to the linked list. */
532 mtail = &me->me_next;
537 #endif /* MOUNTED_GETMNT. */
539 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
541 /* The next_dev() and fs_stat_dev() system calls give the list of
542 all file systems, including the information returned by statvfs()
543 (fs type, total blocks, free blocks etc.), but without the mount
544 point. But on BeOS all file systems except / are mounted in the
545 rootfs, directly under /.
546 The directory name of the mount point is often, but not always,
547 identical to the volume name of the device.
548 We therefore get the list of subdirectories of /, and the list
549 of all file systems, and match the two lists. */
557 struct rootdir_entry *next;
559 struct rootdir_entry *rootdir_list;
560 struct rootdir_entry **rootdir_tail;
565 /* All volumes are mounted in the rootfs, directly under /. */
567 rootdir_tail = &rootdir_list;
568 dirp = opendir ("/");
573 while ((d = readdir (dirp)) != NULL)
578 if (strcmp (d->d_name, "..") == 0)
581 if (strcmp (d->d_name, ".") == 0)
582 name = xstrdup ("/");
585 name = xmalloc (1 + strlen (d->d_name) + 1);
587 strcpy (name + 1, d->d_name);
590 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
592 struct rootdir_entry *re = xmalloc (sizeof *re);
594 re->dev = statbuf.st_dev;
595 re->ino = statbuf.st_ino;
597 /* Add to the linked list. */
599 rootdir_tail = &re->next;
606 *rootdir_tail = NULL;
608 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
609 if (fs_stat_dev (dev, &fi) >= 0)
611 /* Note: fi.dev == dev. */
612 struct rootdir_entry *re;
614 for (re = rootdir_list; re; re = re->next)
615 if (re->dev == fi.dev && re->ino == fi.root)
618 me = xmalloc (sizeof *me);
619 me->me_devname = xstrdup (fi.device_name[0] != '\0'
620 ? fi.device_name : fi.fsh_name);
621 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
622 me->me_type = xstrdup (fi.fsh_name);
623 me->me_type_malloced = 1;
626 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
628 /* Add to the linked list. */
630 mtail = &me->me_next;
634 while (rootdir_list != NULL)
636 struct rootdir_entry *re = rootdir_list;
637 rootdir_list = re->next;
642 #endif /* MOUNTED_FS_STAT_DEV */
644 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
648 struct statfs *stats;
650 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
653 if (SIZE_MAX / sizeof *stats <= numsys)
656 bufsize = (1 + numsys) * sizeof *stats;
657 stats = xmalloc (bufsize);
658 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
666 for (counter = 0; counter < numsys; counter++)
668 me = xmalloc (sizeof *me);
669 me->me_devname = xstrdup (stats[counter].f_mntfromname);
670 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
671 me->me_type = xstrdup (FS_TYPE (stats[counter]));
672 me->me_type_malloced = 1;
673 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
674 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
675 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
677 /* Add to the linked list. */
679 mtail = &me->me_next;
684 #endif /* MOUNTED_GETFSSTAT */
686 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
689 char *table = "/etc/mnttab";
692 fp = fopen (table, "r");
696 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
698 me = xmalloc (sizeof *me);
699 # ifdef GETFSTYP /* SVR3. */
700 me->me_devname = xstrdup (mnt.mt_dev);
702 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
703 strcpy (me->me_devname, "/dev/");
704 strcpy (me->me_devname + 5, mnt.mt_dev);
706 me->me_mountdir = xstrdup (mnt.mt_filsys);
707 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
709 me->me_type_malloced = 0;
710 # ifdef GETFSTYP /* SVR3. */
714 char typebuf[FSTYPSZ];
716 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
717 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
719 me->me_type = xstrdup (typebuf);
720 me->me_type_malloced = 1;
724 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
725 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
727 /* Add to the linked list. */
729 mtail = &me->me_next;
734 /* The last fread() call must have failed. */
735 int saved_errno = errno;
741 if (fclose (fp) == EOF)
744 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
746 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
748 struct mntent **mnttbl = getmnttbl (), **ent;
749 for (ent = mnttbl; *ent; ent++)
751 me = xmalloc (sizeof *me);
752 me->me_devname = xstrdup ((*ent)->mt_resource);
753 me->me_mountdir = xstrdup ((*ent)->mt_directory);
754 me->me_type = xstrdup ((*ent)->mt_fstype);
755 me->me_type_malloced = 1;
756 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
757 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
758 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
760 /* Add to the linked list. */
762 mtail = &me->me_next;
768 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
771 char *table = MNTTAB;
776 # if defined F_RDLCK && defined F_SETLKW
777 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
778 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
779 for this file name, we should use their macro name instead.
780 (Why not just lock MNTTAB directly? We don't know.) */
782 # define MNTTAB_LOCK "/etc/.mnttab.lock"
784 lockfd = open (MNTTAB_LOCK, O_RDONLY);
788 flock.l_type = F_RDLCK;
789 flock.l_whence = SEEK_SET;
792 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
795 int saved_errno = errno;
801 else if (errno != ENOENT)
806 fp = fopen (table, "r");
811 while ((ret = getmntent (fp, &mnt)) == 0)
813 me = xmalloc (sizeof *me);
814 me->me_devname = xstrdup (mnt.mnt_special);
815 me->me_mountdir = xstrdup (mnt.mnt_mountp);
816 me->me_type = xstrdup (mnt.mnt_fstype);
817 me->me_type_malloced = 1;
818 me->me_dummy = MNT_IGNORE (&mnt) != 0;
819 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
820 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
822 /* Add to the linked list. */
824 mtail = &me->me_next;
827 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
830 if (0 <= lockfd && close (lockfd) != 0)
839 #endif /* MOUNTED_GETMNTENT2. */
841 #ifdef MOUNTED_VMOUNT /* AIX. */
844 char *entries, *thisent;
849 /* Ask how many bytes to allocate for the mounted file system info. */
850 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
852 entries = xmalloc (bufsize);
854 /* Get the list of mounted file systems. */
855 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
858 int saved_errno = errno;
864 for (i = 0, thisent = entries;
866 i++, thisent += vmp->vmt_length)
868 char *options, *ignore;
870 vmp = (struct vmount *) thisent;
871 me = xmalloc (sizeof *me);
872 if (vmp->vmt_flags & MNT_REMOTE)
877 /* Prepend the remote dirname. */
878 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
879 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
880 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
881 strcpy (me->me_devname, host);
882 strcat (me->me_devname, ":");
883 strcat (me->me_devname, dir);
888 me->me_devname = xstrdup (thisent +
889 vmp->vmt_data[VMT_OBJECT].vmt_off);
891 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
892 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
893 me->me_type_malloced = 1;
894 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
895 ignore = strstr (options, "ignore");
896 me->me_dummy = (ignore
897 && (ignore == options || ignore[-1] == ',')
898 && (ignore[sizeof "ignore" - 1] == ','
899 || ignore[sizeof "ignore" - 1] == '\0'));
900 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
902 /* Add to the linked list. */
904 mtail = &me->me_next;
908 #endif /* MOUNTED_VMOUNT. */
910 #ifdef MOUNTED_INTERIX_STATVFS
912 DIR *dirp = opendir ("/dev/fs");
913 char node[9 + NAME_MAX];
922 struct dirent *result;
924 if (readdir_r (dirp, &entry, &result) || result == NULL)
927 strcpy (node, "/dev/fs/");
928 strcat (node, entry.d_name);
930 if (statvfs (node, &dev) == 0)
932 me = xmalloc (sizeof *me);
933 me->me_devname = xstrdup (dev.f_mntfromname);
934 me->me_mountdir = xstrdup (dev.f_mntonname);
935 me->me_type = xstrdup (dev.f_fstypename);
936 me->me_type_malloced = 1;
937 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
938 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
939 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
941 /* Add to the linked list. */
943 mtail = &me->me_next;
947 #endif /* MOUNTED_INTERIX_STATVFS */
955 int saved_errno = errno;
960 me = mount_list->me_next;
961 free (mount_list->me_devname);
962 free (mount_list->me_mountdir);
963 if (mount_list->me_type_malloced)
964 free (mount_list->me_type);