1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991-1992, 1997-2010 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>
116 /* So special that it's not worth putting this in autoconf. */
117 # undef MOUNTED_FREAD_FSTYP
118 # define MOUNTED_GETMNTTBL
121 #if HAVE_SYS_MNTENT_H
122 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
123 # include <sys/mntent.h>
127 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
128 # define MNT_IGNORE(M) hasmntopt (M, MNTOPT_IGNORE)
130 # define MNT_IGNORE(M) 0
134 # include "unlocked-io.h"
137 /* The results of open() in this file are not used with fchdir,
138 therefore save some unnecessary work in fchdir.c. */
142 /* The results of opendir() in this file are not used with dirfd and fchdir,
143 therefore save some unnecessary work in fchdir.c. */
148 # define ME_DUMMY(Fs_name, Fs_type) \
149 (strcmp (Fs_type, "autofs") == 0 \
150 || strcmp (Fs_type, "none") == 0 \
151 || strcmp (Fs_type, "proc") == 0 \
152 || strcmp (Fs_type, "subfs") == 0 \
153 /* for NetBSD 3.0 */ \
154 || strcmp (Fs_type, "kernfs") == 0 \
156 || strcmp (Fs_type, "ignore") == 0)
160 # include <windows.h>
161 # define ME_REMOTE me_remote
162 /* All cygwin mount points include `:' or start with `//'; so it
163 requires a native Windows call to determine remote disks. */
165 me_remote (char const *fs_name, char const *fs_type _GL_UNUSED)
167 if (fs_name[0] && fs_name[1] == ':')
169 char const drive[3] = { fs_name[0], ':', '\0' };
170 switch (GetDriveType (drive))
172 case DRIVE_REMOVABLE:
184 /* A file system is `remote' if its Fs_name contains a `:'
185 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
186 # define ME_REMOTE(Fs_name, Fs_type) \
187 (strchr (Fs_name, ':') != NULL \
188 || ((Fs_name)[0] == '/' \
189 && (Fs_name)[1] == '/' \
190 && (strcmp (Fs_type, "smbfs") == 0 \
191 || strcmp (Fs_type, "cifs") == 0)))
194 #if MOUNTED_GETMNTINFO
196 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
198 fstype_to_string (short int t)
293 fsp_to_string (const struct statfs *fsp)
295 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
296 return (char *) (fsp->f_fstypename);
298 return fstype_to_string (fsp->f_type);
302 #endif /* MOUNTED_GETMNTINFO */
304 #ifdef MOUNTED_VMOUNT /* AIX. */
306 fstype_to_string (int t)
310 e = getvfsbytype (t);
311 if (!e || !e->vfsent_name)
314 return e->vfsent_name;
316 #endif /* MOUNTED_VMOUNT */
319 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
321 /* Return the device number from MOUNT_OPTIONS, if possible.
322 Otherwise return (dev_t) -1. */
324 dev_from_mount_options (char const *mount_options)
326 /* GNU/Linux allows file system implementations to define their own
327 meaning for "dev=" mount options, so don't trust the meaning
331 static char const dev_pattern[] = ",dev=";
332 char const *devopt = strstr (mount_options, dev_pattern);
336 char const *optval = devopt + sizeof dev_pattern - 1;
338 unsigned long int dev;
340 dev = strtoul (optval, &optvalend, 16);
341 if (optval != optvalend
342 && (*optvalend == '\0' || *optvalend == ',')
343 && ! (dev == ULONG_MAX && errno == ERANGE)
344 && dev == (dev_t) dev)
349 (void) mount_options;
355 /* Return a list of the currently mounted file systems, or NULL on error.
356 Add each entry to the tail of the list so that they stay in order.
357 If NEED_FS_TYPE is true, ensure that the file system type fields in
358 the returned list are valid. Otherwise, they might not be. */
361 read_file_system_list (bool need_fs_type)
363 struct mount_entry *mount_list;
364 struct mount_entry *me;
365 struct mount_entry **mtail = &mount_list;
368 #ifdef MOUNTED_LISTMNTENT
370 struct tabmntent *mntlist, *p;
372 struct mount_entry *me;
374 /* the third and fourth arguments could be used to filter mounts,
375 but Crays doesn't seem to have any mounts that we want to
376 remove. Specifically, automount create normal NFS mounts.
379 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
381 for (p = mntlist; p; p = p->next) {
383 me = xmalloc (sizeof *me);
384 me->me_devname = xstrdup (mnt->mnt_fsname);
385 me->me_mountdir = xstrdup (mnt->mnt_dir);
386 me->me_type = xstrdup (mnt->mnt_type);
387 me->me_type_malloced = 1;
388 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
389 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
392 mtail = &me->me_next;
394 freemntlist (mntlist);
398 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
401 char const *table = MOUNTED;
404 fp = setmntent (table, "r");
408 while ((mnt = getmntent (fp)))
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);
417 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
419 /* Add to the linked list. */
421 mtail = &me->me_next;
424 if (endmntent (fp) == 0)
427 #endif /* MOUNTED_GETMNTENT1. */
429 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
434 entries = getmntinfo (&fsp, MNT_NOWAIT);
437 for (; entries-- > 0; fsp++)
439 char *fs_type = fsp_to_string (fsp);
441 me = xmalloc (sizeof *me);
442 me->me_devname = xstrdup (fsp->f_mntfromname);
443 me->me_mountdir = xstrdup (fsp->f_mntonname);
444 me->me_type = fs_type;
445 me->me_type_malloced = 0;
446 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
447 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
448 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
450 /* Add to the linked list. */
452 mtail = &me->me_next;
455 #endif /* MOUNTED_GETMNTINFO */
457 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
462 entries = getmntinfo (&fsp, MNT_NOWAIT);
465 for (; entries-- > 0; fsp++)
467 me = xmalloc (sizeof *me);
468 me->me_devname = xstrdup (fsp->f_mntfromname);
469 me->me_mountdir = xstrdup (fsp->f_mntonname);
470 me->me_type = xstrdup (fsp->f_fstypename);
471 me->me_type_malloced = 1;
472 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
473 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
474 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
476 /* Add to the linked list. */
478 mtail = &me->me_next;
481 #endif /* MOUNTED_GETMNTINFO2 */
483 #ifdef MOUNTED_GETMNT /* Ultrix. */
490 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
493 me = xmalloc (sizeof *me);
494 me->me_devname = xstrdup (fsd.fd_req.devname);
495 me->me_mountdir = xstrdup (fsd.fd_req.path);
496 me->me_type = gt_names[fsd.fd_req.fstype];
497 me->me_type_malloced = 0;
498 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
499 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
500 me->me_dev = fsd.fd_req.dev;
502 /* Add to the linked list. */
504 mtail = &me->me_next;
509 #endif /* MOUNTED_GETMNT. */
511 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
513 /* The next_dev() and fs_stat_dev() system calls give the list of
514 all file systems, including the information returned by statvfs()
515 (fs type, total blocks, free blocks etc.), but without the mount
516 point. But on BeOS all file systems except / are mounted in the
517 rootfs, directly under /.
518 The directory name of the mount point is often, but not always,
519 identical to the volume name of the device.
520 We therefore get the list of subdirectories of /, and the list
521 of all file systems, and match the two lists. */
529 struct rootdir_entry *next;
531 struct rootdir_entry *rootdir_list;
532 struct rootdir_entry **rootdir_tail;
537 /* All volumes are mounted in the rootfs, directly under /. */
539 rootdir_tail = &rootdir_list;
540 dirp = opendir ("/");
545 while ((d = readdir (dirp)) != NULL)
550 if (strcmp (d->d_name, "..") == 0)
553 if (strcmp (d->d_name, ".") == 0)
554 name = xstrdup ("/");
557 name = xmalloc (1 + strlen (d->d_name) + 1);
559 strcpy (name + 1, d->d_name);
562 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
564 struct rootdir_entry *re = xmalloc (sizeof *re);
566 re->dev = statbuf.st_dev;
567 re->ino = statbuf.st_ino;
569 /* Add to the linked list. */
571 rootdir_tail = &re->next;
578 *rootdir_tail = NULL;
580 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
581 if (fs_stat_dev (dev, &fi) >= 0)
583 /* Note: fi.dev == dev. */
584 struct rootdir_entry *re;
586 for (re = rootdir_list; re; re = re->next)
587 if (re->dev == fi.dev && re->ino == fi.root)
590 me = xmalloc (sizeof *me);
591 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
592 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
593 me->me_type = xstrdup (fi.fsh_name);
594 me->me_type_malloced = 1;
597 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
599 /* Add to the linked list. */
601 mtail = &me->me_next;
605 while (rootdir_list != NULL)
607 struct rootdir_entry *re = rootdir_list;
608 rootdir_list = re->next;
613 #endif /* MOUNTED_FS_STAT_DEV */
615 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
619 struct statfs *stats;
621 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
624 if (SIZE_MAX / sizeof *stats <= numsys)
627 bufsize = (1 + numsys) * sizeof *stats;
628 stats = xmalloc (bufsize);
629 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
637 for (counter = 0; counter < numsys; counter++)
639 me = xmalloc (sizeof *me);
640 me->me_devname = xstrdup (stats[counter].f_mntfromname);
641 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
642 me->me_type = xstrdup (FS_TYPE (stats[counter]));
643 me->me_type_malloced = 1;
644 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
645 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
646 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
648 /* Add to the linked list. */
650 mtail = &me->me_next;
655 #endif /* MOUNTED_GETFSSTAT */
657 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
660 char *table = "/etc/mnttab";
663 fp = fopen (table, "r");
667 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
669 me = xmalloc (sizeof *me);
670 # ifdef GETFSTYP /* SVR3. */
671 me->me_devname = xstrdup (mnt.mt_dev);
673 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
674 strcpy (me->me_devname, "/dev/");
675 strcpy (me->me_devname + 5, mnt.mt_dev);
677 me->me_mountdir = xstrdup (mnt.mt_filsys);
678 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
680 me->me_type_malloced = 0;
681 # ifdef GETFSTYP /* SVR3. */
685 char typebuf[FSTYPSZ];
687 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
688 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
690 me->me_type = xstrdup (typebuf);
691 me->me_type_malloced = 1;
695 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
696 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
698 /* Add to the linked list. */
700 mtail = &me->me_next;
705 /* The last fread() call must have failed. */
706 int saved_errno = errno;
712 if (fclose (fp) == EOF)
715 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
717 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
719 struct mntent **mnttbl = getmnttbl (), **ent;
720 for (ent=mnttbl;*ent;ent++)
722 me = xmalloc (sizeof *me);
723 me->me_devname = xstrdup ( (*ent)->mt_resource);
724 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
725 me->me_type = xstrdup ((*ent)->mt_fstype);
726 me->me_type_malloced = 1;
727 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
728 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
729 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
731 /* Add to the linked list. */
733 mtail = &me->me_next;
739 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
742 char *table = MNTTAB;
747 # if defined F_RDLCK && defined F_SETLKW
748 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
749 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
750 for this file name, we should use their macro name instead.
751 (Why not just lock MNTTAB directly? We don't know.) */
753 # define MNTTAB_LOCK "/etc/.mnttab.lock"
755 lockfd = open (MNTTAB_LOCK, O_RDONLY);
759 flock.l_type = F_RDLCK;
760 flock.l_whence = SEEK_SET;
763 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
766 int saved_errno = errno;
772 else if (errno != ENOENT)
777 fp = fopen (table, "r");
782 while ((ret = getmntent (fp, &mnt)) == 0)
784 me = xmalloc (sizeof *me);
785 me->me_devname = xstrdup (mnt.mnt_special);
786 me->me_mountdir = xstrdup (mnt.mnt_mountp);
787 me->me_type = xstrdup (mnt.mnt_fstype);
788 me->me_type_malloced = 1;
789 me->me_dummy = MNT_IGNORE (&mnt) != 0;
790 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
791 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
793 /* Add to the linked list. */
795 mtail = &me->me_next;
798 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
801 if (0 <= lockfd && close (lockfd) != 0)
810 #endif /* MOUNTED_GETMNTENT2. */
812 #ifdef MOUNTED_VMOUNT /* AIX. */
815 char *entries, *thisent;
820 /* Ask how many bytes to allocate for the mounted file system info. */
821 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
823 entries = xmalloc (bufsize);
825 /* Get the list of mounted file systems. */
826 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
829 int saved_errno = errno;
835 for (i = 0, thisent = entries;
837 i++, thisent += vmp->vmt_length)
839 char *options, *ignore;
841 vmp = (struct vmount *) thisent;
842 me = xmalloc (sizeof *me);
843 if (vmp->vmt_flags & MNT_REMOTE)
848 /* Prepend the remote dirname. */
849 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
850 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
851 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
852 strcpy (me->me_devname, host);
853 strcat (me->me_devname, ":");
854 strcat (me->me_devname, dir);
859 me->me_devname = xstrdup (thisent +
860 vmp->vmt_data[VMT_OBJECT].vmt_off);
862 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
863 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
864 me->me_type_malloced = 1;
865 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
866 ignore = strstr (options, "ignore");
867 me->me_dummy = (ignore
868 && (ignore == options || ignore[-1] == ',')
869 && (ignore[sizeof "ignore" - 1] == ','
870 || ignore[sizeof "ignore" - 1] == '\0'));
871 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
873 /* Add to the linked list. */
875 mtail = &me->me_next;
879 #endif /* MOUNTED_VMOUNT. */
887 int saved_errno = errno;
892 me = mount_list->me_next;
893 free (mount_list->me_devname);
894 free (mount_list->me_mountdir);
895 if (mount_list->me_type_malloced)
896 free (mount_list->me_type);