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>
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] == ':')
170 sprintf (drive, "%c:\\", fs_name[0]);
171 switch (GetDriveType (drive))
173 case DRIVE_REMOVABLE:
185 /* A file system is `remote' if its Fs_name contains a `:'
186 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
187 # define ME_REMOTE(Fs_name, Fs_type) \
188 (strchr (Fs_name, ':') != NULL \
189 || ((Fs_name)[0] == '/' \
190 && (Fs_name)[1] == '/' \
191 && (strcmp (Fs_type, "smbfs") == 0 \
192 || strcmp (Fs_type, "cifs") == 0)))
195 #if MOUNTED_GETMNTINFO
197 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
199 fstype_to_string (short int t)
294 fsp_to_string (const struct statfs *fsp)
296 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
297 return (char *) (fsp->f_fstypename);
299 return fstype_to_string (fsp->f_type);
303 #endif /* MOUNTED_GETMNTINFO */
305 #ifdef MOUNTED_VMOUNT /* AIX. */
307 fstype_to_string (int t)
311 e = getvfsbytype (t);
312 if (!e || !e->vfsent_name)
315 return e->vfsent_name;
317 #endif /* MOUNTED_VMOUNT */
320 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
322 /* Return the device number from MOUNT_OPTIONS, if possible.
323 Otherwise return (dev_t) -1. */
325 dev_from_mount_options (char const *mount_options)
327 /* GNU/Linux allows file system implementations to define their own
328 meaning for "dev=" mount options, so don't trust the meaning
332 static char const dev_pattern[] = ",dev=";
333 char const *devopt = strstr (mount_options, dev_pattern);
337 char const *optval = devopt + sizeof dev_pattern - 1;
339 unsigned long int dev;
341 dev = strtoul (optval, &optvalend, 16);
342 if (optval != optvalend
343 && (*optvalend == '\0' || *optvalend == ',')
344 && ! (dev == ULONG_MAX && errno == ERANGE)
345 && dev == (dev_t) dev)
350 (void) mount_options;
356 /* Return a list of the currently mounted file systems, or NULL on error.
357 Add each entry to the tail of the list so that they stay in order.
358 If NEED_FS_TYPE is true, ensure that the file system type fields in
359 the returned list are valid. Otherwise, they might not be. */
362 read_file_system_list (bool need_fs_type)
364 struct mount_entry *mount_list;
365 struct mount_entry *me;
366 struct mount_entry **mtail = &mount_list;
369 #ifdef MOUNTED_LISTMNTENT
371 struct tabmntent *mntlist, *p;
373 struct mount_entry *me;
375 /* the third and fourth arguments could be used to filter mounts,
376 but Crays doesn't seem to have any mounts that we want to
377 remove. Specifically, automount create normal NFS mounts.
380 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
382 for (p = mntlist; p; p = p->next) {
384 me = xmalloc (sizeof *me);
385 me->me_devname = xstrdup (mnt->mnt_fsname);
386 me->me_mountdir = xstrdup (mnt->mnt_dir);
387 me->me_type = xstrdup (mnt->mnt_type);
388 me->me_type_malloced = 1;
389 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
390 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
393 mtail = &me->me_next;
395 freemntlist (mntlist);
399 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
402 char const *table = MOUNTED;
405 fp = setmntent (table, "r");
409 while ((mnt = getmntent (fp)))
411 me = xmalloc (sizeof *me);
412 me->me_devname = xstrdup (mnt->mnt_fsname);
413 me->me_mountdir = xstrdup (mnt->mnt_dir);
414 me->me_type = xstrdup (mnt->mnt_type);
415 me->me_type_malloced = 1;
416 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
417 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
418 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
420 /* Add to the linked list. */
422 mtail = &me->me_next;
425 if (endmntent (fp) == 0)
428 #endif /* MOUNTED_GETMNTENT1. */
430 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
435 entries = getmntinfo (&fsp, MNT_NOWAIT);
438 for (; entries-- > 0; fsp++)
440 char *fs_type = fsp_to_string (fsp);
442 me = xmalloc (sizeof *me);
443 me->me_devname = xstrdup (fsp->f_mntfromname);
444 me->me_mountdir = xstrdup (fsp->f_mntonname);
445 me->me_type = fs_type;
446 me->me_type_malloced = 0;
447 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
448 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
449 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
451 /* Add to the linked list. */
453 mtail = &me->me_next;
456 #endif /* MOUNTED_GETMNTINFO */
458 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
463 entries = getmntinfo (&fsp, MNT_NOWAIT);
466 for (; entries-- > 0; fsp++)
468 me = xmalloc (sizeof *me);
469 me->me_devname = xstrdup (fsp->f_mntfromname);
470 me->me_mountdir = xstrdup (fsp->f_mntonname);
471 me->me_type = xstrdup (fsp->f_fstypename);
472 me->me_type_malloced = 1;
473 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
474 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
475 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
477 /* Add to the linked list. */
479 mtail = &me->me_next;
482 #endif /* MOUNTED_GETMNTINFO2 */
484 #ifdef MOUNTED_GETMNT /* Ultrix. */
491 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
494 me = xmalloc (sizeof *me);
495 me->me_devname = xstrdup (fsd.fd_req.devname);
496 me->me_mountdir = xstrdup (fsd.fd_req.path);
497 me->me_type = gt_names[fsd.fd_req.fstype];
498 me->me_type_malloced = 0;
499 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
500 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
501 me->me_dev = fsd.fd_req.dev;
503 /* Add to the linked list. */
505 mtail = &me->me_next;
510 #endif /* MOUNTED_GETMNT. */
512 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
514 /* The next_dev() and fs_stat_dev() system calls give the list of
515 all file systems, including the information returned by statvfs()
516 (fs type, total blocks, free blocks etc.), but without the mount
517 point. But on BeOS all file systems except / are mounted in the
518 rootfs, directly under /.
519 The directory name of the mount point is often, but not always,
520 identical to the volume name of the device.
521 We therefore get the list of subdirectories of /, and the list
522 of all file systems, and match the two lists. */
530 struct rootdir_entry *next;
532 struct rootdir_entry *rootdir_list;
533 struct rootdir_entry **rootdir_tail;
538 /* All volumes are mounted in the rootfs, directly under /. */
540 rootdir_tail = &rootdir_list;
541 dirp = opendir ("/");
546 while ((d = readdir (dirp)) != NULL)
551 if (strcmp (d->d_name, "..") == 0)
554 if (strcmp (d->d_name, ".") == 0)
555 name = xstrdup ("/");
558 name = xmalloc (1 + strlen (d->d_name) + 1);
560 strcpy (name + 1, d->d_name);
563 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
565 struct rootdir_entry *re = xmalloc (sizeof *re);
567 re->dev = statbuf.st_dev;
568 re->ino = statbuf.st_ino;
570 /* Add to the linked list. */
572 rootdir_tail = &re->next;
579 *rootdir_tail = NULL;
581 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
582 if (fs_stat_dev (dev, &fi) >= 0)
584 /* Note: fi.dev == dev. */
585 struct rootdir_entry *re;
587 for (re = rootdir_list; re; re = re->next)
588 if (re->dev == fi.dev && re->ino == fi.root)
591 me = xmalloc (sizeof *me);
592 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
593 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
594 me->me_type = xstrdup (fi.fsh_name);
595 me->me_type_malloced = 1;
598 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
600 /* Add to the linked list. */
602 mtail = &me->me_next;
606 while (rootdir_list != NULL)
608 struct rootdir_entry *re = rootdir_list;
609 rootdir_list = re->next;
614 #endif /* MOUNTED_FS_STAT_DEV */
616 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
620 struct statfs *stats;
622 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
625 if (SIZE_MAX / sizeof *stats <= numsys)
628 bufsize = (1 + numsys) * sizeof *stats;
629 stats = xmalloc (bufsize);
630 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
638 for (counter = 0; counter < numsys; counter++)
640 me = xmalloc (sizeof *me);
641 me->me_devname = xstrdup (stats[counter].f_mntfromname);
642 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
643 me->me_type = xstrdup (FS_TYPE (stats[counter]));
644 me->me_type_malloced = 1;
645 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
646 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
647 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
649 /* Add to the linked list. */
651 mtail = &me->me_next;
656 #endif /* MOUNTED_GETFSSTAT */
658 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
661 char *table = "/etc/mnttab";
664 fp = fopen (table, "r");
668 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
670 me = xmalloc (sizeof *me);
671 # ifdef GETFSTYP /* SVR3. */
672 me->me_devname = xstrdup (mnt.mt_dev);
674 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
675 strcpy (me->me_devname, "/dev/");
676 strcpy (me->me_devname + 5, mnt.mt_dev);
678 me->me_mountdir = xstrdup (mnt.mt_filsys);
679 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
681 me->me_type_malloced = 0;
682 # ifdef GETFSTYP /* SVR3. */
686 char typebuf[FSTYPSZ];
688 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
689 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
691 me->me_type = xstrdup (typebuf);
692 me->me_type_malloced = 1;
696 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
697 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
699 /* Add to the linked list. */
701 mtail = &me->me_next;
706 /* The last fread() call must have failed. */
707 int saved_errno = errno;
713 if (fclose (fp) == EOF)
716 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
718 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
720 struct mntent **mnttbl = getmnttbl (), **ent;
721 for (ent=mnttbl;*ent;ent++)
723 me = xmalloc (sizeof *me);
724 me->me_devname = xstrdup ( (*ent)->mt_resource);
725 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
726 me->me_type = xstrdup ((*ent)->mt_fstype);
727 me->me_type_malloced = 1;
728 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
729 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
730 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
732 /* Add to the linked list. */
734 mtail = &me->me_next;
740 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
743 char *table = MNTTAB;
748 # if defined F_RDLCK && defined F_SETLKW
749 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
750 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
751 for this file name, we should use their macro name instead.
752 (Why not just lock MNTTAB directly? We don't know.) */
754 # define MNTTAB_LOCK "/etc/.mnttab.lock"
756 lockfd = open (MNTTAB_LOCK, O_RDONLY);
760 flock.l_type = F_RDLCK;
761 flock.l_whence = SEEK_SET;
764 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
767 int saved_errno = errno;
773 else if (errno != ENOENT)
778 fp = fopen (table, "r");
783 while ((ret = getmntent (fp, &mnt)) == 0)
785 me = xmalloc (sizeof *me);
786 me->me_devname = xstrdup (mnt.mnt_special);
787 me->me_mountdir = xstrdup (mnt.mnt_mountp);
788 me->me_type = xstrdup (mnt.mnt_fstype);
789 me->me_type_malloced = 1;
790 me->me_dummy = MNT_IGNORE (&mnt) != 0;
791 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
792 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
794 /* Add to the linked list. */
796 mtail = &me->me_next;
799 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
802 if (0 <= lockfd && close (lockfd) != 0)
811 #endif /* MOUNTED_GETMNTENT2. */
813 #ifdef MOUNTED_VMOUNT /* AIX. */
816 char *entries, *thisent;
821 /* Ask how many bytes to allocate for the mounted file system info. */
822 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
824 entries = xmalloc (bufsize);
826 /* Get the list of mounted file systems. */
827 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
830 int saved_errno = errno;
836 for (i = 0, thisent = entries;
838 i++, thisent += vmp->vmt_length)
840 char *options, *ignore;
842 vmp = (struct vmount *) thisent;
843 me = xmalloc (sizeof *me);
844 if (vmp->vmt_flags & MNT_REMOTE)
849 /* Prepend the remote dirname. */
850 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
851 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
852 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
853 strcpy (me->me_devname, host);
854 strcat (me->me_devname, ":");
855 strcat (me->me_devname, dir);
860 me->me_devname = xstrdup (thisent +
861 vmp->vmt_data[VMT_OBJECT].vmt_off);
863 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
864 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
865 me->me_type_malloced = 1;
866 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
867 ignore = strstr (options, "ignore");
868 me->me_dummy = (ignore
869 && (ignore == options || ignore[-1] == ',')
870 && (ignore[sizeof "ignore" - 1] == ','
871 || ignore[sizeof "ignore" - 1] == '\0'));
872 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
874 /* Add to the linked list. */
876 mtail = &me->me_next;
880 #endif /* MOUNTED_VMOUNT. */
888 int saved_errno = errno;
893 me = mount_list->me_next;
894 free (mount_list->me_devname);
895 free (mount_list->me_mountdir);
896 if (mount_list->me_type_malloced)
897 free (mount_list->me_type);