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 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 and its Fs_name starts with `//'). */
157 # define ME_REMOTE(Fs_name, Fs_type) \
158 (strchr (Fs_name, ':') != 0 \
159 || ((Fs_name)[0] == '/' \
160 && (Fs_name)[1] == '/' \
161 && strcmp (Fs_type, "smbfs") == 0))
164 #if MOUNTED_GETMNTINFO
166 # if ! HAVE_F_FSTYPENAME_IN_STATFS
168 fstype_to_string (short int t)
260 # endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */
262 /* __NetBSD__ || BSD_NET2 || __OpenBSD__ */
264 fsp_to_string (const struct statfs *fsp)
266 # if defined HAVE_F_FSTYPENAME_IN_STATFS
267 return (char *) (fsp->f_fstypename);
269 return fstype_to_string (fsp->f_type);
273 #endif /* MOUNTED_GETMNTINFO */
275 #ifdef MOUNTED_VMOUNT /* AIX. */
277 fstype_to_string (int t)
281 e = getvfsbytype (t);
282 if (!e || !e->vfsent_name)
285 return e->vfsent_name;
287 #endif /* MOUNTED_VMOUNT */
290 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
292 /* Return the device number from MOUNT_OPTIONS, if possible.
293 Otherwise return (dev_t) -1. */
296 dev_from_mount_options (char const *mount_options)
298 /* GNU/Linux allows file system implementations to define their own
299 meaning for "dev=" mount options, so don't trust the meaning
303 static char const dev_pattern[] = ",dev=";
304 char const *devopt = strstr (mount_options, dev_pattern);
308 char const *optval = devopt + sizeof dev_pattern - 1;
310 unsigned long int dev;
312 dev = strtoul (optval, &optvalend, 16);
313 if (optval != optvalend
314 && (*optvalend == '\0' || *optvalend == ',')
315 && ! (dev == ULONG_MAX && errno == ERANGE)
316 && dev == (dev_t) dev)
327 /* Return a list of the currently mounted file systems, or NULL on error.
328 Add each entry to the tail of the list so that they stay in order.
329 If NEED_FS_TYPE is true, ensure that the file system type fields in
330 the returned list are valid. Otherwise, they might not be. */
333 read_file_system_list (bool need_fs_type)
335 struct mount_entry *mount_list;
336 struct mount_entry *me;
337 struct mount_entry **mtail = &mount_list;
339 #ifdef MOUNTED_LISTMNTENT
341 struct tabmntent *mntlist, *p;
343 struct mount_entry *me;
345 /* the third and fourth arguments could be used to filter mounts,
346 but Crays doesn't seem to have any mounts that we want to
347 remove. Specifically, automount create normal NFS mounts.
350 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
352 for (p = mntlist; p; p = p->next) {
354 me = xmalloc (sizeof *me);
355 me->me_devname = xstrdup (mnt->mnt_fsname);
356 me->me_mountdir = xstrdup (mnt->mnt_dir);
357 me->me_type = xstrdup (mnt->mnt_type);
358 me->me_type_malloced = 1;
359 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
360 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
363 mtail = &me->me_next;
365 freemntlist (mntlist);
369 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
372 char *table = MOUNTED;
375 fp = setmntent (table, "r");
379 while ((mnt = getmntent (fp)))
381 me = xmalloc (sizeof *me);
382 me->me_devname = xstrdup (mnt->mnt_fsname);
383 me->me_mountdir = xstrdup (mnt->mnt_dir);
384 me->me_type = xstrdup (mnt->mnt_type);
385 me->me_type_malloced = 1;
386 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
387 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
388 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
390 /* Add to the linked list. */
392 mtail = &me->me_next;
395 if (endmntent (fp) == 0)
398 #endif /* MOUNTED_GETMNTENT1. */
400 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
405 entries = getmntinfo (&fsp, MNT_NOWAIT);
408 for (; entries-- > 0; fsp++)
410 char *fs_type = fsp_to_string (fsp);
412 me = xmalloc (sizeof *me);
413 me->me_devname = xstrdup (fsp->f_mntfromname);
414 me->me_mountdir = xstrdup (fsp->f_mntonname);
415 me->me_type = fs_type;
416 me->me_type_malloced = 0;
417 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
418 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
419 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
421 /* Add to the linked list. */
423 mtail = &me->me_next;
426 #endif /* MOUNTED_GETMNTINFO */
428 #ifdef MOUNTED_GETMNT /* Ultrix. */
435 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
438 me = xmalloc (sizeof *me);
439 me->me_devname = xstrdup (fsd.fd_req.devname);
440 me->me_mountdir = xstrdup (fsd.fd_req.path);
441 me->me_type = gt_names[fsd.fd_req.fstype];
442 me->me_type_malloced = 0;
443 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
444 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
445 me->me_dev = fsd.fd_req.dev;
447 /* Add to the linked list. */
449 mtail = &me->me_next;
454 #endif /* MOUNTED_GETMNT. */
456 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
458 /* The next_dev() and fs_stat_dev() system calls give the list of
459 all file systems, including the information returned by statvfs()
460 (fs type, total blocks, free blocks etc.), but without the mount
461 point. But on BeOS all file systems except / are mounted in the
462 rootfs, directly under /.
463 The directory name of the mount point is often, but not always,
464 identical to the volume name of the device.
465 We therefore get the list of subdirectories of /, and the list
466 of all file systems, and match the two lists. */
474 struct rootdir_entry *next;
476 struct rootdir_entry *rootdir_list;
477 struct rootdir_entry **rootdir_tail;
482 /* All volumes are mounted in the rootfs, directly under /. */
484 rootdir_tail = &rootdir_list;
485 dirp = opendir ("/");
490 while ((d = readdir (dirp)) != NULL)
495 if (strcmp (d->d_name, "..") == 0)
498 if (strcmp (d->d_name, ".") == 0)
499 name = xstrdup ("/");
502 name = xmalloc (1 + strlen (d->d_name) + 1);
504 strcpy (name + 1, d->d_name);
507 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
509 struct rootdir_entry *re = xmalloc (sizeof *re);
511 re->dev = statbuf.st_dev;
512 re->ino = statbuf.st_ino;
514 /* Add to the linked list. */
516 rootdir_tail = &re->next;
523 *rootdir_tail = NULL;
525 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
526 if (fs_stat_dev (dev, &fi) >= 0)
528 /* Note: fi.dev == dev. */
529 struct rootdir_entry *re;
531 for (re = rootdir_list; re; re = re->next)
532 if (re->dev == fi.dev && re->ino == fi.root)
535 me = xmalloc (sizeof *me);
536 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
537 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
538 me->me_type = xstrdup (fi.fsh_name);
539 me->me_type_malloced = 1;
542 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
544 /* Add to the linked list. */
546 mtail = &me->me_next;
550 while (rootdir_list != NULL)
552 struct rootdir_entry *re = rootdir_list;
553 rootdir_list = re->next;
558 #endif /* MOUNTED_FS_STAT_DEV */
560 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
564 struct statfs *stats;
566 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
569 if (SIZE_MAX / sizeof *stats <= numsys)
572 bufsize = (1 + numsys) * sizeof *stats;
573 stats = xmalloc (bufsize);
574 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
582 for (counter = 0; counter < numsys; counter++)
584 me = xmalloc (sizeof *me);
585 me->me_devname = xstrdup (stats[counter].f_mntfromname);
586 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
587 me->me_type = xstrdup (FS_TYPE (stats[counter]));
588 me->me_type_malloced = 1;
589 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
590 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
591 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
593 /* Add to the linked list. */
595 mtail = &me->me_next;
600 #endif /* MOUNTED_GETFSSTAT */
602 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
605 char *table = "/etc/mnttab";
608 fp = fopen (table, "r");
612 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
614 me = xmalloc (sizeof *me);
615 # ifdef GETFSTYP /* SVR3. */
616 me->me_devname = xstrdup (mnt.mt_dev);
618 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
619 strcpy (me->me_devname, "/dev/");
620 strcpy (me->me_devname + 5, mnt.mt_dev);
622 me->me_mountdir = xstrdup (mnt.mt_filsys);
623 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
625 me->me_type_malloced = 0;
626 # ifdef GETFSTYP /* SVR3. */
630 char typebuf[FSTYPSZ];
632 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
633 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
635 me->me_type = xstrdup (typebuf);
636 me->me_type_malloced = 1;
640 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
641 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
643 /* Add to the linked list. */
645 mtail = &me->me_next;
650 /* The last fread() call must have failed. */
651 int saved_errno = errno;
657 if (fclose (fp) == EOF)
660 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
662 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
664 struct mntent **mnttbl = getmnttbl (), **ent;
665 for (ent=mnttbl;*ent;ent++)
667 me = xmalloc (sizeof *me);
668 me->me_devname = xstrdup ( (*ent)->mt_resource);
669 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
670 me->me_type = xstrdup ((*ent)->mt_fstype);
671 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);
674 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
676 /* Add to the linked list. */
678 mtail = &me->me_next;
684 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
687 char *table = MNTTAB;
692 # if defined F_RDLCK && defined F_SETLKW
693 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
694 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
695 for this file name, we should use their macro name instead.
696 (Why not just lock MNTTAB directly? We don't know.) */
698 # define MNTTAB_LOCK "/etc/.mnttab.lock"
700 lockfd = open (MNTTAB_LOCK, O_RDONLY);
704 flock.l_type = F_RDLCK;
705 flock.l_whence = SEEK_SET;
708 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
711 int saved_errno = errno;
717 else if (errno != ENOENT)
722 fp = fopen (table, "r");
727 while ((ret = getmntent (fp, &mnt)) == 0)
729 me = xmalloc (sizeof *me);
730 me->me_devname = xstrdup (mnt.mnt_special);
731 me->me_mountdir = xstrdup (mnt.mnt_mountp);
732 me->me_type = xstrdup (mnt.mnt_fstype);
733 me->me_type_malloced = 1;
734 me->me_dummy = MNT_IGNORE (&mnt) != 0;
735 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
736 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
738 /* Add to the linked list. */
740 mtail = &me->me_next;
743 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
746 if (0 <= lockfd && close (lockfd) != 0)
755 #endif /* MOUNTED_GETMNTENT2. */
757 #ifdef MOUNTED_VMOUNT /* AIX. */
760 char *entries, *thisent;
765 /* Ask how many bytes to allocate for the mounted file system info. */
766 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
768 entries = xmalloc (bufsize);
770 /* Get the list of mounted file systems. */
771 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
774 int saved_errno = errno;
780 for (i = 0, thisent = entries;
782 i++, thisent += vmp->vmt_length)
784 char *options, *ignore;
786 vmp = (struct vmount *) thisent;
787 me = xmalloc (sizeof *me);
788 if (vmp->vmt_flags & MNT_REMOTE)
793 /* Prepend the remote dirname. */
794 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
795 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
796 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
797 strcpy (me->me_devname, host);
798 strcat (me->me_devname, ":");
799 strcat (me->me_devname, dir);
804 me->me_devname = xstrdup (thisent +
805 vmp->vmt_data[VMT_OBJECT].vmt_off);
807 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
808 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
809 me->me_type_malloced = 1;
810 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
811 ignore = strstr (options, "ignore");
812 me->me_dummy = (ignore
813 && (ignore == options || ignore[-1] == ',')
814 && (ignore[sizeof "ignore" - 1] == ','
815 || ignore[sizeof "ignore" - 1] == '\0'));
816 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
818 /* Add to the linked list. */
820 mtail = &me->me_next;
824 #endif /* MOUNTED_VMOUNT. */
832 int saved_errno = errno;
837 me = mount_list->me_next;
838 free (mount_list->me_devname);
839 free (mount_list->me_mountdir);
840 if (mount_list->me_type_malloced)
841 free (mount_list->me_type);