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. */
22 #include "mountlist.h"
42 # include <sys/param.h>
45 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
47 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
48 NGROUPS is used as an array dimension in ucred.h */
49 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
52 # include <sys/mount.h>
54 # if HAVE_SYS_FS_TYPES_H
55 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
57 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
58 # define FS_TYPE(Ent) ((Ent).f_fstypename)
60 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
62 #endif /* MOUNTED_GETFSSTAT */
64 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
67 # if defined _PATH_MOUNTED /* GNU libc */
68 # define MOUNTED _PATH_MOUNTED
70 # if defined MNT_MNTTAB /* HP-UX. */
71 # define MOUNTED MNT_MNTTAB
73 # if defined MNTTABNAME /* Dynix. */
74 # define MOUNTED MNTTABNAME
79 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
80 # include <sys/mount.h>
83 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
84 # include <sys/statvfs.h>
87 #ifdef MOUNTED_GETMNT /* Ultrix. */
88 # include <sys/mount.h>
89 # include <sys/fs_types.h>
92 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
97 #ifdef MOUNTED_FREAD /* SVR2. */
101 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
103 # include <sys/fstyp.h>
104 # include <sys/statfs.h>
107 #ifdef MOUNTED_LISTMNTENT
111 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
112 # include <sys/mnttab.h>
115 #ifdef MOUNTED_VMOUNT /* AIX. */
117 # include <sys/vfs.h>
121 /* So special that it's not worth putting this in autoconf. */
122 # undef MOUNTED_FREAD_FSTYP
123 # define MOUNTED_GETMNTTBL
126 #if HAVE_SYS_MNTENT_H
127 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
128 # include <sys/mntent.h>
132 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
133 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
135 # define MNT_IGNORE(M) 0
139 # include "unlocked-io.h"
143 # define SIZE_MAX ((size_t) -1)
147 # define ME_DUMMY(Fs_name, Fs_type) \
148 (strcmp (Fs_type, "autofs") == 0 \
149 || strcmp (Fs_type, "none") == 0 \
150 || strcmp (Fs_type, "proc") == 0 \
151 || strcmp (Fs_type, "subfs") == 0 \
152 /* for NetBSD 3.0 */ \
153 || strcmp (Fs_type, "kernfs") == 0 \
155 || strcmp (Fs_type, "ignore") == 0)
159 /* A file system is `remote' if its Fs_name contains a `:'
160 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
161 # define ME_REMOTE(Fs_name, Fs_type) \
162 (strchr (Fs_name, ':') != NULL \
163 || ((Fs_name)[0] == '/' \
164 && (Fs_name)[1] == '/' \
165 && (strcmp (Fs_type, "smbfs") == 0 \
166 || strcmp (Fs_type, "cifs") == 0)))
169 #if MOUNTED_GETMNTINFO
171 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
173 fstype_to_string (short int t)
268 fsp_to_string (const struct statfs *fsp)
270 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
271 return (char *) (fsp->f_fstypename);
273 return fstype_to_string (fsp->f_type);
277 #endif /* MOUNTED_GETMNTINFO */
279 #ifdef MOUNTED_VMOUNT /* AIX. */
281 fstype_to_string (int t)
285 e = getvfsbytype (t);
286 if (!e || !e->vfsent_name)
289 return e->vfsent_name;
291 #endif /* MOUNTED_VMOUNT */
294 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
296 /* Return the device number from MOUNT_OPTIONS, if possible.
297 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)
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;
343 #ifdef MOUNTED_LISTMNTENT
345 struct tabmntent *mntlist, *p;
347 struct mount_entry *me;
349 /* the third and fourth arguments could be used to filter mounts,
350 but Crays doesn't seem to have any mounts that we want to
351 remove. Specifically, automount create normal NFS mounts.
354 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
356 for (p = mntlist; p; p = p->next) {
358 me = xmalloc (sizeof *me);
359 me->me_devname = xstrdup (mnt->mnt_fsname);
360 me->me_mountdir = xstrdup (mnt->mnt_dir);
361 me->me_type = xstrdup (mnt->mnt_type);
362 me->me_type_malloced = 1;
363 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
364 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
367 mtail = &me->me_next;
369 freemntlist (mntlist);
373 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
376 char *table = MOUNTED;
379 fp = setmntent (table, "r");
383 while ((mnt = getmntent (fp)))
385 me = xmalloc (sizeof *me);
386 me->me_devname = xstrdup (mnt->mnt_fsname);
387 me->me_mountdir = xstrdup (mnt->mnt_dir);
388 me->me_type = xstrdup (mnt->mnt_type);
389 me->me_type_malloced = 1;
390 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
391 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
392 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
394 /* Add to the linked list. */
396 mtail = &me->me_next;
399 if (endmntent (fp) == 0)
402 #endif /* MOUNTED_GETMNTENT1. */
404 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
409 entries = getmntinfo (&fsp, MNT_NOWAIT);
412 for (; entries-- > 0; fsp++)
414 char *fs_type = fsp_to_string (fsp);
416 me = xmalloc (sizeof *me);
417 me->me_devname = xstrdup (fsp->f_mntfromname);
418 me->me_mountdir = xstrdup (fsp->f_mntonname);
419 me->me_type = fs_type;
420 me->me_type_malloced = 0;
421 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
422 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
423 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
425 /* Add to the linked list. */
427 mtail = &me->me_next;
430 #endif /* MOUNTED_GETMNTINFO */
432 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
437 entries = getmntinfo (&fsp, MNT_NOWAIT);
440 for (; entries-- > 0; 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 = xstrdup (fsp->f_fstypename);
446 me->me_type_malloced = 1;
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_GETMNTINFO2 */
458 #ifdef MOUNTED_GETMNT /* Ultrix. */
465 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
468 me = xmalloc (sizeof *me);
469 me->me_devname = xstrdup (fsd.fd_req.devname);
470 me->me_mountdir = xstrdup (fsd.fd_req.path);
471 me->me_type = gt_names[fsd.fd_req.fstype];
472 me->me_type_malloced = 0;
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 = fsd.fd_req.dev;
477 /* Add to the linked list. */
479 mtail = &me->me_next;
484 #endif /* MOUNTED_GETMNT. */
486 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
488 /* The next_dev() and fs_stat_dev() system calls give the list of
489 all file systems, including the information returned by statvfs()
490 (fs type, total blocks, free blocks etc.), but without the mount
491 point. But on BeOS all file systems except / are mounted in the
492 rootfs, directly under /.
493 The directory name of the mount point is often, but not always,
494 identical to the volume name of the device.
495 We therefore get the list of subdirectories of /, and the list
496 of all file systems, and match the two lists. */
504 struct rootdir_entry *next;
506 struct rootdir_entry *rootdir_list;
507 struct rootdir_entry **rootdir_tail;
512 /* All volumes are mounted in the rootfs, directly under /. */
514 rootdir_tail = &rootdir_list;
515 dirp = opendir ("/");
520 while ((d = readdir (dirp)) != NULL)
525 if (strcmp (d->d_name, "..") == 0)
528 if (strcmp (d->d_name, ".") == 0)
529 name = xstrdup ("/");
532 name = xmalloc (1 + strlen (d->d_name) + 1);
534 strcpy (name + 1, d->d_name);
537 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
539 struct rootdir_entry *re = xmalloc (sizeof *re);
541 re->dev = statbuf.st_dev;
542 re->ino = statbuf.st_ino;
544 /* Add to the linked list. */
546 rootdir_tail = &re->next;
553 *rootdir_tail = NULL;
555 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
556 if (fs_stat_dev (dev, &fi) >= 0)
558 /* Note: fi.dev == dev. */
559 struct rootdir_entry *re;
561 for (re = rootdir_list; re; re = re->next)
562 if (re->dev == fi.dev && re->ino == fi.root)
565 me = xmalloc (sizeof *me);
566 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
567 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
568 me->me_type = xstrdup (fi.fsh_name);
569 me->me_type_malloced = 1;
572 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
574 /* Add to the linked list. */
576 mtail = &me->me_next;
580 while (rootdir_list != NULL)
582 struct rootdir_entry *re = rootdir_list;
583 rootdir_list = re->next;
588 #endif /* MOUNTED_FS_STAT_DEV */
590 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
594 struct statfs *stats;
596 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
599 if (SIZE_MAX / sizeof *stats <= numsys)
602 bufsize = (1 + numsys) * sizeof *stats;
603 stats = xmalloc (bufsize);
604 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
612 for (counter = 0; counter < numsys; counter++)
614 me = xmalloc (sizeof *me);
615 me->me_devname = xstrdup (stats[counter].f_mntfromname);
616 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
617 me->me_type = xstrdup (FS_TYPE (stats[counter]));
618 me->me_type_malloced = 1;
619 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
620 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
621 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
623 /* Add to the linked list. */
625 mtail = &me->me_next;
630 #endif /* MOUNTED_GETFSSTAT */
632 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
635 char *table = "/etc/mnttab";
638 fp = fopen (table, "r");
642 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
644 me = xmalloc (sizeof *me);
645 # ifdef GETFSTYP /* SVR3. */
646 me->me_devname = xstrdup (mnt.mt_dev);
648 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
649 strcpy (me->me_devname, "/dev/");
650 strcpy (me->me_devname + 5, mnt.mt_dev);
652 me->me_mountdir = xstrdup (mnt.mt_filsys);
653 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
655 me->me_type_malloced = 0;
656 # ifdef GETFSTYP /* SVR3. */
660 char typebuf[FSTYPSZ];
662 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
663 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
665 me->me_type = xstrdup (typebuf);
666 me->me_type_malloced = 1;
670 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
671 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
673 /* Add to the linked list. */
675 mtail = &me->me_next;
680 /* The last fread() call must have failed. */
681 int saved_errno = errno;
687 if (fclose (fp) == EOF)
690 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
692 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
694 struct mntent **mnttbl = getmnttbl (), **ent;
695 for (ent=mnttbl;*ent;ent++)
697 me = xmalloc (sizeof *me);
698 me->me_devname = xstrdup ( (*ent)->mt_resource);
699 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
700 me->me_type = xstrdup ((*ent)->mt_fstype);
701 me->me_type_malloced = 1;
702 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
703 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
704 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
706 /* Add to the linked list. */
708 mtail = &me->me_next;
714 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
717 char *table = MNTTAB;
722 # if defined F_RDLCK && defined F_SETLKW
723 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
724 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
725 for this file name, we should use their macro name instead.
726 (Why not just lock MNTTAB directly? We don't know.) */
728 # define MNTTAB_LOCK "/etc/.mnttab.lock"
730 lockfd = open (MNTTAB_LOCK, O_RDONLY);
734 flock.l_type = F_RDLCK;
735 flock.l_whence = SEEK_SET;
738 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
741 int saved_errno = errno;
747 else if (errno != ENOENT)
752 fp = fopen (table, "r");
757 while ((ret = getmntent (fp, &mnt)) == 0)
759 me = xmalloc (sizeof *me);
760 me->me_devname = xstrdup (mnt.mnt_special);
761 me->me_mountdir = xstrdup (mnt.mnt_mountp);
762 me->me_type = xstrdup (mnt.mnt_fstype);
763 me->me_type_malloced = 1;
764 me->me_dummy = MNT_IGNORE (&mnt) != 0;
765 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
766 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
768 /* Add to the linked list. */
770 mtail = &me->me_next;
773 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
776 if (0 <= lockfd && close (lockfd) != 0)
785 #endif /* MOUNTED_GETMNTENT2. */
787 #ifdef MOUNTED_VMOUNT /* AIX. */
790 char *entries, *thisent;
795 /* Ask how many bytes to allocate for the mounted file system info. */
796 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
798 entries = xmalloc (bufsize);
800 /* Get the list of mounted file systems. */
801 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
804 int saved_errno = errno;
810 for (i = 0, thisent = entries;
812 i++, thisent += vmp->vmt_length)
814 char *options, *ignore;
816 vmp = (struct vmount *) thisent;
817 me = xmalloc (sizeof *me);
818 if (vmp->vmt_flags & MNT_REMOTE)
823 /* Prepend the remote dirname. */
824 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
825 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
826 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
827 strcpy (me->me_devname, host);
828 strcat (me->me_devname, ":");
829 strcat (me->me_devname, dir);
834 me->me_devname = xstrdup (thisent +
835 vmp->vmt_data[VMT_OBJECT].vmt_off);
837 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
838 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
839 me->me_type_malloced = 1;
840 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
841 ignore = strstr (options, "ignore");
842 me->me_dummy = (ignore
843 && (ignore == options || ignore[-1] == ',')
844 && (ignore[sizeof "ignore" - 1] == ','
845 || ignore[sizeof "ignore" - 1] == '\0'));
846 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
848 /* Add to the linked list. */
850 mtail = &me->me_next;
854 #endif /* MOUNTED_VMOUNT. */
862 int saved_errno = errno;
867 me = mount_list->me_next;
868 free (mount_list->me_devname);
869 free (mount_list->me_mountdir);
870 if (mount_list->me_type_malloced)
871 free (mount_list->me_type);