1 /* mountlist.c -- return a list of mounted filesystems
2 Copyright (C) 1991, 1992, 1997-2004 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include <sys/types.h>
30 # define SIZE_MAX ((size_t) -1)
51 # include <sys/param.h>
54 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
56 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
57 NGROUPS is used as an array dimension in ucred.h */
58 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
61 # include <sys/mount.h>
63 # if HAVE_SYS_FS_TYPES_H
64 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
66 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
67 # define FS_TYPE(Ent) ((Ent).f_fstypename)
69 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
71 #endif /* MOUNTED_GETFSSTAT */
73 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
76 # if defined _PATH_MOUNTED /* GNU libc */
77 # define MOUNTED _PATH_MOUNTED
79 # if defined MNT_MNTTAB /* HP-UX. */
80 # define MOUNTED MNT_MNTTAB
82 # if defined MNTTABNAME /* Dynix. */
83 # define MOUNTED MNTTABNAME
88 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
89 # include <sys/mount.h>
92 #ifdef MOUNTED_GETMNT /* Ultrix. */
93 # include <sys/mount.h>
94 # include <sys/fs_types.h>
97 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
102 #ifdef MOUNTED_FREAD /* SVR2. */
106 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
108 # include <sys/fstyp.h>
109 # include <sys/statfs.h>
112 #ifdef MOUNTED_LISTMNTENT
116 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
117 # include <sys/mnttab.h>
120 #ifdef MOUNTED_VMOUNT /* AIX. */
122 # include <sys/vfs.h>
126 /* So special that it's not worth putting this in autoconf. */
127 # undef MOUNTED_FREAD_FSTYP
128 # define MOUNTED_GETMNTTBL
131 #if HAVE_SYS_MNTENT_H
132 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
133 # include <sys/mntent.h>
137 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
138 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
140 # define MNT_IGNORE(M) 0
143 #include "mountlist.h"
144 #include "unlocked-io.h"
146 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
147 /* Return the value of the hexadecimal number represented by CP.
148 No prefix (like '0x') or suffix (like 'h') is expected to be
150 /* FIXME: this can overflow */
160 if (*cp >= 'a' && *cp <= 'f')
161 val = val * 16 + *cp - 'a' + 10;
162 else if (*cp >= 'A' && *cp <= 'F')
163 val = val * 16 + *cp - 'A' + 10;
164 else if (*cp >= '0' && *cp <= '9')
165 val = val * 16 + *cp - '0';
172 #endif /* MOUNTED_GETMNTENT1. */
174 #if MOUNTED_GETMNTINFO
176 # if ! HAVE_F_FSTYPENAME_IN_STATFS
178 fstype_to_string (short t)
270 # endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */
272 /* __NetBSD__ || BSD_NET2 || __OpenBSD__ */
274 fsp_to_string (const struct statfs *fsp)
276 # if defined HAVE_F_FSTYPENAME_IN_STATFS
277 return (char *) (fsp->f_fstypename);
279 return fstype_to_string (fsp->f_type);
283 #endif /* MOUNTED_GETMNTINFO */
285 #ifdef MOUNTED_VMOUNT /* AIX. */
287 fstype_to_string (int t)
291 e = getvfsbytype (t);
292 if (!e || !e->vfsent_name)
295 return e->vfsent_name;
297 #endif /* MOUNTED_VMOUNT */
299 /* Return a list of the currently mounted filesystems, or NULL on error.
300 Add each entry to the tail of the list so that they stay in order.
301 If NEED_FS_TYPE is nonzero, ensure that the filesystem type fields in
302 the returned list are valid. Otherwise, they might not be. */
305 read_filesystem_list (int need_fs_type)
307 struct mount_entry *mount_list;
308 struct mount_entry *me;
309 struct mount_entry **mtail = &mount_list;
311 #ifdef MOUNTED_LISTMNTENT
313 struct tabmntent *mntlist, *p;
315 struct mount_entry *me;
317 /* the third and fourth arguments could be used to filter mounts,
318 but Crays doesn't seem to have any mounts that we want to
319 remove. Specifically, automount create normal NFS mounts.
322 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
324 for (p = mntlist; p; p = p->next) {
326 me = xmalloc (sizeof *me);
327 me->me_devname = xstrdup (mnt->mnt_fsname);
328 me->me_mountdir = xstrdup (mnt->mnt_dir);
329 me->me_type = xstrdup (mnt->mnt_type);
330 me->me_type_malloced = 1;
331 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
332 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
335 mtail = &me->me_next;
337 freemntlist (mntlist);
341 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
344 char *table = MOUNTED;
348 fp = setmntent (table, "r");
352 while ((mnt = getmntent (fp)))
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);
361 devopt = strstr (mnt->mnt_opts, "dev=");
364 if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
365 me->me_dev = xatoi (devopt + 6);
367 me->me_dev = xatoi (devopt + 4);
370 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
372 /* Add to the linked list. */
374 mtail = &me->me_next;
377 if (endmntent (fp) == 0)
380 #endif /* MOUNTED_GETMNTENT1. */
382 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
387 entries = getmntinfo (&fsp, MNT_NOWAIT);
390 for (; entries-- > 0; fsp++)
392 char *fs_type = fsp_to_string (fsp);
394 me = xmalloc (sizeof *me);
395 me->me_devname = xstrdup (fsp->f_mntfromname);
396 me->me_mountdir = xstrdup (fsp->f_mntonname);
397 me->me_type = fs_type;
398 me->me_type_malloced = 0;
399 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
400 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
401 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
403 /* Add to the linked list. */
405 mtail = &me->me_next;
408 #endif /* MOUNTED_GETMNTINFO */
410 #ifdef MOUNTED_GETMNT /* Ultrix. */
417 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
420 me = xmalloc (sizeof *me);
421 me->me_devname = xstrdup (fsd.fd_req.devname);
422 me->me_mountdir = xstrdup (fsd.fd_req.path);
423 me->me_type = gt_names[fsd.fd_req.fstype];
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 = fsd.fd_req.dev;
429 /* Add to the linked list. */
431 mtail = &me->me_next;
436 #endif /* MOUNTED_GETMNT. */
438 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
440 /* The next_dev() and fs_stat_dev() system calls give the list of
441 all filesystems, including the information returned by statvfs()
442 (fs type, total blocks, free blocks etc.), but without the mount
443 point. But on BeOS all filesystems except / are mounted in the
444 rootfs, directly under /.
445 The directory name of the mount point is often, but not always,
446 identical to the volume name of the device.
447 We therefore get the list of subdirectories of /, and the list
448 of all filesystems, and match the two lists. */
456 struct rootdir_entry *next;
458 struct rootdir_entry *rootdir_list;
459 struct rootdir_entry **rootdir_tail;
464 /* All volumes are mounted in the rootfs, directly under /. */
466 rootdir_tail = &rootdir_list;
467 dirp = opendir ("/");
472 while ((d = readdir (dirp)) != NULL)
477 if (strcmp (d->d_name, "..") == 0)
480 if (strcmp (d->d_name, ".") == 0)
481 name = xstrdup ("/");
484 name = xmalloc (1 + strlen (d->d_name) + 1);
486 strcpy (name + 1, d->d_name);
489 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
491 struct rootdir_entry *re = xmalloc (sizeof *re);
493 re->dev = statbuf.st_dev;
494 re->ino = statbuf.st_ino;
496 /* Add to the linked list. */
498 rootdir_tail = &re->next;
505 *rootdir_tail = NULL;
507 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
508 if (fs_stat_dev (dev, &fi) >= 0)
510 /* Note: fi.dev == dev. */
511 struct rootdir_entry *re;
513 for (re = rootdir_list; re; re = re->next)
514 if (re->dev == fi.dev && re->ino == fi.root)
517 me = xmalloc (sizeof *me);
518 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
519 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
520 me->me_type = xstrdup (fi.fsh_name);
521 me->me_type_malloced = 1;
524 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
526 /* Add to the linked list. */
528 mtail = &me->me_next;
532 while (rootdir_list != NULL)
534 struct rootdir_entry *re = rootdir_list;
535 rootdir_list = re->next;
540 #endif /* MOUNTED_FS_STAT_DEV */
542 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
546 struct statfs *stats;
548 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
551 if (SIZE_MAX / sizeof *stats <= numsys)
554 bufsize = (1 + numsys) * sizeof *stats;
555 stats = xmalloc (bufsize);
556 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
564 for (counter = 0; counter < numsys; counter++)
566 me = xmalloc (sizeof *me);
567 me->me_devname = xstrdup (stats[counter].f_mntfromname);
568 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
569 me->me_type = xstrdup (FS_TYPE (stats[counter]));
570 me->me_type_malloced = 1;
571 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
572 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
573 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
575 /* Add to the linked list. */
577 mtail = &me->me_next;
582 #endif /* MOUNTED_GETFSSTAT */
584 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
587 char *table = "/etc/mnttab";
590 fp = fopen (table, "r");
594 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
596 me = xmalloc (sizeof *me);
597 # ifdef GETFSTYP /* SVR3. */
598 me->me_devname = xstrdup (mnt.mt_dev);
600 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
601 strcpy (me->me_devname, "/dev/");
602 strcpy (me->me_devname + 5, mnt.mt_dev);
604 me->me_mountdir = xstrdup (mnt.mt_filsys);
605 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
607 me->me_type_malloced = 0;
608 # ifdef GETFSTYP /* SVR3. */
612 char typebuf[FSTYPSZ];
614 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
615 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
617 me->me_type = xstrdup (typebuf);
618 me->me_type_malloced = 1;
622 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
623 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
625 /* Add to the linked list. */
627 mtail = &me->me_next;
632 /* The last fread() call must have failed. */
633 int saved_errno = errno;
639 if (fclose (fp) == EOF)
642 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
644 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes it's own way */
646 struct mntent **mnttbl = getmnttbl (), **ent;
647 for (ent=mnttbl;*ent;ent++)
649 me = xmalloc (sizeof *me);
650 me->me_devname = xstrdup ( (*ent)->mt_resource);
651 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
652 me->me_type = xstrdup ((*ent)->mt_fstype);
653 me->me_type_malloced = 1;
654 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
655 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
656 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
658 /* Add to the linked list. */
660 mtail = &me->me_next;
666 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
669 char *table = MNTTAB;
674 # if defined F_RDLCK && defined F_SETLKW
675 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
676 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
677 for this file name, we should use their macro name instead.
678 (Why not just lock MNTTAB directly? We don't know.) */
680 # define MNTTAB_LOCK "/etc/.mnttab.lock"
682 lockfd = open (MNTTAB_LOCK, O_RDONLY);
686 flock.l_type = F_RDLCK;
687 flock.l_whence = SEEK_SET;
690 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
693 int saved_errno = errno;
699 else if (errno != ENOENT)
704 fp = fopen (table, "r");
709 while ((ret = getmntent (fp, &mnt)) == 0)
711 me = xmalloc (sizeof *me);
712 me->me_devname = xstrdup (mnt.mnt_special);
713 me->me_mountdir = xstrdup (mnt.mnt_mountp);
714 me->me_type = xstrdup (mnt.mnt_fstype);
715 me->me_type_malloced = 1;
716 me->me_dummy = MNT_IGNORE (&mnt) != 0;
717 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
718 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
720 /* Add to the linked list. */
722 mtail = &me->me_next;
725 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
728 if (0 <= lockfd && close (lockfd) != 0)
737 #endif /* MOUNTED_GETMNTENT2. */
739 #ifdef MOUNTED_VMOUNT /* AIX. */
742 char *entries, *thisent;
747 /* Ask how many bytes to allocate for the mounted filesystem info. */
748 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
750 entries = xmalloc (bufsize);
752 /* Get the list of mounted filesystems. */
753 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
756 int saved_errno = errno;
762 for (i = 0, thisent = entries;
764 i++, thisent += vmp->vmt_length)
766 char *options, *ignore;
768 vmp = (struct vmount *) thisent;
769 me = xmalloc (sizeof *me);
770 if (vmp->vmt_flags & MNT_REMOTE)
775 /* Prepend the remote pathname. */
776 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
777 path = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
778 me->me_devname = xmalloc (strlen (host) + strlen (path) + 2);
779 strcpy (me->me_devname, host);
780 strcat (me->me_devname, ":");
781 strcat (me->me_devname, path);
786 me->me_devname = xstrdup (thisent +
787 vmp->vmt_data[VMT_OBJECT].vmt_off);
789 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
790 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
791 me->me_type_malloced = 1;
792 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
793 ignore = strstr (options, "ignore");
794 me->me_dummy = (ignore
795 && (ignore == options || ignore[-1] == ',')
796 && (ignore[sizeof "ignore" - 1] == ','
797 || ignore[sizeof "ignore" - 1] == '\0'));
798 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
800 /* Add to the linked list. */
802 mtail = &me->me_next;
806 #endif /* MOUNTED_VMOUNT. */
814 int saved_errno = errno;
819 me = mount_list->me_next;
820 free (mount_list->me_devname);
821 free (mount_list->me_mountdir);
822 if (mount_list->me_type_malloced)
823 free (mount_list->me_type);