1 /* mountlist.c -- return a list of mounted file systems
3 Copyright (C) 1991, 1992, 1997-2009 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "mountlist.h"
36 # include <sys/param.h>
39 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
41 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
42 NGROUPS is used as an array dimension in ucred.h */
43 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
46 # include <sys/mount.h>
48 # if HAVE_SYS_FS_TYPES_H
49 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
51 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
52 # define FS_TYPE(Ent) ((Ent).f_fstypename)
54 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
56 #endif /* MOUNTED_GETFSSTAT */
58 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
61 # if defined _PATH_MOUNTED /* GNU libc */
62 # define MOUNTED _PATH_MOUNTED
64 # if defined MNT_MNTTAB /* HP-UX. */
65 # define MOUNTED MNT_MNTTAB
67 # if defined MNTTABNAME /* Dynix. */
68 # define MOUNTED MNTTABNAME
73 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
74 # include <sys/mount.h>
77 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
78 # include <sys/statvfs.h>
81 #ifdef MOUNTED_GETMNT /* Ultrix. */
82 # include <sys/mount.h>
83 # include <sys/fs_types.h>
86 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
91 #ifdef MOUNTED_FREAD /* SVR2. */
95 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
97 # include <sys/fstyp.h>
98 # include <sys/statfs.h>
101 #ifdef MOUNTED_LISTMNTENT
105 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
106 # include <sys/mnttab.h>
109 #ifdef MOUNTED_VMOUNT /* AIX. */
111 # include <sys/vfs.h>
115 /* So special that it's not worth putting this in autoconf. */
116 # undef MOUNTED_FREAD_FSTYP
117 # define MOUNTED_GETMNTTBL
120 #if HAVE_SYS_MNTENT_H
121 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
122 # include <sys/mntent.h>
126 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
127 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
129 # define MNT_IGNORE(M) 0
133 # include "unlocked-io.h"
137 # define SIZE_MAX ((size_t) -1)
140 /* The results of open() in this file are not used with fchdir,
141 therefore save some unnecessary work in fchdir.c. */
145 /* The results of opendir() in this file are not used with dirfd and fchdir,
146 therefore save some unnecessary work in fchdir.c. */
151 # define ME_DUMMY(Fs_name, Fs_type) \
152 (strcmp (Fs_type, "autofs") == 0 \
153 || strcmp (Fs_type, "none") == 0 \
154 || strcmp (Fs_type, "proc") == 0 \
155 || strcmp (Fs_type, "subfs") == 0 \
156 /* for NetBSD 3.0 */ \
157 || strcmp (Fs_type, "kernfs") == 0 \
159 || strcmp (Fs_type, "ignore") == 0)
163 /* A file system is `remote' if its Fs_name contains a `:'
164 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
165 # define ME_REMOTE(Fs_name, Fs_type) \
166 (strchr (Fs_name, ':') != NULL \
167 || ((Fs_name)[0] == '/' \
168 && (Fs_name)[1] == '/' \
169 && (strcmp (Fs_type, "smbfs") == 0 \
170 || strcmp (Fs_type, "cifs") == 0)))
173 #if MOUNTED_GETMNTINFO
175 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
177 fstype_to_string (short int t)
272 fsp_to_string (const struct statfs *fsp)
274 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
275 return (char *) (fsp->f_fstypename);
277 return fstype_to_string (fsp->f_type);
281 #endif /* MOUNTED_GETMNTINFO */
283 #ifdef MOUNTED_VMOUNT /* AIX. */
285 fstype_to_string (int t)
289 e = getvfsbytype (t);
290 if (!e || !e->vfsent_name)
293 return e->vfsent_name;
295 #endif /* MOUNTED_VMOUNT */
298 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
300 /* Return the device number from MOUNT_OPTIONS, if possible.
301 Otherwise return (dev_t) -1. */
303 dev_from_mount_options (char const *mount_options)
305 /* GNU/Linux allows file system implementations to define their own
306 meaning for "dev=" mount options, so don't trust the meaning
310 static char const dev_pattern[] = ",dev=";
311 char const *devopt = strstr (mount_options, dev_pattern);
315 char const *optval = devopt + sizeof dev_pattern - 1;
317 unsigned long int dev;
319 dev = strtoul (optval, &optvalend, 16);
320 if (optval != optvalend
321 && (*optvalend == '\0' || *optvalend == ',')
322 && ! (dev == ULONG_MAX && errno == ERANGE)
323 && dev == (dev_t) dev)
328 (void) mount_options;
334 /* Return a list of the currently mounted file systems, or NULL on error.
335 Add each entry to the tail of the list so that they stay in order.
336 If NEED_FS_TYPE is true, ensure that the file system type fields in
337 the returned list are valid. Otherwise, they might not be. */
340 read_file_system_list (bool need_fs_type)
342 struct mount_entry *mount_list;
343 struct mount_entry *me;
344 struct mount_entry **mtail = &mount_list;
347 #ifdef MOUNTED_LISTMNTENT
349 struct tabmntent *mntlist, *p;
351 struct mount_entry *me;
353 /* the third and fourth arguments could be used to filter mounts,
354 but Crays doesn't seem to have any mounts that we want to
355 remove. Specifically, automount create normal NFS mounts.
358 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
360 for (p = mntlist; p; p = p->next) {
362 me = xmalloc (sizeof *me);
363 me->me_devname = xstrdup (mnt->mnt_fsname);
364 me->me_mountdir = xstrdup (mnt->mnt_dir);
365 me->me_type = xstrdup (mnt->mnt_type);
366 me->me_type_malloced = 1;
367 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
368 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
371 mtail = &me->me_next;
373 freemntlist (mntlist);
377 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
380 char const *table = MOUNTED;
383 fp = setmntent (table, "r");
387 while ((mnt = getmntent (fp)))
389 me = xmalloc (sizeof *me);
390 me->me_devname = xstrdup (mnt->mnt_fsname);
391 me->me_mountdir = xstrdup (mnt->mnt_dir);
392 me->me_type = xstrdup (mnt->mnt_type);
393 me->me_type_malloced = 1;
394 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
395 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
396 me->me_dev = dev_from_mount_options (mnt->mnt_opts);
398 /* Add to the linked list. */
400 mtail = &me->me_next;
403 if (endmntent (fp) == 0)
406 #endif /* MOUNTED_GETMNTENT1. */
408 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
413 entries = getmntinfo (&fsp, MNT_NOWAIT);
416 for (; entries-- > 0; fsp++)
418 char *fs_type = fsp_to_string (fsp);
420 me = xmalloc (sizeof *me);
421 me->me_devname = xstrdup (fsp->f_mntfromname);
422 me->me_mountdir = xstrdup (fsp->f_mntonname);
423 me->me_type = fs_type;
424 me->me_type_malloced = 0;
425 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
426 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
427 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
429 /* Add to the linked list. */
431 mtail = &me->me_next;
434 #endif /* MOUNTED_GETMNTINFO */
436 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
441 entries = getmntinfo (&fsp, MNT_NOWAIT);
444 for (; entries-- > 0; fsp++)
446 me = xmalloc (sizeof *me);
447 me->me_devname = xstrdup (fsp->f_mntfromname);
448 me->me_mountdir = xstrdup (fsp->f_mntonname);
449 me->me_type = xstrdup (fsp->f_fstypename);
450 me->me_type_malloced = 1;
451 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
452 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
453 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
455 /* Add to the linked list. */
457 mtail = &me->me_next;
460 #endif /* MOUNTED_GETMNTINFO2 */
462 #ifdef MOUNTED_GETMNT /* Ultrix. */
469 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
472 me = xmalloc (sizeof *me);
473 me->me_devname = xstrdup (fsd.fd_req.devname);
474 me->me_mountdir = xstrdup (fsd.fd_req.path);
475 me->me_type = gt_names[fsd.fd_req.fstype];
476 me->me_type_malloced = 0;
477 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
478 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
479 me->me_dev = fsd.fd_req.dev;
481 /* Add to the linked list. */
483 mtail = &me->me_next;
488 #endif /* MOUNTED_GETMNT. */
490 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
492 /* The next_dev() and fs_stat_dev() system calls give the list of
493 all file systems, including the information returned by statvfs()
494 (fs type, total blocks, free blocks etc.), but without the mount
495 point. But on BeOS all file systems except / are mounted in the
496 rootfs, directly under /.
497 The directory name of the mount point is often, but not always,
498 identical to the volume name of the device.
499 We therefore get the list of subdirectories of /, and the list
500 of all file systems, and match the two lists. */
508 struct rootdir_entry *next;
510 struct rootdir_entry *rootdir_list;
511 struct rootdir_entry **rootdir_tail;
516 /* All volumes are mounted in the rootfs, directly under /. */
518 rootdir_tail = &rootdir_list;
519 dirp = opendir ("/");
524 while ((d = readdir (dirp)) != NULL)
529 if (strcmp (d->d_name, "..") == 0)
532 if (strcmp (d->d_name, ".") == 0)
533 name = xstrdup ("/");
536 name = xmalloc (1 + strlen (d->d_name) + 1);
538 strcpy (name + 1, d->d_name);
541 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
543 struct rootdir_entry *re = xmalloc (sizeof *re);
545 re->dev = statbuf.st_dev;
546 re->ino = statbuf.st_ino;
548 /* Add to the linked list. */
550 rootdir_tail = &re->next;
557 *rootdir_tail = NULL;
559 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
560 if (fs_stat_dev (dev, &fi) >= 0)
562 /* Note: fi.dev == dev. */
563 struct rootdir_entry *re;
565 for (re = rootdir_list; re; re = re->next)
566 if (re->dev == fi.dev && re->ino == fi.root)
569 me = xmalloc (sizeof *me);
570 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
571 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
572 me->me_type = xstrdup (fi.fsh_name);
573 me->me_type_malloced = 1;
576 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
578 /* Add to the linked list. */
580 mtail = &me->me_next;
584 while (rootdir_list != NULL)
586 struct rootdir_entry *re = rootdir_list;
587 rootdir_list = re->next;
592 #endif /* MOUNTED_FS_STAT_DEV */
594 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
598 struct statfs *stats;
600 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
603 if (SIZE_MAX / sizeof *stats <= numsys)
606 bufsize = (1 + numsys) * sizeof *stats;
607 stats = xmalloc (bufsize);
608 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
616 for (counter = 0; counter < numsys; counter++)
618 me = xmalloc (sizeof *me);
619 me->me_devname = xstrdup (stats[counter].f_mntfromname);
620 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
621 me->me_type = xstrdup (FS_TYPE (stats[counter]));
622 me->me_type_malloced = 1;
623 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
624 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
625 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
627 /* Add to the linked list. */
629 mtail = &me->me_next;
634 #endif /* MOUNTED_GETFSSTAT */
636 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
639 char *table = "/etc/mnttab";
642 fp = fopen (table, "r");
646 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
648 me = xmalloc (sizeof *me);
649 # ifdef GETFSTYP /* SVR3. */
650 me->me_devname = xstrdup (mnt.mt_dev);
652 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
653 strcpy (me->me_devname, "/dev/");
654 strcpy (me->me_devname + 5, mnt.mt_dev);
656 me->me_mountdir = xstrdup (mnt.mt_filsys);
657 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
659 me->me_type_malloced = 0;
660 # ifdef GETFSTYP /* SVR3. */
664 char typebuf[FSTYPSZ];
666 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
667 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
669 me->me_type = xstrdup (typebuf);
670 me->me_type_malloced = 1;
674 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
675 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
677 /* Add to the linked list. */
679 mtail = &me->me_next;
684 /* The last fread() call must have failed. */
685 int saved_errno = errno;
691 if (fclose (fp) == EOF)
694 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
696 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
698 struct mntent **mnttbl = getmnttbl (), **ent;
699 for (ent=mnttbl;*ent;ent++)
701 me = xmalloc (sizeof *me);
702 me->me_devname = xstrdup ( (*ent)->mt_resource);
703 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
704 me->me_type = xstrdup ((*ent)->mt_fstype);
705 me->me_type_malloced = 1;
706 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
707 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
708 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
710 /* Add to the linked list. */
712 mtail = &me->me_next;
718 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
721 char *table = MNTTAB;
726 # if defined F_RDLCK && defined F_SETLKW
727 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
728 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
729 for this file name, we should use their macro name instead.
730 (Why not just lock MNTTAB directly? We don't know.) */
732 # define MNTTAB_LOCK "/etc/.mnttab.lock"
734 lockfd = open (MNTTAB_LOCK, O_RDONLY);
738 flock.l_type = F_RDLCK;
739 flock.l_whence = SEEK_SET;
742 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
745 int saved_errno = errno;
751 else if (errno != ENOENT)
756 fp = fopen (table, "r");
761 while ((ret = getmntent (fp, &mnt)) == 0)
763 me = xmalloc (sizeof *me);
764 me->me_devname = xstrdup (mnt.mnt_special);
765 me->me_mountdir = xstrdup (mnt.mnt_mountp);
766 me->me_type = xstrdup (mnt.mnt_fstype);
767 me->me_type_malloced = 1;
768 me->me_dummy = MNT_IGNORE (&mnt) != 0;
769 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
770 me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
772 /* Add to the linked list. */
774 mtail = &me->me_next;
777 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
780 if (0 <= lockfd && close (lockfd) != 0)
789 #endif /* MOUNTED_GETMNTENT2. */
791 #ifdef MOUNTED_VMOUNT /* AIX. */
794 char *entries, *thisent;
799 /* Ask how many bytes to allocate for the mounted file system info. */
800 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
802 entries = xmalloc (bufsize);
804 /* Get the list of mounted file systems. */
805 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
808 int saved_errno = errno;
814 for (i = 0, thisent = entries;
816 i++, thisent += vmp->vmt_length)
818 char *options, *ignore;
820 vmp = (struct vmount *) thisent;
821 me = xmalloc (sizeof *me);
822 if (vmp->vmt_flags & MNT_REMOTE)
827 /* Prepend the remote dirname. */
828 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
829 dir = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
830 me->me_devname = xmalloc (strlen (host) + strlen (dir) + 2);
831 strcpy (me->me_devname, host);
832 strcat (me->me_devname, ":");
833 strcat (me->me_devname, dir);
838 me->me_devname = xstrdup (thisent +
839 vmp->vmt_data[VMT_OBJECT].vmt_off);
841 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
842 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
843 me->me_type_malloced = 1;
844 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
845 ignore = strstr (options, "ignore");
846 me->me_dummy = (ignore
847 && (ignore == options || ignore[-1] == ',')
848 && (ignore[sizeof "ignore" - 1] == ','
849 || ignore[sizeof "ignore" - 1] == '\0'));
850 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
852 /* Add to the linked list. */
854 mtail = &me->me_next;
858 #endif /* MOUNTED_VMOUNT. */
866 int saved_errno = errno;
871 me = mount_list->me_next;
872 free (mount_list->me_devname);
873 free (mount_list->me_mountdir);
874 if (mount_list->me_type_malloced)
875 free (mount_list->me_type);