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 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. */
24 #include "mountlist.h"
44 # include <sys/param.h>
47 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
49 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
50 NGROUPS is used as an array dimension in ucred.h */
51 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
54 # include <sys/mount.h>
56 # if HAVE_SYS_FS_TYPES_H
57 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
59 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
60 # define FS_TYPE(Ent) ((Ent).f_fstypename)
62 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
64 #endif /* MOUNTED_GETFSSTAT */
66 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
69 # if defined _PATH_MOUNTED /* GNU libc */
70 # define MOUNTED _PATH_MOUNTED
72 # if defined MNT_MNTTAB /* HP-UX. */
73 # define MOUNTED MNT_MNTTAB
75 # if defined MNTTABNAME /* Dynix. */
76 # define MOUNTED MNTTABNAME
81 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
82 # include <sys/mount.h>
85 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
86 # include <sys/statvfs.h>
89 #ifdef MOUNTED_GETMNT /* Ultrix. */
90 # include <sys/mount.h>
91 # include <sys/fs_types.h>
94 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
99 #ifdef MOUNTED_FREAD /* SVR2. */
103 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
105 # include <sys/fstyp.h>
106 # include <sys/statfs.h>
109 #ifdef MOUNTED_LISTMNTENT
113 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
114 # include <sys/mnttab.h>
117 #ifdef MOUNTED_VMOUNT /* AIX. */
119 # include <sys/vfs.h>
123 /* So special that it's not worth putting this in autoconf. */
124 # undef MOUNTED_FREAD_FSTYP
125 # define MOUNTED_GETMNTTBL
128 #if HAVE_SYS_MNTENT_H
129 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
130 # include <sys/mntent.h>
134 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
135 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
137 # define MNT_IGNORE(M) 0
141 # include "unlocked-io.h"
145 # define SIZE_MAX ((size_t) -1)
149 # define ME_DUMMY(Fs_name, Fs_type) \
150 (strcmp (Fs_type, "autofs") == 0 \
151 || strcmp (Fs_type, "none") == 0 \
152 || strcmp (Fs_type, "proc") == 0 \
153 || strcmp (Fs_type, "subfs") == 0 \
154 /* for NetBSD 3.0 */ \
155 || strcmp (Fs_type, "kernfs") == 0 \
157 || strcmp (Fs_type, "ignore") == 0)
161 /* A file system is `remote' if its Fs_name contains a `:'
162 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
163 # define ME_REMOTE(Fs_name, Fs_type) \
164 (strchr (Fs_name, ':') != NULL \
165 || ((Fs_name)[0] == '/' \
166 && (Fs_name)[1] == '/' \
167 && (strcmp (Fs_type, "smbfs") == 0 \
168 || strcmp (Fs_type, "cifs") == 0)))
171 #if MOUNTED_GETMNTINFO
173 # if ! HAVE_F_FSTYPENAME_IN_STATFS
175 fstype_to_string (short int t)
267 # endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */
270 fsp_to_string (const struct statfs *fsp)
272 # if defined HAVE_F_FSTYPENAME_IN_STATFS
273 return (char *) (fsp->f_fstypename);
275 return fstype_to_string (fsp->f_type);
279 #endif /* MOUNTED_GETMNTINFO */
281 #ifdef MOUNTED_VMOUNT /* AIX. */
283 fstype_to_string (int t)
287 e = getvfsbytype (t);
288 if (!e || !e->vfsent_name)
291 return e->vfsent_name;
293 #endif /* MOUNTED_VMOUNT */
296 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
298 /* Return the device number from MOUNT_OPTIONS, if possible.
299 Otherwise return (dev_t) -1. */
302 dev_from_mount_options (char const *mount_options)
304 /* GNU/Linux allows file system implementations to define their own
305 meaning for "dev=" mount options, so don't trust the meaning
309 static char const dev_pattern[] = ",dev=";
310 char const *devopt = strstr (mount_options, dev_pattern);
314 char const *optval = devopt + sizeof dev_pattern - 1;
316 unsigned long int dev;
318 dev = strtoul (optval, &optvalend, 16);
319 if (optval != optvalend
320 && (*optvalend == '\0' || *optvalend == ',')
321 && ! (dev == ULONG_MAX && errno == ERANGE)
322 && dev == (dev_t) dev)
333 /* Return a list of the currently mounted file systems, or NULL on error.
334 Add each entry to the tail of the list so that they stay in order.
335 If NEED_FS_TYPE is true, ensure that the file system type fields in
336 the returned list are valid. Otherwise, they might not be. */
339 read_file_system_list (bool need_fs_type)
341 struct mount_entry *mount_list;
342 struct mount_entry *me;
343 struct mount_entry **mtail = &mount_list;
345 #ifdef MOUNTED_LISTMNTENT
347 struct tabmntent *mntlist, *p;
349 struct mount_entry *me;
351 /* the third and fourth arguments could be used to filter mounts,
352 but Crays doesn't seem to have any mounts that we want to
353 remove. Specifically, automount create normal NFS mounts.
356 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
358 for (p = mntlist; p; p = p->next) {
360 me = xmalloc (sizeof *me);
361 me->me_devname = xstrdup (mnt->mnt_fsname);
362 me->me_mountdir = xstrdup (mnt->mnt_dir);
363 me->me_type = xstrdup (mnt->mnt_type);
364 me->me_type_malloced = 1;
365 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
366 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
369 mtail = &me->me_next;
371 freemntlist (mntlist);
375 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
378 char *table = MOUNTED;
381 fp = setmntent (table, "r");
385 while ((mnt = getmntent (fp)))
387 me = xmalloc (sizeof *me);
388 me->me_devname = xstrdup (mnt->mnt_fsname);
389 me->me_mountdir = xstrdup (mnt->mnt_dir);
390 me->me_type = xstrdup (mnt->mnt_type);
391 me->me_type_malloced = 1;
392 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
393 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
394 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
396 /* Add to the linked list. */
398 mtail = &me->me_next;
401 if (endmntent (fp) == 0)
404 #endif /* MOUNTED_GETMNTENT1. */
406 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
411 entries = getmntinfo (&fsp, MNT_NOWAIT);
414 for (; entries-- > 0; fsp++)
416 char *fs_type = fsp_to_string (fsp);
418 me = xmalloc (sizeof *me);
419 me->me_devname = xstrdup (fsp->f_mntfromname);
420 me->me_mountdir = xstrdup (fsp->f_mntonname);
421 me->me_type = fs_type;
422 me->me_type_malloced = 0;
423 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
424 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
425 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
427 /* Add to the linked list. */
429 mtail = &me->me_next;
432 #endif /* MOUNTED_GETMNTINFO */
434 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
439 entries = getmntinfo (&fsp, MNT_NOWAIT);
442 for (; entries-- > 0; fsp++)
444 me = xmalloc (sizeof *me);
445 me->me_devname = xstrdup (fsp->f_mntfromname);
446 me->me_mountdir = xstrdup (fsp->f_mntonname);
447 me->me_type = xstrdup (fsp->f_fstypename);
448 me->me_type_malloced = 1;
449 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
450 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
451 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
453 /* Add to the linked list. */
455 mtail = &me->me_next;
458 #endif /* MOUNTED_GETMNTINFO2 */
460 #ifdef MOUNTED_GETMNT /* Ultrix. */
467 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
470 me = xmalloc (sizeof *me);
471 me->me_devname = xstrdup (fsd.fd_req.devname);
472 me->me_mountdir = xstrdup (fsd.fd_req.path);
473 me->me_type = gt_names[fsd.fd_req.fstype];
474 me->me_type_malloced = 0;
475 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
476 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
477 me->me_dev = fsd.fd_req.dev;
479 /* Add to the linked list. */
481 mtail = &me->me_next;
486 #endif /* MOUNTED_GETMNT. */
488 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
490 /* The next_dev() and fs_stat_dev() system calls give the list of
491 all file systems, including the information returned by statvfs()
492 (fs type, total blocks, free blocks etc.), but without the mount
493 point. But on BeOS all file systems except / are mounted in the
494 rootfs, directly under /.
495 The directory name of the mount point is often, but not always,
496 identical to the volume name of the device.
497 We therefore get the list of subdirectories of /, and the list
498 of all file systems, and match the two lists. */
506 struct rootdir_entry *next;
508 struct rootdir_entry *rootdir_list;
509 struct rootdir_entry **rootdir_tail;
514 /* All volumes are mounted in the rootfs, directly under /. */
516 rootdir_tail = &rootdir_list;
517 dirp = opendir ("/");
522 while ((d = readdir (dirp)) != NULL)
527 if (strcmp (d->d_name, "..") == 0)
530 if (strcmp (d->d_name, ".") == 0)
531 name = xstrdup ("/");
534 name = xmalloc (1 + strlen (d->d_name) + 1);
536 strcpy (name + 1, d->d_name);
539 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
541 struct rootdir_entry *re = xmalloc (sizeof *re);
543 re->dev = statbuf.st_dev;
544 re->ino = statbuf.st_ino;
546 /* Add to the linked list. */
548 rootdir_tail = &re->next;
555 *rootdir_tail = NULL;
557 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
558 if (fs_stat_dev (dev, &fi) >= 0)
560 /* Note: fi.dev == dev. */
561 struct rootdir_entry *re;
563 for (re = rootdir_list; re; re = re->next)
564 if (re->dev == fi.dev && re->ino == fi.root)
567 me = xmalloc (sizeof *me);
568 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
569 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
570 me->me_type = xstrdup (fi.fsh_name);
571 me->me_type_malloced = 1;
574 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
576 /* Add to the linked list. */
578 mtail = &me->me_next;
582 while (rootdir_list != NULL)
584 struct rootdir_entry *re = rootdir_list;
585 rootdir_list = re->next;
590 #endif /* MOUNTED_FS_STAT_DEV */
592 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
596 struct statfs *stats;
598 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
601 if (SIZE_MAX / sizeof *stats <= numsys)
604 bufsize = (1 + numsys) * sizeof *stats;
605 stats = xmalloc (bufsize);
606 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
614 for (counter = 0; counter < numsys; counter++)
616 me = xmalloc (sizeof *me);
617 me->me_devname = xstrdup (stats[counter].f_mntfromname);
618 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
619 me->me_type = xstrdup (FS_TYPE (stats[counter]));
620 me->me_type_malloced = 1;
621 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
622 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
623 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
625 /* Add to the linked list. */
627 mtail = &me->me_next;
632 #endif /* MOUNTED_GETFSSTAT */
634 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
637 char *table = "/etc/mnttab";
640 fp = fopen (table, "r");
644 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
646 me = xmalloc (sizeof *me);
647 # ifdef GETFSTYP /* SVR3. */
648 me->me_devname = xstrdup (mnt.mt_dev);
650 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
651 strcpy (me->me_devname, "/dev/");
652 strcpy (me->me_devname + 5, mnt.mt_dev);
654 me->me_mountdir = xstrdup (mnt.mt_filsys);
655 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
657 me->me_type_malloced = 0;
658 # ifdef GETFSTYP /* SVR3. */
662 char typebuf[FSTYPSZ];
664 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
665 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
667 me->me_type = xstrdup (typebuf);
668 me->me_type_malloced = 1;
672 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
673 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
675 /* Add to the linked list. */
677 mtail = &me->me_next;
682 /* The last fread() call must have failed. */
683 int saved_errno = errno;
689 if (fclose (fp) == EOF)
692 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
694 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
696 struct mntent **mnttbl = getmnttbl (), **ent;
697 for (ent=mnttbl;*ent;ent++)
699 me = xmalloc (sizeof *me);
700 me->me_devname = xstrdup ( (*ent)->mt_resource);
701 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
702 me->me_type = xstrdup ((*ent)->mt_fstype);
703 me->me_type_malloced = 1;
704 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
705 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
706 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
708 /* Add to the linked list. */
710 mtail = &me->me_next;
716 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
719 char *table = MNTTAB;
724 # if defined F_RDLCK && defined F_SETLKW
725 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
726 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
727 for this file name, we should use their macro name instead.
728 (Why not just lock MNTTAB directly? We don't know.) */
730 # define MNTTAB_LOCK "/etc/.mnttab.lock"
732 lockfd = open (MNTTAB_LOCK, O_RDONLY);
736 flock.l_type = F_RDLCK;
737 flock.l_whence = SEEK_SET;
740 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
743 int saved_errno = errno;
749 else if (errno != ENOENT)
754 fp = fopen (table, "r");
759 while ((ret = getmntent (fp, &mnt)) == 0)
761 me = xmalloc (sizeof *me);
762 me->me_devname = xstrdup (mnt.mnt_special);
763 me->me_mountdir = xstrdup (mnt.mnt_mountp);
764 me->me_type = xstrdup (mnt.mnt_fstype);
765 me->me_type_malloced = 1;
766 me->me_dummy = MNT_IGNORE (&mnt) != 0;
767 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
768 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
770 /* Add to the linked list. */
772 mtail = &me->me_next;
775 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
778 if (0 <= lockfd && close (lockfd) != 0)
787 #endif /* MOUNTED_GETMNTENT2. */
789 #ifdef MOUNTED_VMOUNT /* AIX. */
792 char *entries, *thisent;
797 /* Ask how many bytes to allocate for the mounted file system info. */
798 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
800 entries = xmalloc (bufsize);
802 /* Get the list of mounted file systems. */
803 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
806 int saved_errno = errno;
812 for (i = 0, thisent = entries;
814 i++, thisent += vmp->vmt_length)
816 char *options, *ignore;
818 vmp = (struct vmount *) thisent;
819 me = xmalloc (sizeof *me);
820 if (vmp->vmt_flags & MNT_REMOTE)
825 /* Prepend the remote dirname. */
826 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
827 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
828 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
829 strcpy (me->me_devname, host);
830 strcat (me->me_devname, ":");
831 strcat (me->me_devname, dir);
836 me->me_devname = xstrdup (thisent +
837 vmp->vmt_data[VMT_OBJECT].vmt_off);
839 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
840 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
841 me->me_type_malloced = 1;
842 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
843 ignore = strstr (options, "ignore");
844 me->me_dummy = (ignore
845 && (ignore == options || ignore[-1] == ',')
846 && (ignore[sizeof "ignore" - 1] == ','
847 || ignore[sizeof "ignore" - 1] == '\0'));
848 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
850 /* Add to the linked list. */
852 mtail = &me->me_next;
856 #endif /* MOUNTED_VMOUNT. */
864 int saved_errno = errno;
869 me = mount_list->me_next;
870 free (mount_list->me_devname);
871 free (mount_list->me_mountdir);
872 if (mount_list->me_type_malloced)
873 free (mount_list->me_type);