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_GETMNT /* Ultrix. */
86 # include <sys/mount.h>
87 # include <sys/fs_types.h>
90 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
95 #ifdef MOUNTED_FREAD /* SVR2. */
99 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
101 # include <sys/fstyp.h>
102 # include <sys/statfs.h>
105 #ifdef MOUNTED_LISTMNTENT
109 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
110 # include <sys/mnttab.h>
113 #ifdef MOUNTED_VMOUNT /* AIX. */
115 # include <sys/vfs.h>
119 /* So special that it's not worth putting this in autoconf. */
120 # undef MOUNTED_FREAD_FSTYP
121 # define MOUNTED_GETMNTTBL
124 #if HAVE_SYS_MNTENT_H
125 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
126 # include <sys/mntent.h>
130 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
131 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
133 # define MNT_IGNORE(M) 0
137 # include "unlocked-io.h"
141 # define SIZE_MAX ((size_t) -1)
145 # define ME_DUMMY(Fs_name, Fs_type) \
146 (strcmp (Fs_type, "autofs") == 0 \
147 || strcmp (Fs_type, "none") == 0 \
148 || strcmp (Fs_type, "proc") == 0 \
149 || strcmp (Fs_type, "subfs") == 0 \
151 || strcmp (Fs_type, "ignore") == 0)
155 /* A file system is `remote' if its Fs_name contains a `:'
156 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
157 # define ME_REMOTE(Fs_name, Fs_type) \
158 (strchr (Fs_name, ':') != NULL \
159 || ((Fs_name)[0] == '/' \
160 && (Fs_name)[1] == '/' \
161 && (strcmp (Fs_type, "smbfs") == 0 \
162 || strcmp (Fs_type, "cifs") == 0)))
165 #if MOUNTED_GETMNTINFO
167 # if ! HAVE_F_FSTYPENAME_IN_STATFS
169 fstype_to_string (short int t)
261 # endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */
263 /* __NetBSD__ || BSD_NET2 || __OpenBSD__ */
265 fsp_to_string (const struct statfs *fsp)
267 # if defined HAVE_F_FSTYPENAME_IN_STATFS
268 return (char *) (fsp->f_fstypename);
270 return fstype_to_string (fsp->f_type);
274 #endif /* MOUNTED_GETMNTINFO */
276 #ifdef MOUNTED_VMOUNT /* AIX. */
278 fstype_to_string (int t)
282 e = getvfsbytype (t);
283 if (!e || !e->vfsent_name)
286 return e->vfsent_name;
288 #endif /* MOUNTED_VMOUNT */
291 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
293 /* Return the device number from MOUNT_OPTIONS, if possible.
294 Otherwise return (dev_t) -1. */
297 dev_from_mount_options (char const *mount_options)
299 /* GNU/Linux allows file system implementations to define their own
300 meaning for "dev=" mount options, so don't trust the meaning
304 static char const dev_pattern[] = ",dev=";
305 char const *devopt = strstr (mount_options, dev_pattern);
309 char const *optval = devopt + sizeof dev_pattern - 1;
311 unsigned long int dev;
313 dev = strtoul (optval, &optvalend, 16);
314 if (optval != optvalend
315 && (*optvalend == '\0' || *optvalend == ',')
316 && ! (dev == ULONG_MAX && errno == ERANGE)
317 && dev == (dev_t) dev)
328 /* Return a list of the currently mounted file systems, or NULL on error.
329 Add each entry to the tail of the list so that they stay in order.
330 If NEED_FS_TYPE is true, ensure that the file system type fields in
331 the returned list are valid. Otherwise, they might not be. */
334 read_file_system_list (bool need_fs_type)
336 struct mount_entry *mount_list;
337 struct mount_entry *me;
338 struct mount_entry **mtail = &mount_list;
340 #ifdef MOUNTED_LISTMNTENT
342 struct tabmntent *mntlist, *p;
344 struct mount_entry *me;
346 /* the third and fourth arguments could be used to filter mounts,
347 but Crays doesn't seem to have any mounts that we want to
348 remove. Specifically, automount create normal NFS mounts.
351 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
353 for (p = mntlist; p; p = p->next) {
355 me = xmalloc (sizeof *me);
356 me->me_devname = xstrdup (mnt->mnt_fsname);
357 me->me_mountdir = xstrdup (mnt->mnt_dir);
358 me->me_type = xstrdup (mnt->mnt_type);
359 me->me_type_malloced = 1;
360 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
361 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
364 mtail = &me->me_next;
366 freemntlist (mntlist);
370 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
373 char *table = MOUNTED;
376 fp = setmntent (table, "r");
380 while ((mnt = getmntent (fp)))
382 me = xmalloc (sizeof *me);
383 me->me_devname = xstrdup (mnt->mnt_fsname);
384 me->me_mountdir = xstrdup (mnt->mnt_dir);
385 me->me_type = xstrdup (mnt->mnt_type);
386 me->me_type_malloced = 1;
387 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
388 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
389 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
391 /* Add to the linked list. */
393 mtail = &me->me_next;
396 if (endmntent (fp) == 0)
399 #endif /* MOUNTED_GETMNTENT1. */
401 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
406 entries = getmntinfo (&fsp, MNT_NOWAIT);
409 for (; entries-- > 0; fsp++)
411 char *fs_type = fsp_to_string (fsp);
413 me = xmalloc (sizeof *me);
414 me->me_devname = xstrdup (fsp->f_mntfromname);
415 me->me_mountdir = xstrdup (fsp->f_mntonname);
416 me->me_type = fs_type;
417 me->me_type_malloced = 0;
418 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
419 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
420 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
422 /* Add to the linked list. */
424 mtail = &me->me_next;
427 #endif /* MOUNTED_GETMNTINFO */
429 #ifdef MOUNTED_GETMNT /* Ultrix. */
436 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
439 me = xmalloc (sizeof *me);
440 me->me_devname = xstrdup (fsd.fd_req.devname);
441 me->me_mountdir = xstrdup (fsd.fd_req.path);
442 me->me_type = gt_names[fsd.fd_req.fstype];
443 me->me_type_malloced = 0;
444 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
445 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
446 me->me_dev = fsd.fd_req.dev;
448 /* Add to the linked list. */
450 mtail = &me->me_next;
455 #endif /* MOUNTED_GETMNT. */
457 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
459 /* The next_dev() and fs_stat_dev() system calls give the list of
460 all file systems, including the information returned by statvfs()
461 (fs type, total blocks, free blocks etc.), but without the mount
462 point. But on BeOS all file systems except / are mounted in the
463 rootfs, directly under /.
464 The directory name of the mount point is often, but not always,
465 identical to the volume name of the device.
466 We therefore get the list of subdirectories of /, and the list
467 of all file systems, and match the two lists. */
475 struct rootdir_entry *next;
477 struct rootdir_entry *rootdir_list;
478 struct rootdir_entry **rootdir_tail;
483 /* All volumes are mounted in the rootfs, directly under /. */
485 rootdir_tail = &rootdir_list;
486 dirp = opendir ("/");
491 while ((d = readdir (dirp)) != NULL)
496 if (strcmp (d->d_name, "..") == 0)
499 if (strcmp (d->d_name, ".") == 0)
500 name = xstrdup ("/");
503 name = xmalloc (1 + strlen (d->d_name) + 1);
505 strcpy (name + 1, d->d_name);
508 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
510 struct rootdir_entry *re = xmalloc (sizeof *re);
512 re->dev = statbuf.st_dev;
513 re->ino = statbuf.st_ino;
515 /* Add to the linked list. */
517 rootdir_tail = &re->next;
524 *rootdir_tail = NULL;
526 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
527 if (fs_stat_dev (dev, &fi) >= 0)
529 /* Note: fi.dev == dev. */
530 struct rootdir_entry *re;
532 for (re = rootdir_list; re; re = re->next)
533 if (re->dev == fi.dev && re->ino == fi.root)
536 me = xmalloc (sizeof *me);
537 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
538 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
539 me->me_type = xstrdup (fi.fsh_name);
540 me->me_type_malloced = 1;
543 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
545 /* Add to the linked list. */
547 mtail = &me->me_next;
551 while (rootdir_list != NULL)
553 struct rootdir_entry *re = rootdir_list;
554 rootdir_list = re->next;
559 #endif /* MOUNTED_FS_STAT_DEV */
561 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
565 struct statfs *stats;
567 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
570 if (SIZE_MAX / sizeof *stats <= numsys)
573 bufsize = (1 + numsys) * sizeof *stats;
574 stats = xmalloc (bufsize);
575 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
583 for (counter = 0; counter < numsys; counter++)
585 me = xmalloc (sizeof *me);
586 me->me_devname = xstrdup (stats[counter].f_mntfromname);
587 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
588 me->me_type = xstrdup (FS_TYPE (stats[counter]));
589 me->me_type_malloced = 1;
590 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
591 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
592 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
594 /* Add to the linked list. */
596 mtail = &me->me_next;
601 #endif /* MOUNTED_GETFSSTAT */
603 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
606 char *table = "/etc/mnttab";
609 fp = fopen (table, "r");
613 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
615 me = xmalloc (sizeof *me);
616 # ifdef GETFSTYP /* SVR3. */
617 me->me_devname = xstrdup (mnt.mt_dev);
619 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
620 strcpy (me->me_devname, "/dev/");
621 strcpy (me->me_devname + 5, mnt.mt_dev);
623 me->me_mountdir = xstrdup (mnt.mt_filsys);
624 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
626 me->me_type_malloced = 0;
627 # ifdef GETFSTYP /* SVR3. */
631 char typebuf[FSTYPSZ];
633 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
634 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
636 me->me_type = xstrdup (typebuf);
637 me->me_type_malloced = 1;
641 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
642 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
644 /* Add to the linked list. */
646 mtail = &me->me_next;
651 /* The last fread() call must have failed. */
652 int saved_errno = errno;
658 if (fclose (fp) == EOF)
661 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
663 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
665 struct mntent **mnttbl = getmnttbl (), **ent;
666 for (ent=mnttbl;*ent;ent++)
668 me = xmalloc (sizeof *me);
669 me->me_devname = xstrdup ( (*ent)->mt_resource);
670 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
671 me->me_type = xstrdup ((*ent)->mt_fstype);
672 me->me_type_malloced = 1;
673 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
674 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
675 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
677 /* Add to the linked list. */
679 mtail = &me->me_next;
685 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
688 char *table = MNTTAB;
693 # if defined F_RDLCK && defined F_SETLKW
694 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
695 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
696 for this file name, we should use their macro name instead.
697 (Why not just lock MNTTAB directly? We don't know.) */
699 # define MNTTAB_LOCK "/etc/.mnttab.lock"
701 lockfd = open (MNTTAB_LOCK, O_RDONLY);
705 flock.l_type = F_RDLCK;
706 flock.l_whence = SEEK_SET;
709 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
712 int saved_errno = errno;
718 else if (errno != ENOENT)
723 fp = fopen (table, "r");
728 while ((ret = getmntent (fp, &mnt)) == 0)
730 me = xmalloc (sizeof *me);
731 me->me_devname = xstrdup (mnt.mnt_special);
732 me->me_mountdir = xstrdup (mnt.mnt_mountp);
733 me->me_type = xstrdup (mnt.mnt_fstype);
734 me->me_type_malloced = 1;
735 me->me_dummy = MNT_IGNORE (&mnt) != 0;
736 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
737 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
739 /* Add to the linked list. */
741 mtail = &me->me_next;
744 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
747 if (0 <= lockfd && close (lockfd) != 0)
756 #endif /* MOUNTED_GETMNTENT2. */
758 #ifdef MOUNTED_VMOUNT /* AIX. */
761 char *entries, *thisent;
766 /* Ask how many bytes to allocate for the mounted file system info. */
767 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
769 entries = xmalloc (bufsize);
771 /* Get the list of mounted file systems. */
772 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
775 int saved_errno = errno;
781 for (i = 0, thisent = entries;
783 i++, thisent += vmp->vmt_length)
785 char *options, *ignore;
787 vmp = (struct vmount *) thisent;
788 me = xmalloc (sizeof *me);
789 if (vmp->vmt_flags & MNT_REMOTE)
794 /* Prepend the remote dirname. */
795 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
796 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
797 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
798 strcpy (me->me_devname, host);
799 strcat (me->me_devname, ":");
800 strcat (me->me_devname, dir);
805 me->me_devname = xstrdup (thisent +
806 vmp->vmt_data[VMT_OBJECT].vmt_off);
808 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
809 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
810 me->me_type_malloced = 1;
811 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
812 ignore = strstr (options, "ignore");
813 me->me_dummy = (ignore
814 && (ignore == options || ignore[-1] == ',')
815 && (ignore[sizeof "ignore" - 1] == ','
816 || ignore[sizeof "ignore" - 1] == '\0'));
817 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
819 /* Add to the linked list. */
821 mtail = &me->me_next;
825 #endif /* MOUNTED_VMOUNT. */
833 int saved_errno = errno;
838 me = mount_list->me_next;
839 free (mount_list->me_devname);
840 free (mount_list->me_mountdir);
841 if (mount_list->me_type_malloced)
842 free (mount_list->me_type);