1 /* mountlist.c -- return a list of mounted filesystems
2 Copyright (C) 1991, 1992, 1997-2003 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>
47 # include <sys/param.h>
50 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
52 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
55 # include <sys/mount.h>
57 # if HAVE_SYS_FS_TYPES_H
58 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
60 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
61 # define FS_TYPE(Ent) ((Ent).f_fstypename)
63 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
65 #endif /* MOUNTED_GETFSSTAT */
67 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
70 # if defined _PATH_MOUNTED /* GNU libc */
71 # define MOUNTED _PATH_MOUNTED
73 # if defined MNT_MNTTAB /* HP-UX. */
74 # define MOUNTED MNT_MNTTAB
76 # if defined MNTTABNAME /* Dynix. */
77 # define MOUNTED MNTTABNAME
82 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
83 # include <sys/mount.h>
86 #ifdef MOUNTED_GETMNT /* Ultrix. */
87 # include <sys/mount.h>
88 # include <sys/fs_types.h>
91 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
96 #ifdef MOUNTED_FREAD /* SVR2. */
100 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
102 # include <sys/fstyp.h>
103 # include <sys/statfs.h>
106 #ifdef MOUNTED_LISTMNTENT
110 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
111 # include <sys/mnttab.h>
114 #ifdef MOUNTED_VMOUNT /* AIX. */
116 # include <sys/vfs.h>
120 /* So special that it's not worth putting this in autoconf. */
121 # undef MOUNTED_FREAD_FSTYP
122 # define MOUNTED_GETMNTTBL
125 #if HAVE_SYS_MNTENT_H
126 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
127 # include <sys/mntent.h>
131 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
132 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
134 # define MNT_IGNORE(M) 0
137 #include "mountlist.h"
138 #include "unlocked-io.h"
140 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
141 /* Return the value of the hexadecimal number represented by CP.
142 No prefix (like '0x') or suffix (like 'h') is expected to be
144 /* FIXME: this can overflow */
154 if (*cp >= 'a' && *cp <= 'f')
155 val = val * 16 + *cp - 'a' + 10;
156 else if (*cp >= 'A' && *cp <= 'F')
157 val = val * 16 + *cp - 'A' + 10;
158 else if (*cp >= '0' && *cp <= '9')
159 val = val * 16 + *cp - '0';
166 #endif /* MOUNTED_GETMNTENT1. */
168 #if MOUNTED_GETMNTINFO
170 # if ! HAVE_F_FSTYPENAME_IN_STATFS
172 fstype_to_string (short t)
264 # endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */
266 /* __NetBSD__ || BSD_NET2 || __OpenBSD__ */
268 fsp_to_string (const struct statfs *fsp)
270 # if defined HAVE_F_FSTYPENAME_IN_STATFS
271 return (char *) (fsp->f_fstypename);
273 return fstype_to_string (fsp->f_type);
277 #endif /* MOUNTED_GETMNTINFO */
279 #ifdef MOUNTED_VMOUNT /* AIX. */
281 fstype_to_string (int t)
285 e = getvfsbytype (t);
286 if (!e || !e->vfsent_name)
289 return e->vfsent_name;
291 #endif /* MOUNTED_VMOUNT */
293 /* Return a list of the currently mounted filesystems, or NULL on error.
294 Add each entry to the tail of the list so that they stay in order.
295 If NEED_FS_TYPE is nonzero, ensure that the filesystem type fields in
296 the returned list are valid. Otherwise, they might not be. */
299 read_filesystem_list (int need_fs_type)
301 struct mount_entry *mount_list;
302 struct mount_entry *me;
303 struct mount_entry **mtail = &mount_list;
305 #ifdef MOUNTED_LISTMNTENT
307 struct tabmntent *mntlist, *p;
309 struct mount_entry *me;
311 /* the third and fourth arguments could be used to filter mounts,
312 but Crays doesn't seem to have any mounts that we want to
313 remove. Specifically, automount create normal NFS mounts.
316 if (listmntent (&mntlist, KMTAB, NULL, NULL) < 0)
318 for (p = mntlist; p; p = p->next) {
320 me = xmalloc (sizeof (struct mount_entry));
321 me->me_devname = xstrdup (mnt->mnt_fsname);
322 me->me_mountdir = xstrdup (mnt->mnt_dir);
323 me->me_type = xstrdup (mnt->mnt_type);
324 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
325 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
328 mtail = &me->me_next;
330 freemntlist (mntlist);
334 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
337 char *table = MOUNTED;
341 fp = setmntent (table, "r");
345 while ((mnt = getmntent (fp)))
347 me = xmalloc (sizeof (struct mount_entry));
348 me->me_devname = xstrdup (mnt->mnt_fsname);
349 me->me_mountdir = xstrdup (mnt->mnt_dir);
350 me->me_type = xstrdup (mnt->mnt_type);
351 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
352 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
353 devopt = strstr (mnt->mnt_opts, "dev=");
356 if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
357 me->me_dev = xatoi (devopt + 6);
359 me->me_dev = xatoi (devopt + 4);
362 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
364 /* Add to the linked list. */
366 mtail = &me->me_next;
369 if (endmntent (fp) == 0)
372 #endif /* MOUNTED_GETMNTENT1. */
374 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
379 entries = getmntinfo (&fsp, MNT_NOWAIT);
382 for (; entries-- > 0; fsp++)
384 char *fs_type = fsp_to_string (fsp);
386 me = xmalloc (sizeof (struct mount_entry));
387 me->me_devname = xstrdup (fsp->f_mntfromname);
388 me->me_mountdir = xstrdup (fsp->f_mntonname);
389 me->me_type = fs_type;
390 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
391 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
392 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
394 /* Add to the linked list. */
396 mtail = &me->me_next;
399 #endif /* MOUNTED_GETMNTINFO */
401 #ifdef MOUNTED_GETMNT /* Ultrix. */
408 0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
411 me = xmalloc (sizeof (struct mount_entry));
412 me->me_devname = xstrdup (fsd.fd_req.devname);
413 me->me_mountdir = xstrdup (fsd.fd_req.path);
414 me->me_type = gt_names[fsd.fd_req.fstype];
415 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
416 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
417 me->me_dev = fsd.fd_req.dev;
419 /* Add to the linked list. */
421 mtail = &me->me_next;
426 #endif /* MOUNTED_GETMNT. */
428 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
430 /* The next_dev() and fs_stat_dev() system calls give the list of
431 all filesystems, including the information returned by statvfs()
432 (fs type, total blocks, free blocks etc.), but without the mount
433 point. But on BeOS all filesystems except / are mounted in the
434 rootfs, directly under /.
435 The directory name of the mount point is often, but not always,
436 identical to the volume name of the device.
437 We therefore get the list of subdirectories of /, and the list
438 of all filesystems, and match the two lists. */
446 struct rootdir_entry *next;
448 struct rootdir_entry *rootdir_list;
449 struct rootdir_entry **rootdir_tail;
454 /* All volumes are mounted in the rootfs, directly under /. */
456 rootdir_tail = &rootdir_list;
457 dirp = opendir ("/");
462 while ((d = readdir (dirp)) != NULL)
467 if (strcmp (d->d_name, "..") == 0)
470 if (strcmp (d->d_name, ".") == 0)
471 name = xstrdup ("/");
474 name = xmalloc (1 + strlen (d->d_name) + 1);
476 strcpy (name + 1, d->d_name);
479 if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
481 struct rootdir_entry *re;
483 re = xmalloc (sizeof (struct rootdir_entry));
485 re->dev = statbuf.st_dev;
486 re->ino = statbuf.st_ino;
488 /* Add to the linked list. */
490 rootdir_tail = &re->next;
497 *rootdir_tail = NULL;
499 for (pos = 0; (dev = next_dev (&pos)) >= 0; )
500 if (fs_stat_dev (dev, &fi) >= 0)
502 /* Note: fi.dev == dev. */
503 struct rootdir_entry *re;
505 for (re = rootdir_list; re; re = re->next)
506 if (re->dev == fi.dev && re->ino == fi.root)
509 me = xmalloc (sizeof (struct mount_entry));
510 me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
511 me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
512 me->me_type = xstrdup (fi.fsh_name);
515 me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;
517 /* Add to the linked list. */
519 mtail = &me->me_next;
523 while (rootdir_list != NULL)
525 struct rootdir_entry *re = rootdir_list;
526 rootdir_list = re->next;
531 #endif /* MOUNTED_FS_STAT_DEV */
533 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
535 int numsys, counter, bufsize;
536 struct statfs *stats;
538 numsys = getfsstat ((struct statfs *)0, 0L, MNT_NOWAIT);
542 bufsize = (1 + numsys) * sizeof (struct statfs);
543 stats = xmalloc (bufsize);
544 numsys = getfsstat (stats, bufsize, MNT_NOWAIT);
552 for (counter = 0; counter < numsys; counter++)
554 me = xmalloc (sizeof (struct mount_entry));
555 me->me_devname = xstrdup (stats[counter].f_mntfromname);
556 me->me_mountdir = xstrdup (stats[counter].f_mntonname);
557 me->me_type = xstrdup (FS_TYPE (stats[counter]));
558 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
559 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
560 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
562 /* Add to the linked list. */
564 mtail = &me->me_next;
569 #endif /* MOUNTED_GETFSSTAT */
571 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
574 char *table = "/etc/mnttab";
577 fp = fopen (table, "r");
581 while (fread (&mnt, sizeof mnt, 1, fp) > 0)
583 me = xmalloc (sizeof (struct mount_entry));
584 # ifdef GETFSTYP /* SVR3. */
585 me->me_devname = xstrdup (mnt.mt_dev);
587 me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
588 strcpy (me->me_devname, "/dev/");
589 strcpy (me->me_devname + 5, mnt.mt_dev);
591 me->me_mountdir = xstrdup (mnt.mt_filsys);
592 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
594 # ifdef GETFSTYP /* SVR3. */
598 char typebuf[FSTYPSZ];
600 if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
601 && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
602 me->me_type = xstrdup (typebuf);
605 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
606 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
608 /* Add to the linked list. */
610 mtail = &me->me_next;
615 /* The last fread() call must have failed. */
616 int saved_errno = errno;
622 if (fclose (fp) == EOF)
625 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
627 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes it's own way */
629 struct mntent **mnttbl = getmnttbl (), **ent;
630 for (ent=mnttbl;*ent;ent++)
632 me = xmalloc (sizeof (struct mount_entry));
633 me->me_devname = xstrdup ( (*ent)->mt_resource);
634 me->me_mountdir = xstrdup ( (*ent)->mt_directory);
635 me->me_type = xstrdup ((*ent)->mt_fstype);
636 me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
637 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
638 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
640 /* Add to the linked list. */
642 mtail = &me->me_next;
648 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
651 char *table = MNTTAB;
656 # if defined F_RDLCK && defined F_SETLKW
657 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
658 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
659 for this file name, we should use their macro name instead.
660 (Why not just lock MNTTAB directly? We don't know.) */
662 # define MNTTAB_LOCK "/etc/.mnttab.lock"
664 lockfd = open (MNTTAB_LOCK, O_RDONLY);
668 flock.l_type = F_RDLCK;
669 flock.l_whence = SEEK_SET;
672 while (fcntl (lockfd, F_SETLKW, &flock) == -1)
675 int saved_errno = errno;
681 else if (errno != ENOENT)
686 fp = fopen (table, "r");
691 while ((ret = getmntent (fp, &mnt)) == 0)
693 me = xmalloc (sizeof (struct mount_entry));
694 me->me_devname = xstrdup (mnt.mnt_special);
695 me->me_mountdir = xstrdup (mnt.mnt_mountp);
696 me->me_type = xstrdup (mnt.mnt_fstype);
697 me->me_dummy = MNT_IGNORE (&mnt) != 0;
698 me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
699 me->me_dev = (dev_t) -1; /* Magic; means not known yet. */
701 /* Add to the linked list. */
703 mtail = &me->me_next;
706 ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
709 if (0 <= lockfd && close (lockfd) != 0)
718 #endif /* MOUNTED_GETMNTENT2. */
720 #ifdef MOUNTED_VMOUNT /* AIX. */
723 char *entries, *thisent;
728 /* Ask how many bytes to allocate for the mounted filesystem info. */
729 if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
731 entries = xmalloc (bufsize);
733 /* Get the list of mounted filesystems. */
734 n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
737 int saved_errno = errno;
743 for (i = 0, thisent = entries;
745 i++, thisent += vmp->vmt_length)
747 char *options, *ignore;
749 vmp = (struct vmount *) thisent;
750 me = xmalloc (sizeof (struct mount_entry));
751 if (vmp->vmt_flags & MNT_REMOTE)
756 /* Prepend the remote pathname. */
757 host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
758 path = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
759 me->me_devname = xmalloc (strlen (host) + strlen (path) + 2);
760 strcpy (me->me_devname, host);
761 strcat (me->me_devname, ":");
762 strcat (me->me_devname, path);
767 me->me_devname = xstrdup (thisent +
768 vmp->vmt_data[VMT_OBJECT].vmt_off);
770 me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
771 me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
772 options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
773 ignore = strstr (options, "ignore");
774 me->me_dummy = (ignore
775 && (ignore == options || ignore[-1] == ',')
776 && (ignore[sizeof "ignore" - 1] == ','
777 || ignore[sizeof "ignore" - 1] == '\0'));
778 me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want. */
780 /* Add to the linked list. */
782 mtail = &me->me_next;
786 #endif /* MOUNTED_VMOUNT. */
794 int saved_errno = errno;
799 me = mount_list->me_next;
800 free (mount_list->me_devname);
801 free (mount_list->me_mountdir);
802 /* FIXME: me_type is not always malloced. */