1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2013 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, "fusectl") == 0 \
160 || strcmp (Fs_type, "mqueue") == 0 \
161 || strcmp (Fs_type, "rpc_pipefs") == 0 \
162 || strcmp (Fs_type, "sysfs") == 0 \
163 /* FreeBSD, Linux 2.4 */ \
164 || strcmp (Fs_type, "devfs") == 0 \
165 /* for NetBSD 3.0 */ \
166 || strcmp (Fs_type, "kernfs") == 0 \
168 || strcmp (Fs_type, "ignore") == 0)
170 /* Historically, we have marked as "dummy" any file system of type "none",
171 but now that programs like du need to know about bind-mounted directories,
172 we grant an exception to any with "bind" in its list of mount options.
173 I.e., those are *not* dummy entries. */
174 #ifdef MOUNTED_GETMNTENT1
175 # define ME_DUMMY(Fs_name, Fs_type, Fs_ent) \
176 (ME_DUMMY_0 (Fs_name, Fs_type) \
177 || (strcmp (Fs_type, "none") == 0 \
178 && !hasmntopt (Fs_ent, "bind")))
180 # define ME_DUMMY(Fs_name, Fs_type) \
181 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
185 # include <windows.h>
186 # define ME_REMOTE me_remote
187 /* All cygwin mount points include ':' or start with '//'; so it
188 requires a native Windows call to determine remote disks. */
190 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
192 if (fs_name[0] && fs_name[1] == ':')
195 sprintf (drive, "%c:\\", fs_name[0]);
196 switch (GetDriveType (drive))
198 case DRIVE_REMOVABLE:
210 /* A file system is "remote" if its Fs_name contains a ':'
211 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//'). */
212 # define ME_REMOTE(Fs_name, Fs_type) \
213 (strchr (Fs_name, ':') != NULL \
214 || ((Fs_name)[0] == '/' \
215 && (Fs_name)[1] == '/' \
216 && (strcmp (Fs_type, "smbfs") == 0 \
217 || strcmp (Fs_type, "cifs") == 0)))
220 #if MOUNTED_GETMNTINFO
222 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
224 fstype_to_string (short int t)
319 fsp_to_string (const struct statfs *fsp)
321 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
322 return (char *) (fsp->f_fstypename);
324 return fstype_to_string (fsp->f_type);
328 #endif /* MOUNTED_GETMNTINFO */
330 #ifdef MOUNTED_VMOUNT /* AIX. */
332 fstype_to_string (int t)
336 e = getvfsbytype (t);
337 if (!e || !e->vfsent_name)
340 return e->vfsent_name;
342 #endif /* MOUNTED_VMOUNT */
345 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
347 /* Return the device number from MOUNT_OPTIONS, if possible.
348 Otherwise return (dev_t) -1. */
350 dev_from_mount_options (char const *mount_options)
352 /* GNU/Linux allows file system implementations to define their own
353 meaning for "dev=" mount options, so don't trust the meaning
357 static char const dev_pattern[] = ",dev=";
358 char const *devopt = strstr (mount_options, dev_pattern);
362 char const *optval = devopt + sizeof dev_pattern - 1;
364 unsigned long int dev;
366 dev = strtoul (optval, &optvalend, 16);
367 if (optval != optvalend
368 && (*optvalend == '\0' || *optvalend == ',')
369 && ! (dev == ULONG_MAX && errno == ERANGE)
370 && dev == (dev_t) dev)
375 (void) mount_options;
381 /* Return a list of the currently mounted file systems, or NULL on error.
382 Add each entry to the tail of the list so that they stay in order.
383 If NEED_FS_TYPE is true, ensure that the file system type fields in
384 the returned list are valid. Otherwise, they might not be. */
387 read_file_system_list (bool need_fs_type)
389 struct mount_entry *mount_list;
390 struct mount_entry *me;
391 struct mount_entry **mtail = &mount_list;
394 #ifdef MOUNTED_LISTMNTENT
396 struct tabmntent *mntlist, *p;
398 struct mount_entry *me;
400 /* the third and fourth arguments could be used to filter mounts,
401 but Crays doesn't seem to have any mounts that we want to
402 remove. Specifically, automount create normal NFS mounts.
405 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
407 for (p = mntlist; p; p = p->next)
410 me = xmalloc (sizeof *me);
411 me->me_devname = xstrdup (mnt->mnt_fsname);
412 me->me_mountdir = xstrdup (mnt->mnt_dir);
413 me->me_type = xstrdup (mnt->mnt_type);
414 me->me_type_malloced = 1;
415 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
416 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
419 mtail = &me->me_next;
421 freemntlist (mntlist);
425 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
428 char const *table = MOUNTED;
431 fp = setmntent (table, "r");
435 while ((mnt = getmntent (fp)))
437 me = xmalloc (sizeof *me);
438 me->me_devname = xstrdup (mnt->mnt_fsname);
439 me->me_mountdir = xstrdup (mnt->mnt_dir);
440 me->me_type = xstrdup (mnt->mnt_type);
441 me->me_type_malloced = 1;
442 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, mnt);
443 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
444 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
446 /* Add to the linked list. */
448 mtail = &me->me_next;
451 if (endmntent (fp) == 0)
454 #endif /* MOUNTED_GETMNTENT1. */
456 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
461 entries = getmntinfo (&fsp, MNT_NOWAIT);
464 for (; entries-- > 0; fsp++)
466 char *fs_type = fsp_to_string (fsp);
468 me = xmalloc (sizeof *me);
469 me->me_devname = xstrdup (fsp->f_mntfromname);
470 me->me_mountdir = xstrdup (fsp->f_mntonname);
471 me->me_type = fs_type;
472 me->me_type_malloced = 0;
473 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
474 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
475 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
477 /* Add to the linked list. */
479 mtail = &me->me_next;
482 #endif /* MOUNTED_GETMNTINFO */
484 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
489 entries = getmntinfo (&fsp, MNT_NOWAIT);
492 for (; entries-- > 0; fsp++)
494 me = xmalloc (sizeof *me);
495 me->me_devname = xstrdup (fsp->f_mntfromname);
496 me->me_mountdir = xstrdup (fsp->f_mntonname);
497 me->me_type = xstrdup (fsp->f_fstypename);
498 me->me_type_malloced = 1;
499 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
500 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
501 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
503 /* Add to the linked list. */
505 mtail = &me->me_next;
508 #endif /* MOUNTED_GETMNTINFO2 */
510 #ifdef MOUNTED_GETMNT /* Ultrix. */
517 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
520 me = xmalloc (sizeof *me);
521 me->me_devname = xstrdup (fsd.fd_req.devname);
522 me->me_mountdir = xstrdup (fsd.fd_req.path);
523 me->me_type = gt_names[fsd.fd_req.fstype];
524 me->me_type_malloced = 0;
525 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
526 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
527 me->me_dev = fsd.fd_req.dev;
529 /* Add to the linked list. */
531 mtail = &me->me_next;
536 #endif /* MOUNTED_GETMNT. */
538 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
540 /* The next_dev() and fs_stat_dev() system calls give the list of
541 all file systems, including the information returned by statvfs()
542 (fs type, total blocks, free blocks etc.), but without the mount
543 point. But on BeOS all file systems except / are mounted in the
544 rootfs, directly under /.
545 The directory name of the mount point is often, but not always,
546 identical to the volume name of the device.
547 We therefore get the list of subdirectories of /, and the list
548 of all file systems, and match the two lists. */
556 struct rootdir_entry *next;
558 struct rootdir_entry *rootdir_list;
559 struct rootdir_entry **rootdir_tail;
564 /* All volumes are mounted in the rootfs, directly under /. */
566 rootdir_tail = &rootdir_list;
567 dirp = opendir ("/");
572 while ((d = readdir (dirp)) != NULL)
577 if (strcmp (d->d_name, "..") == 0)
580 if (strcmp (d->d_name, ".") == 0)
581 name = xstrdup ("/");
584 name = xmalloc (1 + strlen (d->d_name) + 1);
586 strcpy (name + 1, d->d_name);
589 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
591 struct rootdir_entry *re = xmalloc (sizeof *re);
593 re->dev = statbuf.st_dev;
594 re->ino = statbuf.st_ino;
596 /* Add to the linked list. */
598 rootdir_tail = &re->next;
605 *rootdir_tail = NULL;
607 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
608 if (fs_stat_dev (dev, &fi) >= 0)
610 /* Note: fi.dev == dev. */
611 struct rootdir_entry *re;
613 for (re = rootdir_list; re; re = re->next)
614 if (re->dev == fi.dev && re->ino == fi.root)
617 me = xmalloc (sizeof *me);
618 me->me_devname = xstrdup (fi.device_name[0] != '\0'
619 ? fi.device_name : fi.fsh_name);
620 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
621 me->me_type = xstrdup (fi.fsh_name);
622 me->me_type_malloced = 1;
625 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
627 /* Add to the linked list. */
629 mtail = &me->me_next;
633 while (rootdir_list != NULL)
635 struct rootdir_entry *re = rootdir_list;
636 rootdir_list = re->next;
641 #endif /* MOUNTED_FS_STAT_DEV */
643 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
647 struct statfs *stats;
649 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
652 if (SIZE_MAX / sizeof *stats <= numsys)
655 bufsize = (1 + numsys) * sizeof *stats;
656 stats = xmalloc (bufsize);
657 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
665 for (counter = 0; counter < numsys; counter++)
667 me = xmalloc (sizeof *me);
668 me->me_devname = xstrdup (stats[counter].f_mntfromname);
669 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
670 me->me_type = xstrdup (FS_TYPE (stats[counter]));
671 me->me_type_malloced = 1;
672 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
673 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
674 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
676 /* Add to the linked list. */
678 mtail = &me->me_next;
683 #endif /* MOUNTED_GETFSSTAT */
685 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
688 char *table = "/etc/mnttab";
691 fp = fopen (table, "r");
695 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
697 me = xmalloc (sizeof *me);
698 # ifdef GETFSTYP /* SVR3. */
699 me->me_devname = xstrdup (mnt.mt_dev);
701 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
702 strcpy (me->me_devname, "/dev/");
703 strcpy (me->me_devname + 5, mnt.mt_dev);
705 me->me_mountdir = xstrdup (mnt.mt_filsys);
706 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
708 me->me_type_malloced = 0;
709 # ifdef GETFSTYP /* SVR3. */
713 char typebuf[FSTYPSZ];
715 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
716 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
718 me->me_type = xstrdup (typebuf);
719 me->me_type_malloced = 1;
723 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
724 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
726 /* Add to the linked list. */
728 mtail = &me->me_next;
733 /* The last fread() call must have failed. */
734 int saved_errno = errno;
740 if (fclose (fp) == EOF)
743 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
745 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
747 struct mntent **mnttbl = getmnttbl (), **ent;
748 for (ent = mnttbl; *ent; ent++)
750 me = xmalloc (sizeof *me);
751 me->me_devname = xstrdup ((*ent)->mt_resource);
752 me->me_mountdir = xstrdup ((*ent)->mt_directory);
753 me->me_type = xstrdup ((*ent)->mt_fstype);
754 me->me_type_malloced = 1;
755 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
756 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
757 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
759 /* Add to the linked list. */
761 mtail = &me->me_next;
767 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
770 char *table = MNTTAB;
775 # if defined F_RDLCK && defined F_SETLKW
776 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
777 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
778 for this file name, we should use their macro name instead.
779 (Why not just lock MNTTAB directly? We don't know.) */
781 # define MNTTAB_LOCK "/etc/.mnttab.lock"
783 lockfd = open (MNTTAB_LOCK, O_RDONLY);
787 flock.l_type = F_RDLCK;
788 flock.l_whence = SEEK_SET;
791 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
794 int saved_errno = errno;
800 else if (errno != ENOENT)
805 fp = fopen (table, "r");
810 while ((ret = getmntent (fp, &mnt)) == 0)
812 me = xmalloc (sizeof *me);
813 me->me_devname = xstrdup (mnt.mnt_special);
814 me->me_mountdir = xstrdup (mnt.mnt_mountp);
815 me->me_type = xstrdup (mnt.mnt_fstype);
816 me->me_type_malloced = 1;
817 me->me_dummy = MNT_IGNORE (&mnt) != 0;
818 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
819 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
821 /* Add to the linked list. */
823 mtail = &me->me_next;
826 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
829 if (0 <= lockfd && close (lockfd) != 0)
838 #endif /* MOUNTED_GETMNTENT2. */
840 #ifdef MOUNTED_VMOUNT /* AIX. */
843 char *entries, *thisent;
848 /* Ask how many bytes to allocate for the mounted file system info. */
849 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
851 entries = xmalloc (bufsize);
853 /* Get the list of mounted file systems. */
854 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
857 int saved_errno = errno;
863 for (i = 0, thisent = entries;
865 i++, thisent += vmp->vmt_length)
867 char *options, *ignore;
869 vmp = (struct vmount *) thisent;
870 me = xmalloc (sizeof *me);
871 if (vmp->vmt_flags & MNT_REMOTE)
876 /* Prepend the remote dirname. */
877 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
878 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
879 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
880 strcpy (me->me_devname, host);
881 strcat (me->me_devname, ":");
882 strcat (me->me_devname, dir);
887 me->me_devname = xstrdup (thisent +
888 vmp->vmt_data[VMT_OBJECT].vmt_off);
890 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
891 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
892 me->me_type_malloced = 1;
893 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
894 ignore = strstr (options, "ignore");
895 me->me_dummy = (ignore
896 && (ignore == options || ignore[-1] == ',')
897 && (ignore[sizeof "ignore" - 1] == ','
898 || ignore[sizeof "ignore" - 1] == '\0'));
899 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
901 /* Add to the linked list. */
903 mtail = &me->me_next;
907 #endif /* MOUNTED_VMOUNT. */
909 #ifdef MOUNTED_INTERIX_STATVFS
911 DIR *dirp = opendir ("/dev/fs");
912 char node[9 + NAME_MAX];
921 struct dirent *result;
923 if (readdir_r (dirp, &entry, &result) || result == NULL)
926 strcpy (node, "/dev/fs/");
927 strcat (node, entry.d_name);
929 if (statvfs (node, &dev) == 0)
931 me = xmalloc (sizeof *me);
932 me->me_devname = xstrdup (dev.f_mntfromname);
933 me->me_mountdir = xstrdup (dev.f_mntonname);
934 me->me_type = xstrdup (dev.f_fstypename);
935 me->me_type_malloced = 1;
936 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
937 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
938 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
940 /* Add to the linked list. */
942 mtail = &me->me_next;
946 #endif /* MOUNTED_INTERIX_STATVFS */
954 int saved_errno = errno;
959 me = mount_list->me_next;
960 free (mount_list->me_devname);
961 free (mount_list->me_mountdir);
962 if (mount_list->me_type_malloced)
963 free (mount_list->me_type);