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 NetBSD 3.0 */ \
157 || strcmp (Fs_type, "kernfs") == 0 \
159 || strcmp (Fs_type, "ignore") == 0)
161 /* Historically, we have marked as "dummy" any file system of type "none",
162 but now that programs like du need to know about bind-mounted directories,
163 we grant an exception to any with "bind" in its list of mount options.
164 I.e., those are *not* dummy entries. */
165 #ifdef MOUNTED_GETMNTENT1
166 # define ME_DUMMY(Fs_name, Fs_type, Fs_ent) \
167 (ME_DUMMY_0 (Fs_name, Fs_type) \
168 || (strcmp (Fs_type, "none") == 0 \
169 && !hasmntopt (Fs_ent, "bind")))
171 # define ME_DUMMY(Fs_name, Fs_type) \
172 (ME_DUMMY_0 (Fs_name, Fs_type) || strcmp (Fs_type, "none") == 0)
176 # include <windows.h>
177 # define ME_REMOTE me_remote
178 /* All cygwin mount points include ':' or start with '//'; so it
179 requires a native Windows call to determine remote disks. */
181 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
183 if (fs_name[0] && fs_name[1] == ':')
186 sprintf (drive, "%c:\\", fs_name[0]);
187 switch (GetDriveType (drive))
189 case DRIVE_REMOVABLE:
201 /* A file system is "remote" if its Fs_name contains a ':'
202 or if (it is of type (smbfs or cifs) and its Fs_name starts with '//'). */
203 # define ME_REMOTE(Fs_name, Fs_type) \
204 (strchr (Fs_name, ':') != NULL \
205 || ((Fs_name)[0] == '/' \
206 && (Fs_name)[1] == '/' \
207 && (strcmp (Fs_type, "smbfs") == 0 \
208 || strcmp (Fs_type, "cifs") == 0)))
211 #if MOUNTED_GETMNTINFO
213 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
215 fstype_to_string (short int t)
310 fsp_to_string (const struct statfs *fsp)
312 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
313 return (char *) (fsp->f_fstypename);
315 return fstype_to_string (fsp->f_type);
319 #endif /* MOUNTED_GETMNTINFO */
321 #ifdef MOUNTED_VMOUNT /* AIX. */
323 fstype_to_string (int t)
327 e = getvfsbytype (t);
328 if (!e || !e->vfsent_name)
331 return e->vfsent_name;
333 #endif /* MOUNTED_VMOUNT */
336 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
338 /* Return the device number from MOUNT_OPTIONS, if possible.
339 Otherwise return (dev_t) -1. */
341 dev_from_mount_options (char const *mount_options)
343 /* GNU/Linux allows file system implementations to define their own
344 meaning for "dev=" mount options, so don't trust the meaning
348 static char const dev_pattern[] = ",dev=";
349 char const *devopt = strstr (mount_options, dev_pattern);
353 char const *optval = devopt + sizeof dev_pattern - 1;
355 unsigned long int dev;
357 dev = strtoul (optval, &optvalend, 16);
358 if (optval != optvalend
359 && (*optvalend == '\0' || *optvalend == ',')
360 && ! (dev == ULONG_MAX && errno == ERANGE)
361 && dev == (dev_t) dev)
366 (void) mount_options;
372 /* Return a list of the currently mounted file systems, or NULL on error.
373 Add each entry to the tail of the list so that they stay in order.
374 If NEED_FS_TYPE is true, ensure that the file system type fields in
375 the returned list are valid. Otherwise, they might not be. */
378 read_file_system_list (bool need_fs_type)
380 struct mount_entry *mount_list;
381 struct mount_entry *me;
382 struct mount_entry **mtail = &mount_list;
385 #ifdef MOUNTED_LISTMNTENT
387 struct tabmntent *mntlist, *p;
389 struct mount_entry *me;
391 /* the third and fourth arguments could be used to filter mounts,
392 but Crays doesn't seem to have any mounts that we want to
393 remove. Specifically, automount create normal NFS mounts.
396 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
398 for (p = mntlist; p; p = p->next)
401 me = xmalloc (sizeof *me);
402 me->me_devname = xstrdup (mnt->mnt_fsname);
403 me->me_mountdir = xstrdup (mnt->mnt_dir);
404 me->me_type = xstrdup (mnt->mnt_type);
405 me->me_type_malloced = 1;
406 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
407 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
410 mtail = &me->me_next;
412 freemntlist (mntlist);
416 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
419 char const *table = MOUNTED;
422 fp = setmntent (table, "r");
426 while ((mnt = getmntent (fp)))
428 me = xmalloc (sizeof *me);
429 me->me_devname = xstrdup (mnt->mnt_fsname);
430 me->me_mountdir = xstrdup (mnt->mnt_dir);
431 me->me_type = xstrdup (mnt->mnt_type);
432 me->me_type_malloced = 1;
433 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type, mnt);
434 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
435 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
437 /* Add to the linked list. */
439 mtail = &me->me_next;
442 if (endmntent (fp) == 0)
445 #endif /* MOUNTED_GETMNTENT1. */
447 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
452 entries = getmntinfo (&fsp, MNT_NOWAIT);
455 for (; entries-- > 0; fsp++)
457 char *fs_type = fsp_to_string (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 = fs_type;
463 me->me_type_malloced = 0;
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_GETMNTINFO */
475 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
480 entries = getmntinfo (&fsp, MNT_NOWAIT);
483 for (; entries-- > 0; fsp++)
485 me = xmalloc (sizeof *me);
486 me->me_devname = xstrdup (fsp->f_mntfromname);
487 me->me_mountdir = xstrdup (fsp->f_mntonname);
488 me->me_type = xstrdup (fsp->f_fstypename);
489 me->me_type_malloced = 1;
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 = (dev_t) -1; /* Magic; means not known yet. */
494 /* Add to the linked list. */
496 mtail = &me->me_next;
499 #endif /* MOUNTED_GETMNTINFO2 */
501 #ifdef MOUNTED_GETMNT /* Ultrix. */
508 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
511 me = xmalloc (sizeof *me);
512 me->me_devname = xstrdup (fsd.fd_req.devname);
513 me->me_mountdir = xstrdup (fsd.fd_req.path);
514 me->me_type = gt_names[fsd.fd_req.fstype];
515 me->me_type_malloced = 0;
516 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
517 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
518 me->me_dev = fsd.fd_req.dev;
520 /* Add to the linked list. */
522 mtail = &me->me_next;
527 #endif /* MOUNTED_GETMNT. */
529 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
531 /* The next_dev() and fs_stat_dev() system calls give the list of
532 all file systems, including the information returned by statvfs()
533 (fs type, total blocks, free blocks etc.), but without the mount
534 point. But on BeOS all file systems except / are mounted in the
535 rootfs, directly under /.
536 The directory name of the mount point is often, but not always,
537 identical to the volume name of the device.
538 We therefore get the list of subdirectories of /, and the list
539 of all file systems, and match the two lists. */
547 struct rootdir_entry *next;
549 struct rootdir_entry *rootdir_list;
550 struct rootdir_entry **rootdir_tail;
555 /* All volumes are mounted in the rootfs, directly under /. */
557 rootdir_tail = &rootdir_list;
558 dirp = opendir ("/");
563 while ((d = readdir (dirp)) != NULL)
568 if (strcmp (d->d_name, "..") == 0)
571 if (strcmp (d->d_name, ".") == 0)
572 name = xstrdup ("/");
575 name = xmalloc (1 + strlen (d->d_name) + 1);
577 strcpy (name + 1, d->d_name);
580 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
582 struct rootdir_entry *re = xmalloc (sizeof *re);
584 re->dev = statbuf.st_dev;
585 re->ino = statbuf.st_ino;
587 /* Add to the linked list. */
589 rootdir_tail = &re->next;
596 *rootdir_tail = NULL;
598 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
599 if (fs_stat_dev (dev, &fi) >= 0)
601 /* Note: fi.dev == dev. */
602 struct rootdir_entry *re;
604 for (re = rootdir_list; re; re = re->next)
605 if (re->dev == fi.dev && re->ino == fi.root)
608 me = xmalloc (sizeof *me);
609 me->me_devname = xstrdup (fi.device_name[0] != '\0'
610 ? fi.device_name : fi.fsh_name);
611 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
612 me->me_type = xstrdup (fi.fsh_name);
613 me->me_type_malloced = 1;
616 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
618 /* Add to the linked list. */
620 mtail = &me->me_next;
624 while (rootdir_list != NULL)
626 struct rootdir_entry *re = rootdir_list;
627 rootdir_list = re->next;
632 #endif /* MOUNTED_FS_STAT_DEV */
634 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
638 struct statfs *stats;
640 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
643 if (SIZE_MAX / sizeof *stats <= numsys)
646 bufsize = (1 + numsys) * sizeof *stats;
647 stats = xmalloc (bufsize);
648 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
656 for (counter = 0; counter < numsys; counter++)
658 me = xmalloc (sizeof *me);
659 me->me_devname = xstrdup (stats[counter].f_mntfromname);
660 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
661 me->me_type = xstrdup (FS_TYPE (stats[counter]));
662 me->me_type_malloced = 1;
663 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
664 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
665 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
667 /* Add to the linked list. */
669 mtail = &me->me_next;
674 #endif /* MOUNTED_GETFSSTAT */
676 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
679 char *table = "/etc/mnttab";
682 fp = fopen (table, "r");
686 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
688 me = xmalloc (sizeof *me);
689 # ifdef GETFSTYP /* SVR3. */
690 me->me_devname = xstrdup (mnt.mt_dev);
692 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
693 strcpy (me->me_devname, "/dev/");
694 strcpy (me->me_devname + 5, mnt.mt_dev);
696 me->me_mountdir = xstrdup (mnt.mt_filsys);
697 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
699 me->me_type_malloced = 0;
700 # ifdef GETFSTYP /* SVR3. */
704 char typebuf[FSTYPSZ];
706 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
707 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
709 me->me_type = xstrdup (typebuf);
710 me->me_type_malloced = 1;
714 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
715 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
717 /* Add to the linked list. */
719 mtail = &me->me_next;
724 /* The last fread() call must have failed. */
725 int saved_errno = errno;
731 if (fclose (fp) == EOF)
734 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
736 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
738 struct mntent **mnttbl = getmnttbl (), **ent;
739 for (ent = mnttbl; *ent; ent++)
741 me = xmalloc (sizeof *me);
742 me->me_devname = xstrdup ((*ent)->mt_resource);
743 me->me_mountdir = xstrdup ((*ent)->mt_directory);
744 me->me_type = xstrdup ((*ent)->mt_fstype);
745 me->me_type_malloced = 1;
746 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
747 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
748 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
750 /* Add to the linked list. */
752 mtail = &me->me_next;
758 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
761 char *table = MNTTAB;
766 # if defined F_RDLCK && defined F_SETLKW
767 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
768 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
769 for this file name, we should use their macro name instead.
770 (Why not just lock MNTTAB directly? We don't know.) */
772 # define MNTTAB_LOCK "/etc/.mnttab.lock"
774 lockfd = open (MNTTAB_LOCK, O_RDONLY);
778 flock.l_type = F_RDLCK;
779 flock.l_whence = SEEK_SET;
782 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
785 int saved_errno = errno;
791 else if (errno != ENOENT)
796 fp = fopen (table, "r");
801 while ((ret = getmntent (fp, &mnt)) == 0)
803 me = xmalloc (sizeof *me);
804 me->me_devname = xstrdup (mnt.mnt_special);
805 me->me_mountdir = xstrdup (mnt.mnt_mountp);
806 me->me_type = xstrdup (mnt.mnt_fstype);
807 me->me_type_malloced = 1;
808 me->me_dummy = MNT_IGNORE (&mnt) != 0;
809 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
810 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
812 /* Add to the linked list. */
814 mtail = &me->me_next;
817 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
820 if (0 <= lockfd && close (lockfd) != 0)
829 #endif /* MOUNTED_GETMNTENT2. */
831 #ifdef MOUNTED_VMOUNT /* AIX. */
834 char *entries, *thisent;
839 /* Ask how many bytes to allocate for the mounted file system info. */
840 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
842 entries = xmalloc (bufsize);
844 /* Get the list of mounted file systems. */
845 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
848 int saved_errno = errno;
854 for (i = 0, thisent = entries;
856 i++, thisent += vmp->vmt_length)
858 char *options, *ignore;
860 vmp = (struct vmount *) thisent;
861 me = xmalloc (sizeof *me);
862 if (vmp->vmt_flags & MNT_REMOTE)
867 /* Prepend the remote dirname. */
868 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
869 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
870 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
871 strcpy (me->me_devname, host);
872 strcat (me->me_devname, ":");
873 strcat (me->me_devname, dir);
878 me->me_devname = xstrdup (thisent +
879 vmp->vmt_data[VMT_OBJECT].vmt_off);
881 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
882 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
883 me->me_type_malloced = 1;
884 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
885 ignore = strstr (options, "ignore");
886 me->me_dummy = (ignore
887 && (ignore == options || ignore[-1] == ',')
888 && (ignore[sizeof "ignore" - 1] == ','
889 || ignore[sizeof "ignore" - 1] == '\0'));
890 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
892 /* Add to the linked list. */
894 mtail = &me->me_next;
898 #endif /* MOUNTED_VMOUNT. */
900 #ifdef MOUNTED_INTERIX_STATVFS
902 DIR *dirp = opendir ("/dev/fs");
903 char node[9 + NAME_MAX];
912 struct dirent *result;
914 if (readdir_r (dirp, &entry, &result) || result == NULL)
917 strcpy (node, "/dev/fs/");
918 strcat (node, entry.d_name);
920 if (statvfs (node, &dev) == 0)
922 me = xmalloc (sizeof *me);
923 me->me_devname = xstrdup (dev.f_mntfromname);
924 me->me_mountdir = xstrdup (dev.f_mntonname);
925 me->me_type = xstrdup (dev.f_fstypename);
926 me->me_type_malloced = 1;
927 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
928 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
929 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
931 /* Add to the linked list. */
933 mtail = &me->me_next;
937 #endif /* MOUNTED_INTERIX_STATVFS */
945 int saved_errno = errno;
950 me = mount_list->me_next;
951 free (mount_list->me_devname);
952 free (mount_list->me_mountdir);
953 if (mount_list->me_type_malloced)
954 free (mount_list->me_type);