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 /* A file system is `remote' if its Fs_name contains a `:'
161 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
162 # define ME_REMOTE(Fs_name, Fs_type) \
163 (strchr (Fs_name, ':') != NULL \
164 || ((Fs_name)[0] == '/' \
165 && (Fs_name)[1] == '/' \
166 && (strcmp (Fs_type, "smbfs") == 0 \
167 || strcmp (Fs_type, "cifs") == 0)))
170 #if MOUNTED_GETMNTINFO
172 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
174 fstype_to_string (short int t)
269 fsp_to_string (const struct statfs *fsp)
271 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
272 return (char *) (fsp->f_fstypename);
274 return fstype_to_string (fsp->f_type);
278 #endif /* MOUNTED_GETMNTINFO */
280 #ifdef MOUNTED_VMOUNT /* AIX. */
282 fstype_to_string (int t)
286 e = getvfsbytype (t);
287 if (!e || !e->vfsent_name)
290 return e->vfsent_name;
292 #endif /* MOUNTED_VMOUNT */
295 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
297 /* Return the device number from MOUNT_OPTIONS, if possible.
298 Otherwise return (dev_t) -1. */
300 dev_from_mount_options (char const *mount_options)
302 /* GNU/Linux allows file system implementations to define their own
303 meaning for "dev=" mount options, so don't trust the meaning
307 static char const dev_pattern[] = ",dev=";
308 char const *devopt = strstr (mount_options, dev_pattern);
312 char const *optval = devopt + sizeof dev_pattern - 1;
314 unsigned long int dev;
316 dev = strtoul (optval, &optvalend, 16);
317 if (optval != optvalend
318 && (*optvalend == '\0' || *optvalend == ',')
319 && ! (dev == ULONG_MAX && errno == ERANGE)
320 && dev == (dev_t) dev)
325 (void) mount_options;
331 /* Return a list of the currently mounted file systems, or NULL on error.
332 Add each entry to the tail of the list so that they stay in order.
333 If NEED_FS_TYPE is true, ensure that the file system type fields in
334 the returned list are valid. Otherwise, they might not be. */
337 read_file_system_list (bool need_fs_type)
339 struct mount_entry *mount_list;
340 struct mount_entry *me;
341 struct mount_entry **mtail = &mount_list;
344 #ifdef MOUNTED_LISTMNTENT
346 struct tabmntent *mntlist, *p;
348 struct mount_entry *me;
350 /* the third and fourth arguments could be used to filter mounts,
351 but Crays doesn't seem to have any mounts that we want to
352 remove. Specifically, automount create normal NFS mounts.
355 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
357 for (p = mntlist; p; p = p->next) {
359 me = xmalloc (sizeof *me);
360 me->me_devname = xstrdup (mnt->mnt_fsname);
361 me->me_mountdir = xstrdup (mnt->mnt_dir);
362 me->me_type = xstrdup (mnt->mnt_type);
363 me->me_type_malloced = 1;
364 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
365 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
368 mtail = &me->me_next;
370 freemntlist (mntlist);
374 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
377 char const *table = MOUNTED;
380 fp = setmntent (table, "r");
384 while ((mnt = getmntent (fp)))
386 me = xmalloc (sizeof *me);
387 me->me_devname = xstrdup (mnt->mnt_fsname);
388 me->me_mountdir = xstrdup (mnt->mnt_dir);
389 me->me_type = xstrdup (mnt->mnt_type);
390 me->me_type_malloced = 1;
391 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
392 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
393 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
395 /* Add to the linked list. */
397 mtail = &me->me_next;
400 if (endmntent (fp) == 0)
403 #endif /* MOUNTED_GETMNTENT1. */
405 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
410 entries = getmntinfo (&fsp, MNT_NOWAIT);
413 for (; entries-- > 0; fsp++)
415 char *fs_type = fsp_to_string (fsp);
417 me = xmalloc (sizeof *me);
418 me->me_devname = xstrdup (fsp->f_mntfromname);
419 me->me_mountdir = xstrdup (fsp->f_mntonname);
420 me->me_type = fs_type;
421 me->me_type_malloced = 0;
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_t) -1; /* Magic; means not known yet. */
426 /* Add to the linked list. */
428 mtail = &me->me_next;
431 #endif /* MOUNTED_GETMNTINFO */
433 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
438 entries = getmntinfo (&fsp, MNT_NOWAIT);
441 for (; entries-- > 0; fsp++)
443 me = xmalloc (sizeof *me);
444 me->me_devname = xstrdup (fsp->f_mntfromname);
445 me->me_mountdir = xstrdup (fsp->f_mntonname);
446 me->me_type = xstrdup (fsp->f_fstypename);
447 me->me_type_malloced = 1;
448 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
449 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
450 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
452 /* Add to the linked list. */
454 mtail = &me->me_next;
457 #endif /* MOUNTED_GETMNTINFO2 */
459 #ifdef MOUNTED_GETMNT /* Ultrix. */
466 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
469 me = xmalloc (sizeof *me);
470 me->me_devname = xstrdup (fsd.fd_req.devname);
471 me->me_mountdir = xstrdup (fsd.fd_req.path);
472 me->me_type = gt_names[fsd.fd_req.fstype];
473 me->me_type_malloced = 0;
474 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
475 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
476 me->me_dev = fsd.fd_req.dev;
478 /* Add to the linked list. */
480 mtail = &me->me_next;
485 #endif /* MOUNTED_GETMNT. */
487 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
489 /* The next_dev() and fs_stat_dev() system calls give the list of
490 all file systems, including the information returned by statvfs()
491 (fs type, total blocks, free blocks etc.), but without the mount
492 point. But on BeOS all file systems except / are mounted in the
493 rootfs, directly under /.
494 The directory name of the mount point is often, but not always,
495 identical to the volume name of the device.
496 We therefore get the list of subdirectories of /, and the list
497 of all file systems, and match the two lists. */
505 struct rootdir_entry *next;
507 struct rootdir_entry *rootdir_list;
508 struct rootdir_entry **rootdir_tail;
513 /* All volumes are mounted in the rootfs, directly under /. */
515 rootdir_tail = &rootdir_list;
516 dirp = opendir ("/");
521 while ((d = readdir (dirp)) != NULL)
526 if (strcmp (d->d_name, "..") == 0)
529 if (strcmp (d->d_name, ".") == 0)
530 name = xstrdup ("/");
533 name = xmalloc (1 + strlen (d->d_name) + 1);
535 strcpy (name + 1, d->d_name);
538 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
540 struct rootdir_entry *re = xmalloc (sizeof *re);
542 re->dev = statbuf.st_dev;
543 re->ino = statbuf.st_ino;
545 /* Add to the linked list. */
547 rootdir_tail = &re->next;
554 *rootdir_tail = NULL;
556 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
557 if (fs_stat_dev (dev, &fi) >= 0)
559 /* Note: fi.dev == dev. */
560 struct rootdir_entry *re;
562 for (re = rootdir_list; re; re = re->next)
563 if (re->dev == fi.dev && re->ino == fi.root)
566 me = xmalloc (sizeof *me);
567 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
568 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
569 me->me_type = xstrdup (fi.fsh_name);
570 me->me_type_malloced = 1;
573 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
575 /* Add to the linked list. */
577 mtail = &me->me_next;
581 while (rootdir_list != NULL)
583 struct rootdir_entry *re = rootdir_list;
584 rootdir_list = re->next;
589 #endif /* MOUNTED_FS_STAT_DEV */
591 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
595 struct statfs *stats;
597 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
600 if (SIZE_MAX / sizeof *stats <= numsys)
603 bufsize = (1 + numsys) * sizeof *stats;
604 stats = xmalloc (bufsize);
605 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
613 for (counter = 0; counter < numsys; counter++)
615 me = xmalloc (sizeof *me);
616 me->me_devname = xstrdup (stats[counter].f_mntfromname);
617 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
618 me->me_type = xstrdup (FS_TYPE (stats[counter]));
619 me->me_type_malloced = 1;
620 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
621 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
622 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
624 /* Add to the linked list. */
626 mtail = &me->me_next;
631 #endif /* MOUNTED_GETFSSTAT */
633 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
636 char *table = "/etc/mnttab";
639 fp = fopen (table, "r");
643 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
645 me = xmalloc (sizeof *me);
646 # ifdef GETFSTYP /* SVR3. */
647 me->me_devname = xstrdup (mnt.mt_dev);
649 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
650 strcpy (me->me_devname, "/dev/");
651 strcpy (me->me_devname + 5, mnt.mt_dev);
653 me->me_mountdir = xstrdup (mnt.mt_filsys);
654 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
656 me->me_type_malloced = 0;
657 # ifdef GETFSTYP /* SVR3. */
661 char typebuf[FSTYPSZ];
663 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
664 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
666 me->me_type = xstrdup (typebuf);
667 me->me_type_malloced = 1;
671 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
672 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
674 /* Add to the linked list. */
676 mtail = &me->me_next;
681 /* The last fread() call must have failed. */
682 int saved_errno = errno;
688 if (fclose (fp) == EOF)
691 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
693 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
695 struct mntent **mnttbl = getmnttbl (), **ent;
696 for (ent=mnttbl;*ent;ent++)
698 me = xmalloc (sizeof *me);
699 me->me_devname = xstrdup ( (*ent)->mt_resource);
700 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
701 me->me_type = xstrdup ((*ent)->mt_fstype);
702 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);
705 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
707 /* Add to the linked list. */
709 mtail = &me->me_next;
715 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
718 char *table = MNTTAB;
723 # if defined F_RDLCK && defined F_SETLKW
724 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
725 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
726 for this file name, we should use their macro name instead.
727 (Why not just lock MNTTAB directly? We don't know.) */
729 # define MNTTAB_LOCK "/etc/.mnttab.lock"
731 lockfd = open (MNTTAB_LOCK, O_RDONLY);
735 flock.l_type = F_RDLCK;
736 flock.l_whence = SEEK_SET;
739 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
742 int saved_errno = errno;
748 else if (errno != ENOENT)
753 fp = fopen (table, "r");
758 while ((ret = getmntent (fp, &mnt)) == 0)
760 me = xmalloc (sizeof *me);
761 me->me_devname = xstrdup (mnt.mnt_special);
762 me->me_mountdir = xstrdup (mnt.mnt_mountp);
763 me->me_type = xstrdup (mnt.mnt_fstype);
764 me->me_type_malloced = 1;
765 me->me_dummy = MNT_IGNORE (&mnt) != 0;
766 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
767 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
769 /* Add to the linked list. */
771 mtail = &me->me_next;
774 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
777 if (0 <= lockfd && close (lockfd) != 0)
786 #endif /* MOUNTED_GETMNTENT2. */
788 #ifdef MOUNTED_VMOUNT /* AIX. */
791 char *entries, *thisent;
796 /* Ask how many bytes to allocate for the mounted file system info. */
797 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
799 entries = xmalloc (bufsize);
801 /* Get the list of mounted file systems. */
802 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
805 int saved_errno = errno;
811 for (i = 0, thisent = entries;
813 i++, thisent += vmp->vmt_length)
815 char *options, *ignore;
817 vmp = (struct vmount *) thisent;
818 me = xmalloc (sizeof *me);
819 if (vmp->vmt_flags & MNT_REMOTE)
824 /* Prepend the remote dirname. */
825 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
826 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
827 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
828 strcpy (me->me_devname, host);
829 strcat (me->me_devname, ":");
830 strcat (me->me_devname, dir);
835 me->me_devname = xstrdup (thisent +
836 vmp->vmt_data[VMT_OBJECT].vmt_off);
838 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
839 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
840 me->me_type_malloced = 1;
841 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
842 ignore = strstr (options, "ignore");
843 me->me_dummy = (ignore
844 && (ignore == options || ignore[-1] == ',')
845 && (ignore[sizeof "ignore" - 1] == ','
846 || ignore[sizeof "ignore" - 1] == '\0'));
847 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
849 /* Add to the linked list. */
851 mtail = &me->me_next;
855 #endif /* MOUNTED_VMOUNT. */
863 int saved_errno = errno;
868 me = mount_list->me_next;
869 free (mount_list->me_devname);
870 free (mount_list->me_mountdir);
871 if (mount_list->me_type_malloced)
872 free (mount_list->me_type);