1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2011 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. */
153 # define ME_DUMMY(Fs_name, Fs_type) \
154 (strcmp (Fs_type, "autofs") == 0 \
155 || strcmp (Fs_type, "none") == 0 \
156 || strcmp (Fs_type, "proc") == 0 \
157 || strcmp (Fs_type, "subfs") == 0 \
158 /* for NetBSD 3.0 */ \
159 || strcmp (Fs_type, "kernfs") == 0 \
161 || strcmp (Fs_type, "ignore") == 0)
165 # include <windows.h>
166 # define ME_REMOTE me_remote
167 /* All cygwin mount points include `:' or start with `//'; so it
168 requires a native Windows call to determine remote disks. */
170 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
172 if (fs_name[0] && fs_name[1] == ':')
175 sprintf (drive, "%c:\\", fs_name[0]);
176 switch (GetDriveType (drive))
178 case DRIVE_REMOVABLE:
190 /* A file system is `remote' if its Fs_name contains a `:'
191 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
192 # define ME_REMOTE(Fs_name, Fs_type) \
193 (strchr (Fs_name, ':') != NULL \
194 || ((Fs_name)[0] == '/' \
195 && (Fs_name)[1] == '/' \
196 && (strcmp (Fs_type, "smbfs") == 0 \
197 || strcmp (Fs_type, "cifs") == 0)))
200 #if MOUNTED_GETMNTINFO
202 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
204 fstype_to_string (short int t)
299 fsp_to_string (const struct statfs *fsp)
301 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
302 return (char *) (fsp->f_fstypename);
304 return fstype_to_string (fsp->f_type);
308 #endif /* MOUNTED_GETMNTINFO */
310 #ifdef MOUNTED_VMOUNT /* AIX. */
312 fstype_to_string (int t)
316 e = getvfsbytype (t);
317 if (!e || !e->vfsent_name)
320 return e->vfsent_name;
322 #endif /* MOUNTED_VMOUNT */
325 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
327 /* Return the device number from MOUNT_OPTIONS, if possible.
328 Otherwise return (dev_t) -1. */
330 dev_from_mount_options (char const *mount_options)
332 /* GNU/Linux allows file system implementations to define their own
333 meaning for "dev=" mount options, so don't trust the meaning
337 static char const dev_pattern[] = ",dev=";
338 char const *devopt = strstr (mount_options, dev_pattern);
342 char const *optval = devopt + sizeof dev_pattern - 1;
344 unsigned long int dev;
346 dev = strtoul (optval, &optvalend, 16);
347 if (optval != optvalend
348 && (*optvalend == '\0' || *optvalend == ',')
349 && ! (dev == ULONG_MAX && errno == ERANGE)
350 && dev == (dev_t) dev)
355 (void) mount_options;
361 /* Return a list of the currently mounted file systems, or NULL on error.
362 Add each entry to the tail of the list so that they stay in order.
363 If NEED_FS_TYPE is true, ensure that the file system type fields in
364 the returned list are valid. Otherwise, they might not be. */
367 read_file_system_list (bool need_fs_type)
369 struct mount_entry *mount_list;
370 struct mount_entry *me;
371 struct mount_entry **mtail = &mount_list;
374 #ifdef MOUNTED_LISTMNTENT
376 struct tabmntent *mntlist, *p;
378 struct mount_entry *me;
380 /* the third and fourth arguments could be used to filter mounts,
381 but Crays doesn't seem to have any mounts that we want to
382 remove. Specifically, automount create normal NFS mounts.
385 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
387 for (p = mntlist; p; p = p->next)
390 me = xmalloc (sizeof *me);
391 me->me_devname = xstrdup (mnt->mnt_fsname);
392 me->me_mountdir = xstrdup (mnt->mnt_dir);
393 me->me_type = xstrdup (mnt->mnt_type);
394 me->me_type_malloced = 1;
395 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
396 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
399 mtail = &me->me_next;
401 freemntlist (mntlist);
405 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
408 char const *table = MOUNTED;
411 fp = setmntent (table, "r");
415 while ((mnt = getmntent (fp)))
417 me = xmalloc (sizeof *me);
418 me->me_devname = xstrdup (mnt->mnt_fsname);
419 me->me_mountdir = xstrdup (mnt->mnt_dir);
420 me->me_type = xstrdup (mnt->mnt_type);
421 me->me_type_malloced = 1;
422 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
423 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
424 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
426 /* Add to the linked list. */
428 mtail = &me->me_next;
431 if (endmntent (fp) == 0)
434 #endif /* MOUNTED_GETMNTENT1. */
436 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
441 entries = getmntinfo (&fsp, MNT_NOWAIT);
444 for (; entries-- > 0; fsp++)
446 char *fs_type = fsp_to_string (fsp);
448 me = xmalloc (sizeof *me);
449 me->me_devname = xstrdup (fsp->f_mntfromname);
450 me->me_mountdir = xstrdup (fsp->f_mntonname);
451 me->me_type = fs_type;
452 me->me_type_malloced = 0;
453 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
454 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
455 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
457 /* Add to the linked list. */
459 mtail = &me->me_next;
462 #endif /* MOUNTED_GETMNTINFO */
464 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
469 entries = getmntinfo (&fsp, MNT_NOWAIT);
472 for (; entries-- > 0; fsp++)
474 me = xmalloc (sizeof *me);
475 me->me_devname = xstrdup (fsp->f_mntfromname);
476 me->me_mountdir = xstrdup (fsp->f_mntonname);
477 me->me_type = xstrdup (fsp->f_fstypename);
478 me->me_type_malloced = 1;
479 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
480 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
481 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
483 /* Add to the linked list. */
485 mtail = &me->me_next;
488 #endif /* MOUNTED_GETMNTINFO2 */
490 #ifdef MOUNTED_GETMNT /* Ultrix. */
497 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
500 me = xmalloc (sizeof *me);
501 me->me_devname = xstrdup (fsd.fd_req.devname);
502 me->me_mountdir = xstrdup (fsd.fd_req.path);
503 me->me_type = gt_names[fsd.fd_req.fstype];
504 me->me_type_malloced = 0;
505 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
506 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
507 me->me_dev = fsd.fd_req.dev;
509 /* Add to the linked list. */
511 mtail = &me->me_next;
516 #endif /* MOUNTED_GETMNT. */
518 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
520 /* The next_dev() and fs_stat_dev() system calls give the list of
521 all file systems, including the information returned by statvfs()
522 (fs type, total blocks, free blocks etc.), but without the mount
523 point. But on BeOS all file systems except / are mounted in the
524 rootfs, directly under /.
525 The directory name of the mount point is often, but not always,
526 identical to the volume name of the device.
527 We therefore get the list of subdirectories of /, and the list
528 of all file systems, and match the two lists. */
536 struct rootdir_entry *next;
538 struct rootdir_entry *rootdir_list;
539 struct rootdir_entry **rootdir_tail;
544 /* All volumes are mounted in the rootfs, directly under /. */
546 rootdir_tail = &rootdir_list;
547 dirp = opendir ("/");
552 while ((d = readdir (dirp)) != NULL)
557 if (strcmp (d->d_name, "..") == 0)
560 if (strcmp (d->d_name, ".") == 0)
561 name = xstrdup ("/");
564 name = xmalloc (1 + strlen (d->d_name) + 1);
566 strcpy (name + 1, d->d_name);
569 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
571 struct rootdir_entry *re = xmalloc (sizeof *re);
573 re->dev = statbuf.st_dev;
574 re->ino = statbuf.st_ino;
576 /* Add to the linked list. */
578 rootdir_tail = &re->next;
585 *rootdir_tail = NULL;
587 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
588 if (fs_stat_dev (dev, &fi) >= 0)
590 /* Note: fi.dev == dev. */
591 struct rootdir_entry *re;
593 for (re = rootdir_list; re; re = re->next)
594 if (re->dev == fi.dev && re->ino == fi.root)
597 me = xmalloc (sizeof *me);
598 me->me_devname = xstrdup (fi.device_name[0] != '\0'
599 ? fi.device_name : fi.fsh_name);
600 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
601 me->me_type = xstrdup (fi.fsh_name);
602 me->me_type_malloced = 1;
605 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
607 /* Add to the linked list. */
609 mtail = &me->me_next;
613 while (rootdir_list != NULL)
615 struct rootdir_entry *re = rootdir_list;
616 rootdir_list = re->next;
621 #endif /* MOUNTED_FS_STAT_DEV */
623 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
627 struct statfs *stats;
629 numsys = getfsstat (NULL, 0L, MNT_NOWAIT);
632 if (SIZE_MAX / sizeof *stats <= numsys)
635 bufsize = (1 + numsys) * sizeof *stats;
636 stats = xmalloc (bufsize);
637 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
645 for (counter = 0; counter < numsys; counter++)
647 me = xmalloc (sizeof *me);
648 me->me_devname = xstrdup (stats[counter].f_mntfromname);
649 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
650 me->me_type = xstrdup (FS_TYPE (stats[counter]));
651 me->me_type_malloced = 1;
652 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
653 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
654 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
656 /* Add to the linked list. */
658 mtail = &me->me_next;
663 #endif /* MOUNTED_GETFSSTAT */
665 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
668 char *table = "/etc/mnttab";
671 fp = fopen (table, "r");
675 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
677 me = xmalloc (sizeof *me);
678 # ifdef GETFSTYP /* SVR3. */
679 me->me_devname = xstrdup (mnt.mt_dev);
681 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
682 strcpy (me->me_devname, "/dev/");
683 strcpy (me->me_devname + 5, mnt.mt_dev);
685 me->me_mountdir = xstrdup (mnt.mt_filsys);
686 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
688 me->me_type_malloced = 0;
689 # ifdef GETFSTYP /* SVR3. */
693 char typebuf[FSTYPSZ];
695 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
696 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
698 me->me_type = xstrdup (typebuf);
699 me->me_type_malloced = 1;
703 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
704 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
706 /* Add to the linked list. */
708 mtail = &me->me_next;
713 /* The last fread() call must have failed. */
714 int saved_errno = errno;
720 if (fclose (fp) == EOF)
723 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
725 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
727 struct mntent **mnttbl = getmnttbl (), **ent;
728 for (ent = mnttbl; *ent; ent++)
730 me = xmalloc (sizeof *me);
731 me->me_devname = xstrdup ((*ent)->mt_resource);
732 me->me_mountdir = xstrdup ((*ent)->mt_directory);
733 me->me_type = xstrdup ((*ent)->mt_fstype);
734 me->me_type_malloced = 1;
735 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
736 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
737 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
739 /* Add to the linked list. */
741 mtail = &me->me_next;
747 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
750 char *table = MNTTAB;
755 # if defined F_RDLCK && defined F_SETLKW
756 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
757 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
758 for this file name, we should use their macro name instead.
759 (Why not just lock MNTTAB directly? We don't know.) */
761 # define MNTTAB_LOCK "/etc/.mnttab.lock"
763 lockfd = open (MNTTAB_LOCK, O_RDONLY);
767 flock.l_type = F_RDLCK;
768 flock.l_whence = SEEK_SET;
771 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
774 int saved_errno = errno;
780 else if (errno != ENOENT)
785 fp = fopen (table, "r");
790 while ((ret = getmntent (fp, &mnt)) == 0)
792 me = xmalloc (sizeof *me);
793 me->me_devname = xstrdup (mnt.mnt_special);
794 me->me_mountdir = xstrdup (mnt.mnt_mountp);
795 me->me_type = xstrdup (mnt.mnt_fstype);
796 me->me_type_malloced = 1;
797 me->me_dummy = MNT_IGNORE (&mnt) != 0;
798 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
799 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
801 /* Add to the linked list. */
803 mtail = &me->me_next;
806 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
809 if (0 <= lockfd && close (lockfd) != 0)
818 #endif /* MOUNTED_GETMNTENT2. */
820 #ifdef MOUNTED_VMOUNT /* AIX. */
823 char *entries, *thisent;
828 /* Ask how many bytes to allocate for the mounted file system info. */
829 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
831 entries = xmalloc (bufsize);
833 /* Get the list of mounted file systems. */
834 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
837 int saved_errno = errno;
843 for (i = 0, thisent = entries;
845 i++, thisent += vmp->vmt_length)
847 char *options, *ignore;
849 vmp = (struct vmount *) thisent;
850 me = xmalloc (sizeof *me);
851 if (vmp->vmt_flags & MNT_REMOTE)
856 /* Prepend the remote dirname. */
857 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
858 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
859 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
860 strcpy (me->me_devname, host);
861 strcat (me->me_devname, ":");
862 strcat (me->me_devname, dir);
867 me->me_devname = xstrdup (thisent +
868 vmp->vmt_data[VMT_OBJECT].vmt_off);
870 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
871 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
872 me->me_type_malloced = 1;
873 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
874 ignore = strstr (options, "ignore");
875 me->me_dummy = (ignore
876 && (ignore == options || ignore[-1] == ',')
877 && (ignore[sizeof "ignore" - 1] == ','
878 || ignore[sizeof "ignore" - 1] == '\0'));
879 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
881 /* Add to the linked list. */
883 mtail = &me->me_next;
887 #endif /* MOUNTED_VMOUNT. */
889 #ifdef MOUNTED_INTERIX_STATVFS
891 DIR *dirp = opendir ("/dev/fs");
892 char node[9 + NAME_MAX];
901 struct dirent *result;
903 if (readdir_r (dirp, &entry, &result) || result == NULL)
906 strcpy (node, "/dev/fs/");
907 strcat (node, entry.d_name);
909 if (statvfs (node, &dev) == 0)
911 me = xmalloc (sizeof *me);
912 me->me_devname = xstrdup (dev.f_mntfromname);
913 me->me_mountdir = xstrdup (dev.f_mntonname);
914 me->me_type = xstrdup (dev.f_fstypename);
915 me->me_type_malloced = 1;
916 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
917 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
918 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
920 /* Add to the linked list. */
922 mtail = &me->me_next;
926 #endif /* MOUNTED_INTERIX_STATVFS */
934 int saved_errno = errno;
939 me = mount_list->me_next;
940 free (mount_list->me_devname);
941 free (mount_list->me_mountdir);
942 if (mount_list->me_type_malloced)
943 free (mount_list->me_type);