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)
146 /* The results of open() in this file are not used with fchdir,
147 therefore save some unnecessary work in fchdir.c. */
151 /* The results of opendir() in this file are not used with dirfd and fchdir,
152 therefore save some unnecessary work in fchdir.c. */
157 # define ME_DUMMY(Fs_name, Fs_type) \
158 (strcmp (Fs_type, "autofs") == 0 \
159 || strcmp (Fs_type, "none") == 0 \
160 || strcmp (Fs_type, "proc") == 0 \
161 || strcmp (Fs_type, "subfs") == 0 \
162 /* for NetBSD 3.0 */ \
163 || strcmp (Fs_type, "kernfs") == 0 \
165 || strcmp (Fs_type, "ignore") == 0)
169 /* A file system is `remote' if its Fs_name contains a `:'
170 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
171 # define ME_REMOTE(Fs_name, Fs_type) \
172 (strchr (Fs_name, ':') != NULL \
173 || ((Fs_name)[0] == '/' \
174 && (Fs_name)[1] == '/' \
175 && (strcmp (Fs_type, "smbfs") == 0 \
176 || strcmp (Fs_type, "cifs") == 0)))
179 #if MOUNTED_GETMNTINFO
181 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
183 fstype_to_string (short int t)
278 fsp_to_string (const struct statfs *fsp)
280 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
281 return (char *) (fsp->f_fstypename);
283 return fstype_to_string (fsp->f_type);
287 #endif /* MOUNTED_GETMNTINFO */
289 #ifdef MOUNTED_VMOUNT /* AIX. */
291 fstype_to_string (int t)
295 e = getvfsbytype (t);
296 if (!e || !e->vfsent_name)
299 return e->vfsent_name;
301 #endif /* MOUNTED_VMOUNT */
304 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
306 /* Return the device number from MOUNT_OPTIONS, if possible.
307 Otherwise return (dev_t) -1. */
310 dev_from_mount_options (char const *mount_options)
312 /* GNU/Linux allows file system implementations to define their own
313 meaning for "dev=" mount options, so don't trust the meaning
317 static char const dev_pattern[] = ",dev=";
318 char const *devopt = strstr (mount_options, dev_pattern);
322 char const *optval = devopt + sizeof dev_pattern - 1;
324 unsigned long int dev;
326 dev = strtoul (optval, &optvalend, 16);
327 if (optval != optvalend
328 && (*optvalend == '\0' || *optvalend == ',')
329 && ! (dev == ULONG_MAX && errno == ERANGE)
330 && dev == (dev_t) dev)
341 /* Return a list of the currently mounted file systems, or NULL on error.
342 Add each entry to the tail of the list so that they stay in order.
343 If NEED_FS_TYPE is true, ensure that the file system type fields in
344 the returned list are valid. Otherwise, they might not be. */
347 read_file_system_list (bool need_fs_type)
349 struct mount_entry *mount_list;
350 struct mount_entry *me;
351 struct mount_entry **mtail = &mount_list;
353 #ifdef MOUNTED_LISTMNTENT
355 struct tabmntent *mntlist, *p;
357 struct mount_entry *me;
359 /* the third and fourth arguments could be used to filter mounts,
360 but Crays doesn't seem to have any mounts that we want to
361 remove. Specifically, automount create normal NFS mounts.
364 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
366 for (p = mntlist; p; p = p->next) {
368 me = xmalloc (sizeof *me);
369 me->me_devname = xstrdup (mnt->mnt_fsname);
370 me->me_mountdir = xstrdup (mnt->mnt_dir);
371 me->me_type = xstrdup (mnt->mnt_type);
372 me->me_type_malloced = 1;
373 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
374 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
377 mtail = &me->me_next;
379 freemntlist (mntlist);
383 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
386 char *table = MOUNTED;
389 fp = setmntent (table, "r");
393 while ((mnt = getmntent (fp)))
395 me = xmalloc (sizeof *me);
396 me->me_devname = xstrdup (mnt->mnt_fsname);
397 me->me_mountdir = xstrdup (mnt->mnt_dir);
398 me->me_type = xstrdup (mnt->mnt_type);
399 me->me_type_malloced = 1;
400 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
401 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
402 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
404 /* Add to the linked list. */
406 mtail = &me->me_next;
409 if (endmntent (fp) == 0)
412 #endif /* MOUNTED_GETMNTENT1. */
414 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
419 entries = getmntinfo (&fsp, MNT_NOWAIT);
422 for (; entries-- > 0; fsp++)
424 char *fs_type = fsp_to_string (fsp);
426 me = xmalloc (sizeof *me);
427 me->me_devname = xstrdup (fsp->f_mntfromname);
428 me->me_mountdir = xstrdup (fsp->f_mntonname);
429 me->me_type = fs_type;
430 me->me_type_malloced = 0;
431 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
432 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
433 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
435 /* Add to the linked list. */
437 mtail = &me->me_next;
440 #endif /* MOUNTED_GETMNTINFO */
442 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
447 entries = getmntinfo (&fsp, MNT_NOWAIT);
450 for (; entries-- > 0; fsp++)
452 me = xmalloc (sizeof *me);
453 me->me_devname = xstrdup (fsp->f_mntfromname);
454 me->me_mountdir = xstrdup (fsp->f_mntonname);
455 me->me_type = xstrdup (fsp->f_fstypename);
456 me->me_type_malloced = 1;
457 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
458 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
459 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
461 /* Add to the linked list. */
463 mtail = &me->me_next;
466 #endif /* MOUNTED_GETMNTINFO2 */
468 #ifdef MOUNTED_GETMNT /* Ultrix. */
475 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
478 me = xmalloc (sizeof *me);
479 me->me_devname = xstrdup (fsd.fd_req.devname);
480 me->me_mountdir = xstrdup (fsd.fd_req.path);
481 me->me_type = gt_names[fsd.fd_req.fstype];
482 me->me_type_malloced = 0;
483 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
484 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
485 me->me_dev = fsd.fd_req.dev;
487 /* Add to the linked list. */
489 mtail = &me->me_next;
494 #endif /* MOUNTED_GETMNT. */
496 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
498 /* The next_dev() and fs_stat_dev() system calls give the list of
499 all file systems, including the information returned by statvfs()
500 (fs type, total blocks, free blocks etc.), but without the mount
501 point. But on BeOS all file systems except / are mounted in the
502 rootfs, directly under /.
503 The directory name of the mount point is often, but not always,
504 identical to the volume name of the device.
505 We therefore get the list of subdirectories of /, and the list
506 of all file systems, and match the two lists. */
514 struct rootdir_entry *next;
516 struct rootdir_entry *rootdir_list;
517 struct rootdir_entry **rootdir_tail;
522 /* All volumes are mounted in the rootfs, directly under /. */
524 rootdir_tail = &rootdir_list;
525 dirp = opendir ("/");
530 while ((d = readdir (dirp)) != NULL)
535 if (strcmp (d->d_name, "..") == 0)
538 if (strcmp (d->d_name, ".") == 0)
539 name = xstrdup ("/");
542 name = xmalloc (1 + strlen (d->d_name) + 1);
544 strcpy (name + 1, d->d_name);
547 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
549 struct rootdir_entry *re = xmalloc (sizeof *re);
551 re->dev = statbuf.st_dev;
552 re->ino = statbuf.st_ino;
554 /* Add to the linked list. */
556 rootdir_tail = &re->next;
563 *rootdir_tail = NULL;
565 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
566 if (fs_stat_dev (dev, &fi) >= 0)
568 /* Note: fi.dev == dev. */
569 struct rootdir_entry *re;
571 for (re = rootdir_list; re; re = re->next)
572 if (re->dev == fi.dev && re->ino == fi.root)
575 me = xmalloc (sizeof *me);
576 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
577 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
578 me->me_type = xstrdup (fi.fsh_name);
579 me->me_type_malloced = 1;
582 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
584 /* Add to the linked list. */
586 mtail = &me->me_next;
590 while (rootdir_list != NULL)
592 struct rootdir_entry *re = rootdir_list;
593 rootdir_list = re->next;
598 #endif /* MOUNTED_FS_STAT_DEV */
600 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
604 struct statfs *stats;
606 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
609 if (SIZE_MAX / sizeof *stats <= numsys)
612 bufsize = (1 + numsys) * sizeof *stats;
613 stats = xmalloc (bufsize);
614 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
622 for (counter = 0; counter < numsys; counter++)
624 me = xmalloc (sizeof *me);
625 me->me_devname = xstrdup (stats[counter].f_mntfromname);
626 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
627 me->me_type = xstrdup (FS_TYPE (stats[counter]));
628 me->me_type_malloced = 1;
629 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
630 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
631 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
633 /* Add to the linked list. */
635 mtail = &me->me_next;
640 #endif /* MOUNTED_GETFSSTAT */
642 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
645 char *table = "/etc/mnttab";
648 fp = fopen (table, "r");
652 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
654 me = xmalloc (sizeof *me);
655 # ifdef GETFSTYP /* SVR3. */
656 me->me_devname = xstrdup (mnt.mt_dev);
658 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
659 strcpy (me->me_devname, "/dev/");
660 strcpy (me->me_devname + 5, mnt.mt_dev);
662 me->me_mountdir = xstrdup (mnt.mt_filsys);
663 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
665 me->me_type_malloced = 0;
666 # ifdef GETFSTYP /* SVR3. */
670 char typebuf[FSTYPSZ];
672 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
673 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
675 me->me_type = xstrdup (typebuf);
676 me->me_type_malloced = 1;
680 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
681 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
683 /* Add to the linked list. */
685 mtail = &me->me_next;
690 /* The last fread() call must have failed. */
691 int saved_errno = errno;
697 if (fclose (fp) == EOF)
700 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
702 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
704 struct mntent **mnttbl = getmnttbl (), **ent;
705 for (ent=mnttbl;*ent;ent++)
707 me = xmalloc (sizeof *me);
708 me->me_devname = xstrdup ( (*ent)->mt_resource);
709 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
710 me->me_type = xstrdup ((*ent)->mt_fstype);
711 me->me_type_malloced = 1;
712 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
713 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
714 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
716 /* Add to the linked list. */
718 mtail = &me->me_next;
724 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
727 char *table = MNTTAB;
732 # if defined F_RDLCK && defined F_SETLKW
733 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
734 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
735 for this file name, we should use their macro name instead.
736 (Why not just lock MNTTAB directly? We don't know.) */
738 # define MNTTAB_LOCK "/etc/.mnttab.lock"
740 lockfd = open (MNTTAB_LOCK, O_RDONLY);
744 flock.l_type = F_RDLCK;
745 flock.l_whence = SEEK_SET;
748 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
751 int saved_errno = errno;
757 else if (errno != ENOENT)
762 fp = fopen (table, "r");
767 while ((ret = getmntent (fp, &mnt)) == 0)
769 me = xmalloc (sizeof *me);
770 me->me_devname = xstrdup (mnt.mnt_special);
771 me->me_mountdir = xstrdup (mnt.mnt_mountp);
772 me->me_type = xstrdup (mnt.mnt_fstype);
773 me->me_type_malloced = 1;
774 me->me_dummy = MNT_IGNORE (&mnt) != 0;
775 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
776 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
778 /* Add to the linked list. */
780 mtail = &me->me_next;
783 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
786 if (0 <= lockfd && close (lockfd) != 0)
795 #endif /* MOUNTED_GETMNTENT2. */
797 #ifdef MOUNTED_VMOUNT /* AIX. */
800 char *entries, *thisent;
805 /* Ask how many bytes to allocate for the mounted file system info. */
806 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
808 entries = xmalloc (bufsize);
810 /* Get the list of mounted file systems. */
811 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
814 int saved_errno = errno;
820 for (i = 0, thisent = entries;
822 i++, thisent += vmp->vmt_length)
824 char *options, *ignore;
826 vmp = (struct vmount *) thisent;
827 me = xmalloc (sizeof *me);
828 if (vmp->vmt_flags & MNT_REMOTE)
833 /* Prepend the remote dirname. */
834 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
835 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
836 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
837 strcpy (me->me_devname, host);
838 strcat (me->me_devname, ":");
839 strcat (me->me_devname, dir);
844 me->me_devname = xstrdup (thisent +
845 vmp->vmt_data[VMT_OBJECT].vmt_off);
847 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
848 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
849 me->me_type_malloced = 1;
850 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
851 ignore = strstr (options, "ignore");
852 me->me_dummy = (ignore
853 && (ignore == options || ignore[-1] == ',')
854 && (ignore[sizeof "ignore" - 1] == ','
855 || ignore[sizeof "ignore" - 1] == '\0'));
856 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
858 /* Add to the linked list. */
860 mtail = &me->me_next;
864 #endif /* MOUNTED_VMOUNT. */
872 int saved_errno = errno;
877 me = mount_list->me_next;
878 free (mount_list->me_devname);
879 free (mount_list->me_mountdir);
880 if (mount_list->me_type_malloced)
881 free (mount_list->me_type);