1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991, 1992, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
22 #include "mountlist.h"
38 # include <sys/param.h>
41 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
43 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
44 NGROUPS is used as an array dimension in ucred.h */
45 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
48 # include <sys/mount.h>
50 # if HAVE_SYS_FS_TYPES_H
51 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
53 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
54 # define FS_TYPE(Ent) ((Ent).f_fstypename)
56 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
58 #endif /* MOUNTED_GETFSSTAT */
60 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
63 # if defined _PATH_MOUNTED /* GNU libc */
64 # define MOUNTED _PATH_MOUNTED
66 # if defined MNT_MNTTAB /* HP-UX. */
67 # define MOUNTED MNT_MNTTAB
69 # if defined MNTTABNAME /* Dynix. */
70 # define MOUNTED MNTTABNAME
75 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
76 # include <sys/mount.h>
79 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
80 # include <sys/statvfs.h>
83 #ifdef MOUNTED_GETMNT /* Ultrix. */
84 # include <sys/mount.h>
85 # include <sys/fs_types.h>
88 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
93 #ifdef MOUNTED_FREAD /* SVR2. */
97 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
99 # include <sys/fstyp.h>
100 # include <sys/statfs.h>
103 #ifdef MOUNTED_LISTMNTENT
107 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
108 # include <sys/mnttab.h>
111 #ifdef MOUNTED_VMOUNT /* AIX. */
113 # include <sys/vfs.h>
117 /* So special that it's not worth putting this in autoconf. */
118 # undef MOUNTED_FREAD_FSTYP
119 # define MOUNTED_GETMNTTBL
122 #if HAVE_SYS_MNTENT_H
123 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
124 # include <sys/mntent.h>
128 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
129 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
131 # define MNT_IGNORE(M) 0
135 # include "unlocked-io.h"
139 # define SIZE_MAX ((size_t) -1)
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 /* A file system is `remote' if its Fs_name contains a `:'
166 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
167 # define ME_REMOTE(Fs_name, Fs_type) \
168 (strchr (Fs_name, ':') != NULL \
169 || ((Fs_name)[0] == '/' \
170 && (Fs_name)[1] == '/' \
171 && (strcmp (Fs_type, "smbfs") == 0 \
172 || strcmp (Fs_type, "cifs") == 0)))
175 #if MOUNTED_GETMNTINFO
177 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
179 fstype_to_string (short int t)
274 fsp_to_string (const struct statfs *fsp)
276 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
277 return (char *) (fsp->f_fstypename);
279 return fstype_to_string (fsp->f_type);
283 #endif /* MOUNTED_GETMNTINFO */
285 #ifdef MOUNTED_VMOUNT /* AIX. */
287 fstype_to_string (int t)
291 e = getvfsbytype (t);
292 if (!e || !e->vfsent_name)
295 return e->vfsent_name;
297 #endif /* MOUNTED_VMOUNT */
300 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
302 /* Return the device number from MOUNT_OPTIONS, if possible.
303 Otherwise return (dev_t) -1. */
306 dev_from_mount_options (char const *mount_options)
308 /* GNU/Linux allows file system implementations to define their own
309 meaning for "dev=" mount options, so don't trust the meaning
313 static char const dev_pattern[] = ",dev=";
314 char const *devopt = strstr (mount_options, dev_pattern);
318 char const *optval = devopt + sizeof dev_pattern - 1;
320 unsigned long int dev;
322 dev = strtoul (optval, &optvalend, 16);
323 if (optval != optvalend
324 && (*optvalend == '\0' || *optvalend == ',')
325 && ! (dev == ULONG_MAX && errno == ERANGE)
326 && dev == (dev_t) dev)
337 /* Return a list of the currently mounted file systems, or NULL on error.
338 Add each entry to the tail of the list so that they stay in order.
339 If NEED_FS_TYPE is true, ensure that the file system type fields in
340 the returned list are valid. Otherwise, they might not be. */
343 read_file_system_list (bool need_fs_type)
345 struct mount_entry *mount_list;
346 struct mount_entry *me;
347 struct mount_entry **mtail = &mount_list;
349 #ifdef MOUNTED_LISTMNTENT
351 struct tabmntent *mntlist, *p;
353 struct mount_entry *me;
355 /* the third and fourth arguments could be used to filter mounts,
356 but Crays doesn't seem to have any mounts that we want to
357 remove. Specifically, automount create normal NFS mounts.
360 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
362 for (p = mntlist; p; p = p->next) {
364 me = xmalloc (sizeof *me);
365 me->me_devname = xstrdup (mnt->mnt_fsname);
366 me->me_mountdir = xstrdup (mnt->mnt_dir);
367 me->me_type = xstrdup (mnt->mnt_type);
368 me->me_type_malloced = 1;
369 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
370 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
373 mtail = &me->me_next;
375 freemntlist (mntlist);
379 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
382 char *table = MOUNTED;
385 fp = setmntent (table, "r");
389 while ((mnt = getmntent (fp)))
391 me = xmalloc (sizeof *me);
392 me->me_devname = xstrdup (mnt->mnt_fsname);
393 me->me_mountdir = xstrdup (mnt->mnt_dir);
394 me->me_type = xstrdup (mnt->mnt_type);
395 me->me_type_malloced = 1;
396 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
397 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
398 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
400 /* Add to the linked list. */
402 mtail = &me->me_next;
405 if (endmntent (fp) == 0)
408 #endif /* MOUNTED_GETMNTENT1. */
410 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
415 entries = getmntinfo (&fsp, MNT_NOWAIT);
418 for (; entries-- > 0; fsp++)
420 char *fs_type = fsp_to_string (fsp);
422 me = xmalloc (sizeof *me);
423 me->me_devname = xstrdup (fsp->f_mntfromname);
424 me->me_mountdir = xstrdup (fsp->f_mntonname);
425 me->me_type = fs_type;
426 me->me_type_malloced = 0;
427 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
428 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
429 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
431 /* Add to the linked list. */
433 mtail = &me->me_next;
436 #endif /* MOUNTED_GETMNTINFO */
438 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
443 entries = getmntinfo (&fsp, MNT_NOWAIT);
446 for (; entries-- > 0; 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 = xstrdup (fsp->f_fstypename);
452 me->me_type_malloced = 1;
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_GETMNTINFO2 */
464 #ifdef MOUNTED_GETMNT /* Ultrix. */
471 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
474 me = xmalloc (sizeof *me);
475 me->me_devname = xstrdup (fsd.fd_req.devname);
476 me->me_mountdir = xstrdup (fsd.fd_req.path);
477 me->me_type = gt_names[fsd.fd_req.fstype];
478 me->me_type_malloced = 0;
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 = fsd.fd_req.dev;
483 /* Add to the linked list. */
485 mtail = &me->me_next;
490 #endif /* MOUNTED_GETMNT. */
492 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
494 /* The next_dev() and fs_stat_dev() system calls give the list of
495 all file systems, including the information returned by statvfs()
496 (fs type, total blocks, free blocks etc.), but without the mount
497 point. But on BeOS all file systems except / are mounted in the
498 rootfs, directly under /.
499 The directory name of the mount point is often, but not always,
500 identical to the volume name of the device.
501 We therefore get the list of subdirectories of /, and the list
502 of all file systems, and match the two lists. */
510 struct rootdir_entry *next;
512 struct rootdir_entry *rootdir_list;
513 struct rootdir_entry **rootdir_tail;
518 /* All volumes are mounted in the rootfs, directly under /. */
520 rootdir_tail = &rootdir_list;
521 dirp = opendir ("/");
526 while ((d = readdir (dirp)) != NULL)
531 if (strcmp (d->d_name, "..") == 0)
534 if (strcmp (d->d_name, ".") == 0)
535 name = xstrdup ("/");
538 name = xmalloc (1 + strlen (d->d_name) + 1);
540 strcpy (name + 1, d->d_name);
543 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
545 struct rootdir_entry *re = xmalloc (sizeof *re);
547 re->dev = statbuf.st_dev;
548 re->ino = statbuf.st_ino;
550 /* Add to the linked list. */
552 rootdir_tail = &re->next;
559 *rootdir_tail = NULL;
561 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
562 if (fs_stat_dev (dev, &fi) >= 0)
564 /* Note: fi.dev == dev. */
565 struct rootdir_entry *re;
567 for (re = rootdir_list; re; re = re->next)
568 if (re->dev == fi.dev && re->ino == fi.root)
571 me = xmalloc (sizeof *me);
572 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
573 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
574 me->me_type = xstrdup (fi.fsh_name);
575 me->me_type_malloced = 1;
578 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
580 /* Add to the linked list. */
582 mtail = &me->me_next;
586 while (rootdir_list != NULL)
588 struct rootdir_entry *re = rootdir_list;
589 rootdir_list = re->next;
594 #endif /* MOUNTED_FS_STAT_DEV */
596 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
600 struct statfs *stats;
602 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
605 if (SIZE_MAX / sizeof *stats <= numsys)
608 bufsize = (1 + numsys) * sizeof *stats;
609 stats = xmalloc (bufsize);
610 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
618 for (counter = 0; counter < numsys; counter++)
620 me = xmalloc (sizeof *me);
621 me->me_devname = xstrdup (stats[counter].f_mntfromname);
622 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
623 me->me_type = xstrdup (FS_TYPE (stats[counter]));
624 me->me_type_malloced = 1;
625 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
626 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
627 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
629 /* Add to the linked list. */
631 mtail = &me->me_next;
636 #endif /* MOUNTED_GETFSSTAT */
638 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
641 char *table = "/etc/mnttab";
644 fp = fopen (table, "r");
648 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
650 me = xmalloc (sizeof *me);
651 # ifdef GETFSTYP /* SVR3. */
652 me->me_devname = xstrdup (mnt.mt_dev);
654 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
655 strcpy (me->me_devname, "/dev/");
656 strcpy (me->me_devname + 5, mnt.mt_dev);
658 me->me_mountdir = xstrdup (mnt.mt_filsys);
659 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
661 me->me_type_malloced = 0;
662 # ifdef GETFSTYP /* SVR3. */
666 char typebuf[FSTYPSZ];
668 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
669 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
671 me->me_type = xstrdup (typebuf);
672 me->me_type_malloced = 1;
676 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
677 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
679 /* Add to the linked list. */
681 mtail = &me->me_next;
686 /* The last fread() call must have failed. */
687 int saved_errno = errno;
693 if (fclose (fp) == EOF)
696 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
698 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
700 struct mntent **mnttbl = getmnttbl (), **ent;
701 for (ent=mnttbl;*ent;ent++)
703 me = xmalloc (sizeof *me);
704 me->me_devname = xstrdup ( (*ent)->mt_resource);
705 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
706 me->me_type = xstrdup ((*ent)->mt_fstype);
707 me->me_type_malloced = 1;
708 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
709 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
710 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
712 /* Add to the linked list. */
714 mtail = &me->me_next;
720 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
723 char *table = MNTTAB;
728 # if defined F_RDLCK && defined F_SETLKW
729 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
730 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
731 for this file name, we should use their macro name instead.
732 (Why not just lock MNTTAB directly? We don't know.) */
734 # define MNTTAB_LOCK "/etc/.mnttab.lock"
736 lockfd = open (MNTTAB_LOCK, O_RDONLY);
740 flock.l_type = F_RDLCK;
741 flock.l_whence = SEEK_SET;
744 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
747 int saved_errno = errno;
753 else if (errno != ENOENT)
758 fp = fopen (table, "r");
763 while ((ret = getmntent (fp, &mnt)) == 0)
765 me = xmalloc (sizeof *me);
766 me->me_devname = xstrdup (mnt.mnt_special);
767 me->me_mountdir = xstrdup (mnt.mnt_mountp);
768 me->me_type = xstrdup (mnt.mnt_fstype);
769 me->me_type_malloced = 1;
770 me->me_dummy = MNT_IGNORE (&mnt) != 0;
771 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
772 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
774 /* Add to the linked list. */
776 mtail = &me->me_next;
779 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
782 if (0 <= lockfd && close (lockfd) != 0)
791 #endif /* MOUNTED_GETMNTENT2. */
793 #ifdef MOUNTED_VMOUNT /* AIX. */
796 char *entries, *thisent;
801 /* Ask how many bytes to allocate for the mounted file system info. */
802 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
804 entries = xmalloc (bufsize);
806 /* Get the list of mounted file systems. */
807 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
810 int saved_errno = errno;
816 for (i = 0, thisent = entries;
818 i++, thisent += vmp->vmt_length)
820 char *options, *ignore;
822 vmp = (struct vmount *) thisent;
823 me = xmalloc (sizeof *me);
824 if (vmp->vmt_flags & MNT_REMOTE)
829 /* Prepend the remote dirname. */
830 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
831 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
832 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
833 strcpy (me->me_devname, host);
834 strcat (me->me_devname, ":");
835 strcat (me->me_devname, dir);
840 me->me_devname = xstrdup (thisent +
841 vmp->vmt_data[VMT_OBJECT].vmt_off);
843 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
844 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
845 me->me_type_malloced = 1;
846 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
847 ignore = strstr (options, "ignore");
848 me->me_dummy = (ignore
849 && (ignore == options || ignore[-1] == ',')
850 && (ignore[sizeof "ignore" - 1] == ','
851 || ignore[sizeof "ignore" - 1] == '\0'));
852 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
854 /* Add to the linked list. */
856 mtail = &me->me_next;
860 #endif /* MOUNTED_VMOUNT. */
868 int saved_errno = errno;
873 me = mount_list->me_next;
874 free (mount_list->me_devname);
875 free (mount_list->me_mountdir);
876 if (mount_list->me_type_malloced)
877 free (mount_list->me_type);