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) {
389 me = xmalloc (sizeof *me);
390 me->me_devname = xstrdup (mnt->mnt_fsname);
391 me->me_mountdir = xstrdup (mnt->mnt_dir);
392 me->me_type = xstrdup (mnt->mnt_type);
393 me->me_type_malloced = 1;
394 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
395 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
398 mtail = &me->me_next;
400 freemntlist (mntlist);
404 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
407 char const *table = MOUNTED;
410 fp = setmntent (table, "r");
414 while ((mnt = getmntent (fp)))
416 me = xmalloc (sizeof *me);
417 me->me_devname = xstrdup (mnt->mnt_fsname);
418 me->me_mountdir = xstrdup (mnt->mnt_dir);
419 me->me_type = xstrdup (mnt->mnt_type);
420 me->me_type_malloced = 1;
421 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
422 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
423 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
425 /* Add to the linked list. */
427 mtail = &me->me_next;
430 if (endmntent (fp) == 0)
433 #endif /* MOUNTED_GETMNTENT1. */
435 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
440 entries = getmntinfo (&fsp, MNT_NOWAIT);
443 for (; entries-- > 0; fsp++)
445 char *fs_type = fsp_to_string (fsp);
447 me = xmalloc (sizeof *me);
448 me->me_devname = xstrdup (fsp->f_mntfromname);
449 me->me_mountdir = xstrdup (fsp->f_mntonname);
450 me->me_type = fs_type;
451 me->me_type_malloced = 0;
452 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
453 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
454 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
456 /* Add to the linked list. */
458 mtail = &me->me_next;
461 #endif /* MOUNTED_GETMNTINFO */
463 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
468 entries = getmntinfo (&fsp, MNT_NOWAIT);
471 for (; entries-- > 0; fsp++)
473 me = xmalloc (sizeof *me);
474 me->me_devname = xstrdup (fsp->f_mntfromname);
475 me->me_mountdir = xstrdup (fsp->f_mntonname);
476 me->me_type = xstrdup (fsp->f_fstypename);
477 me->me_type_malloced = 1;
478 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
479 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
480 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
482 /* Add to the linked list. */
484 mtail = &me->me_next;
487 #endif /* MOUNTED_GETMNTINFO2 */
489 #ifdef MOUNTED_GETMNT /* Ultrix. */
496 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
499 me = xmalloc (sizeof *me);
500 me->me_devname = xstrdup (fsd.fd_req.devname);
501 me->me_mountdir = xstrdup (fsd.fd_req.path);
502 me->me_type = gt_names[fsd.fd_req.fstype];
503 me->me_type_malloced = 0;
504 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
505 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
506 me->me_dev = fsd.fd_req.dev;
508 /* Add to the linked list. */
510 mtail = &me->me_next;
515 #endif /* MOUNTED_GETMNT. */
517 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
519 /* The next_dev() and fs_stat_dev() system calls give the list of
520 all file systems, including the information returned by statvfs()
521 (fs type, total blocks, free blocks etc.), but without the mount
522 point. But on BeOS all file systems except / are mounted in the
523 rootfs, directly under /.
524 The directory name of the mount point is often, but not always,
525 identical to the volume name of the device.
526 We therefore get the list of subdirectories of /, and the list
527 of all file systems, and match the two lists. */
535 struct rootdir_entry *next;
537 struct rootdir_entry *rootdir_list;
538 struct rootdir_entry **rootdir_tail;
543 /* All volumes are mounted in the rootfs, directly under /. */
545 rootdir_tail = &rootdir_list;
546 dirp = opendir ("/");
551 while ((d = readdir (dirp)) != NULL)
556 if (strcmp (d->d_name, "..") == 0)
559 if (strcmp (d->d_name, ".") == 0)
560 name = xstrdup ("/");
563 name = xmalloc (1 + strlen (d->d_name) + 1);
565 strcpy (name + 1, d->d_name);
568 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
570 struct rootdir_entry *re = xmalloc (sizeof *re);
572 re->dev = statbuf.st_dev;
573 re->ino = statbuf.st_ino;
575 /* Add to the linked list. */
577 rootdir_tail = &re->next;
584 *rootdir_tail = NULL;
586 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
587 if (fs_stat_dev (dev, &fi) >= 0)
589 /* Note: fi.dev == dev. */
590 struct rootdir_entry *re;
592 for (re = rootdir_list; re; re = re->next)
593 if (re->dev == fi.dev && re->ino == fi.root)
596 me = xmalloc (sizeof *me);
597 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
598 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
599 me->me_type = xstrdup (fi.fsh_name);
600 me->me_type_malloced = 1;
603 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
605 /* Add to the linked list. */
607 mtail = &me->me_next;
611 while (rootdir_list != NULL)
613 struct rootdir_entry *re = rootdir_list;
614 rootdir_list = re->next;
619 #endif /* MOUNTED_FS_STAT_DEV */
621 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
625 struct statfs *stats;
627 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
630 if (SIZE_MAX / sizeof *stats <= numsys)
633 bufsize = (1 + numsys) * sizeof *stats;
634 stats = xmalloc (bufsize);
635 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
643 for (counter = 0; counter < numsys; counter++)
645 me = xmalloc (sizeof *me);
646 me->me_devname = xstrdup (stats[counter].f_mntfromname);
647 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
648 me->me_type = xstrdup (FS_TYPE (stats[counter]));
649 me->me_type_malloced = 1;
650 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
651 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
652 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
654 /* Add to the linked list. */
656 mtail = &me->me_next;
661 #endif /* MOUNTED_GETFSSTAT */
663 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
666 char *table = "/etc/mnttab";
669 fp = fopen (table, "r");
673 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
675 me = xmalloc (sizeof *me);
676 # ifdef GETFSTYP /* SVR3. */
677 me->me_devname = xstrdup (mnt.mt_dev);
679 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
680 strcpy (me->me_devname, "/dev/");
681 strcpy (me->me_devname + 5, mnt.mt_dev);
683 me->me_mountdir = xstrdup (mnt.mt_filsys);
684 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
686 me->me_type_malloced = 0;
687 # ifdef GETFSTYP /* SVR3. */
691 char typebuf[FSTYPSZ];
693 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
694 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
696 me->me_type = xstrdup (typebuf);
697 me->me_type_malloced = 1;
701 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
702 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
704 /* Add to the linked list. */
706 mtail = &me->me_next;
711 /* The last fread() call must have failed. */
712 int saved_errno = errno;
718 if (fclose (fp) == EOF)
721 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
723 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
725 struct mntent **mnttbl = getmnttbl (), **ent;
726 for (ent=mnttbl;*ent;ent++)
728 me = xmalloc (sizeof *me);
729 me->me_devname = xstrdup ( (*ent)->mt_resource);
730 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
731 me->me_type = xstrdup ((*ent)->mt_fstype);
732 me->me_type_malloced = 1;
733 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
734 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
735 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
737 /* Add to the linked list. */
739 mtail = &me->me_next;
745 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
748 char *table = MNTTAB;
753 # if defined F_RDLCK && defined F_SETLKW
754 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
755 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
756 for this file name, we should use their macro name instead.
757 (Why not just lock MNTTAB directly? We don't know.) */
759 # define MNTTAB_LOCK "/etc/.mnttab.lock"
761 lockfd = open (MNTTAB_LOCK, O_RDONLY);
765 flock.l_type = F_RDLCK;
766 flock.l_whence = SEEK_SET;
769 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
772 int saved_errno = errno;
778 else if (errno != ENOENT)
783 fp = fopen (table, "r");
788 while ((ret = getmntent (fp, &mnt)) == 0)
790 me = xmalloc (sizeof *me);
791 me->me_devname = xstrdup (mnt.mnt_special);
792 me->me_mountdir = xstrdup (mnt.mnt_mountp);
793 me->me_type = xstrdup (mnt.mnt_fstype);
794 me->me_type_malloced = 1;
795 me->me_dummy = MNT_IGNORE (&mnt) != 0;
796 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
797 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
799 /* Add to the linked list. */
801 mtail = &me->me_next;
804 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
807 if (0 <= lockfd && close (lockfd) != 0)
816 #endif /* MOUNTED_GETMNTENT2. */
818 #ifdef MOUNTED_VMOUNT /* AIX. */
821 char *entries, *thisent;
826 /* Ask how many bytes to allocate for the mounted file system info. */
827 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
829 entries = xmalloc (bufsize);
831 /* Get the list of mounted file systems. */
832 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
835 int saved_errno = errno;
841 for (i = 0, thisent = entries;
843 i++, thisent += vmp->vmt_length)
845 char *options, *ignore;
847 vmp = (struct vmount *) thisent;
848 me = xmalloc (sizeof *me);
849 if (vmp->vmt_flags & MNT_REMOTE)
854 /* Prepend the remote dirname. */
855 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
856 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
857 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
858 strcpy (me->me_devname, host);
859 strcat (me->me_devname, ":");
860 strcat (me->me_devname, dir);
865 me->me_devname = xstrdup (thisent +
866 vmp->vmt_data[VMT_OBJECT].vmt_off);
868 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
869 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
870 me->me_type_malloced = 1;
871 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
872 ignore = strstr (options, "ignore");
873 me->me_dummy = (ignore
874 && (ignore == options || ignore[-1] == ',')
875 && (ignore[sizeof "ignore" - 1] == ','
876 || ignore[sizeof "ignore" - 1] == '\0'));
877 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
879 /* Add to the linked list. */
881 mtail = &me->me_next;
885 #endif /* MOUNTED_VMOUNT. */
887 #ifdef MOUNTED_INTERIX_STATVFS
889 DIR *dirp = opendir ("/dev/fs");
890 char node[9 + NAME_MAX];
899 struct dirent *result;
901 if (readdir_r (dirp, &entry, &result) || result == NULL)
904 strcpy (node, "/dev/fs/");
905 strcat (node, entry.d_name);
907 if (statvfs (node, &dev) == 0)
909 me = xmalloc (sizeof *me);
910 me->me_devname = xstrdup (dev.f_mntfromname);
911 me->me_mountdir = xstrdup (dev.f_mntonname);
912 me->me_type = xstrdup (dev.f_fstypename);
913 me->me_type_malloced = 1;
914 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
915 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
916 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
918 /* Add to the linked list. */
920 mtail = &me->me_next;
924 #endif /* MOUNTED_INTERIX_STATVFS */
932 int saved_errno = errno;
937 me = mount_list->me_next;
938 free (mount_list->me_devname);
939 free (mount_list->me_mountdir);
940 if (mount_list->me_type_malloced)
941 free (mount_list->me_type);