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, 2007 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 3 of the License, or
9 (at your option) any later version.
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, see <http://www.gnu.org/licenses/>. */
21 #include "mountlist.h"
37 # include <sys/param.h>
40 #if defined MOUNTED_GETFSSTAT /* OSF_1 and Darwin1.3.x */
42 # include <grp.h> /* needed on OSF V4.0 for definition of NGROUPS,
43 NGROUPS is used as an array dimension in ucred.h */
44 # include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
47 # include <sys/mount.h>
49 # if HAVE_SYS_FS_TYPES_H
50 # include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
52 # if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
53 # define FS_TYPE(Ent) ((Ent).f_fstypename)
55 # define FS_TYPE(Ent) mnt_names[(Ent).f_type]
57 #endif /* MOUNTED_GETFSSTAT */
59 #ifdef MOUNTED_GETMNTENT1 /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
62 # if defined _PATH_MOUNTED /* GNU libc */
63 # define MOUNTED _PATH_MOUNTED
65 # if defined MNT_MNTTAB /* HP-UX. */
66 # define MOUNTED MNT_MNTTAB
68 # if defined MNTTABNAME /* Dynix. */
69 # define MOUNTED MNTTABNAME
74 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
75 # include <sys/mount.h>
78 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
79 # include <sys/statvfs.h>
82 #ifdef MOUNTED_GETMNT /* Ultrix. */
83 # include <sys/mount.h>
84 # include <sys/fs_types.h>
87 #ifdef MOUNTED_FS_STAT_DEV /* BeOS. */
92 #ifdef MOUNTED_FREAD /* SVR2. */
96 #ifdef MOUNTED_FREAD_FSTYP /* SVR3. */
98 # include <sys/fstyp.h>
99 # include <sys/statfs.h>
102 #ifdef MOUNTED_LISTMNTENT
106 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
107 # include <sys/mnttab.h>
110 #ifdef MOUNTED_VMOUNT /* AIX. */
112 # include <sys/vfs.h>
116 /* So special that it's not worth putting this in autoconf. */
117 # undef MOUNTED_FREAD_FSTYP
118 # define MOUNTED_GETMNTTBL
121 #if HAVE_SYS_MNTENT_H
122 /* This is to get MNTOPT_IGNORE on e.g. SVR4. */
123 # include <sys/mntent.h>
127 #if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
128 # define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
130 # define MNT_IGNORE(M) 0
134 # include "unlocked-io.h"
138 # define SIZE_MAX ((size_t) -1)
141 /* The results of open() in this file are not used with fchdir,
142 therefore save some unnecessary work in fchdir.c. */
146 /* The results of opendir() in this file are not used with dirfd and fchdir,
147 therefore save some unnecessary work in fchdir.c. */
152 # define ME_DUMMY(Fs_name, Fs_type) \
153 (strcmp (Fs_type, "autofs") == 0 \
154 || strcmp (Fs_type, "none") == 0 \
155 || strcmp (Fs_type, "proc") == 0 \
156 || strcmp (Fs_type, "subfs") == 0 \
157 /* for NetBSD 3.0 */ \
158 || strcmp (Fs_type, "kernfs") == 0 \
160 || strcmp (Fs_type, "ignore") == 0)
164 /* A file system is `remote' if its Fs_name contains a `:'
165 or if (it is of type (smbfs or cifs) and its Fs_name starts with `//'). */
166 # define ME_REMOTE(Fs_name, Fs_type) \
167 (strchr (Fs_name, ':') != NULL \
168 || ((Fs_name)[0] == '/' \
169 && (Fs_name)[1] == '/' \
170 && (strcmp (Fs_type, "smbfs") == 0 \
171 || strcmp (Fs_type, "cifs") == 0)))
174 #if MOUNTED_GETMNTINFO
176 # if ! HAVE_STRUCT_STATFS_F_FSTYPENAME
178 fstype_to_string (short int t
)
273 fsp_to_string (const struct statfs
*fsp
)
275 # if HAVE_STRUCT_STATFS_F_FSTYPENAME
276 return (char *) (fsp
->f_fstypename
);
278 return fstype_to_string (fsp
->f_type
);
282 #endif /* MOUNTED_GETMNTINFO */
284 #ifdef MOUNTED_VMOUNT /* AIX. */
286 fstype_to_string (int t
)
290 e
= getvfsbytype (t
);
291 if (!e
|| !e
->vfsent_name
)
294 return e
->vfsent_name
;
296 #endif /* MOUNTED_VMOUNT */
299 #if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
301 /* Return the device number from MOUNT_OPTIONS, if possible.
302 Otherwise return (dev_t) -1. */
305 dev_from_mount_options (char const *mount_options
)
307 /* GNU/Linux allows file system implementations to define their own
308 meaning for "dev=" mount options, so don't trust the meaning
312 static char const dev_pattern
[] = ",dev=";
313 char const *devopt
= strstr (mount_options
, dev_pattern
);
317 char const *optval
= devopt
+ sizeof dev_pattern
- 1;
319 unsigned long int dev
;
321 dev
= strtoul (optval
, &optvalend
, 16);
322 if (optval
!= optvalend
323 && (*optvalend
== '\0' || *optvalend
== ',')
324 && ! (dev
== ULONG_MAX
&& errno
== ERANGE
)
325 && dev
== (dev_t
) dev
)
336 /* Return a list of the currently mounted file systems, or NULL on error.
337 Add each entry to the tail of the list so that they stay in order.
338 If NEED_FS_TYPE is true, ensure that the file system type fields in
339 the returned list are valid. Otherwise, they might not be. */
342 read_file_system_list (bool need_fs_type
)
344 struct mount_entry
*mount_list
;
345 struct mount_entry
*me
;
346 struct mount_entry
**mtail
= &mount_list
;
348 #ifdef MOUNTED_LISTMNTENT
350 struct tabmntent
*mntlist
, *p
;
352 struct mount_entry
*me
;
354 /* the third and fourth arguments could be used to filter mounts,
355 but Crays doesn't seem to have any mounts that we want to
356 remove. Specifically, automount create normal NFS mounts.
359 if (listmntent (&mntlist
, KMTAB
, NULL
, NULL
) < 0)
361 for (p
= mntlist
; p
; p
= p
->next
) {
363 me
= xmalloc (sizeof *me
);
364 me
->me_devname
= xstrdup (mnt
->mnt_fsname
);
365 me
->me_mountdir
= xstrdup (mnt
->mnt_dir
);
366 me
->me_type
= xstrdup (mnt
->mnt_type
);
367 me
->me_type_malloced
= 1;
368 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
369 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
372 mtail
= &me
->me_next
;
374 freemntlist (mntlist
);
378 #ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
381 char *table
= MOUNTED
;
384 fp
= setmntent (table
, "r");
388 while ((mnt
= getmntent (fp
)))
390 me
= xmalloc (sizeof *me
);
391 me
->me_devname
= xstrdup (mnt
->mnt_fsname
);
392 me
->me_mountdir
= xstrdup (mnt
->mnt_dir
);
393 me
->me_type
= xstrdup (mnt
->mnt_type
);
394 me
->me_type_malloced
= 1;
395 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
396 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
397 me
->me_dev
= dev_from_mount_options (mnt
->mnt_opts
);
399 /* Add to the linked list. */
401 mtail
= &me
->me_next
;
404 if (endmntent (fp
) == 0)
407 #endif /* MOUNTED_GETMNTENT1. */
409 #ifdef MOUNTED_GETMNTINFO /* 4.4BSD. */
414 entries
= getmntinfo (&fsp
, MNT_NOWAIT
);
417 for (; entries
-- > 0; fsp
++)
419 char *fs_type
= fsp_to_string (fsp
);
421 me
= xmalloc (sizeof *me
);
422 me
->me_devname
= xstrdup (fsp
->f_mntfromname
);
423 me
->me_mountdir
= xstrdup (fsp
->f_mntonname
);
424 me
->me_type
= fs_type
;
425 me
->me_type_malloced
= 0;
426 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
427 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
428 me
->me_dev
= (dev_t
) -1; /* Magic; means not known yet. */
430 /* Add to the linked list. */
432 mtail
= &me
->me_next
;
435 #endif /* MOUNTED_GETMNTINFO */
437 #ifdef MOUNTED_GETMNTINFO2 /* NetBSD 3.0. */
442 entries
= getmntinfo (&fsp
, MNT_NOWAIT
);
445 for (; entries
-- > 0; fsp
++)
447 me
= xmalloc (sizeof *me
);
448 me
->me_devname
= xstrdup (fsp
->f_mntfromname
);
449 me
->me_mountdir
= xstrdup (fsp
->f_mntonname
);
450 me
->me_type
= xstrdup (fsp
->f_fstypename
);
451 me
->me_type_malloced
= 1;
452 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
453 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
454 me
->me_dev
= (dev_t
) -1; /* Magic; means not known yet. */
456 /* Add to the linked list. */
458 mtail
= &me
->me_next
;
461 #endif /* MOUNTED_GETMNTINFO2 */
463 #ifdef MOUNTED_GETMNT /* Ultrix. */
470 0 < (val
= getmnt (&offset
, &fsd
, sizeof (fsd
), NOSTAT_MANY
,
473 me
= xmalloc (sizeof *me
);
474 me
->me_devname
= xstrdup (fsd
.fd_req
.devname
);
475 me
->me_mountdir
= xstrdup (fsd
.fd_req
.path
);
476 me
->me_type
= gt_names
[fsd
.fd_req
.fstype
];
477 me
->me_type_malloced
= 0;
478 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
479 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
480 me
->me_dev
= fsd
.fd_req
.dev
;
482 /* Add to the linked list. */
484 mtail
= &me
->me_next
;
489 #endif /* MOUNTED_GETMNT. */
491 #if defined MOUNTED_FS_STAT_DEV /* BeOS */
493 /* The next_dev() and fs_stat_dev() system calls give the list of
494 all file systems, including the information returned by statvfs()
495 (fs type, total blocks, free blocks etc.), but without the mount
496 point. But on BeOS all file systems except / are mounted in the
497 rootfs, directly under /.
498 The directory name of the mount point is often, but not always,
499 identical to the volume name of the device.
500 We therefore get the list of subdirectories of /, and the list
501 of all file systems, and match the two lists. */
509 struct rootdir_entry
*next
;
511 struct rootdir_entry
*rootdir_list
;
512 struct rootdir_entry
**rootdir_tail
;
517 /* All volumes are mounted in the rootfs, directly under /. */
519 rootdir_tail
= &rootdir_list
;
520 dirp
= opendir ("/");
525 while ((d
= readdir (dirp
)) != NULL
)
530 if (strcmp (d
->d_name
, "..") == 0)
533 if (strcmp (d
->d_name
, ".") == 0)
534 name
= xstrdup ("/");
537 name
= xmalloc (1 + strlen (d
->d_name
) + 1);
539 strcpy (name
+ 1, d
->d_name
);
542 if (lstat (name
, &statbuf
) >= 0 && S_ISDIR (statbuf
.st_mode
))
544 struct rootdir_entry
*re
= xmalloc (sizeof *re
);
546 re
->dev
= statbuf
.st_dev
;
547 re
->ino
= statbuf
.st_ino
;
549 /* Add to the linked list. */
551 rootdir_tail
= &re
->next
;
558 *rootdir_tail
= NULL
;
560 for (pos
= 0; (dev
= next_dev (&pos
)) >= 0; )
561 if (fs_stat_dev (dev
, &fi
) >= 0)
563 /* Note: fi.dev == dev. */
564 struct rootdir_entry
*re
;
566 for (re
= rootdir_list
; re
; re
= re
->next
)
567 if (re
->dev
== fi
.dev
&& re
->ino
== fi
.root
)
570 me
= xmalloc (sizeof *me
);
571 me
->me_devname
= xstrdup (fi
.device_name
[0] != '\0' ? fi
.device_name
: fi
.fsh_name
);
572 me
->me_mountdir
= xstrdup (re
!= NULL
? re
->name
: fi
.fsh_name
);
573 me
->me_type
= xstrdup (fi
.fsh_name
);
574 me
->me_type_malloced
= 1;
577 me
->me_remote
= (fi
.flags
& B_FS_IS_SHARED
) != 0;
579 /* Add to the linked list. */
581 mtail
= &me
->me_next
;
585 while (rootdir_list
!= NULL
)
587 struct rootdir_entry
*re
= rootdir_list
;
588 rootdir_list
= re
->next
;
593 #endif /* MOUNTED_FS_STAT_DEV */
595 #if defined MOUNTED_GETFSSTAT /* __alpha running OSF_1 */
599 struct statfs
*stats
;
601 numsys
= getfsstat ((struct statfs
*)0, 0L, MNT_NOWAIT
);
604 if (SIZE_MAX
/ sizeof *stats
<= numsys
)
607 bufsize
= (1 + numsys
) * sizeof *stats
;
608 stats
= xmalloc (bufsize
);
609 numsys
= getfsstat (stats
, bufsize
, MNT_NOWAIT
);
617 for (counter
= 0; counter
< numsys
; counter
++)
619 me
= xmalloc (sizeof *me
);
620 me
->me_devname
= xstrdup (stats
[counter
].f_mntfromname
);
621 me
->me_mountdir
= xstrdup (stats
[counter
].f_mntonname
);
622 me
->me_type
= xstrdup (FS_TYPE (stats
[counter
]));
623 me
->me_type_malloced
= 1;
624 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
625 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
626 me
->me_dev
= (dev_t
) -1; /* Magic; means not known yet. */
628 /* Add to the linked list. */
630 mtail
= &me
->me_next
;
635 #endif /* MOUNTED_GETFSSTAT */
637 #if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23]. */
640 char *table
= "/etc/mnttab";
643 fp
= fopen (table
, "r");
647 while (fread (&mnt
, sizeof mnt
, 1, fp
) > 0)
649 me
= xmalloc (sizeof *me
);
650 # ifdef GETFSTYP /* SVR3. */
651 me
->me_devname
= xstrdup (mnt
.mt_dev
);
653 me
->me_devname
= xmalloc (strlen (mnt
.mt_dev
) + 6);
654 strcpy (me
->me_devname
, "/dev/");
655 strcpy (me
->me_devname
+ 5, mnt
.mt_dev
);
657 me
->me_mountdir
= xstrdup (mnt
.mt_filsys
);
658 me
->me_dev
= (dev_t
) -1; /* Magic; means not known yet. */
660 me
->me_type_malloced
= 0;
661 # ifdef GETFSTYP /* SVR3. */
665 char typebuf
[FSTYPSZ
];
667 if (statfs (me
->me_mountdir
, &fsd
, sizeof fsd
, 0) != -1
668 && sysfs (GETFSTYP
, fsd
.f_fstyp
, typebuf
) != -1)
670 me
->me_type
= xstrdup (typebuf
);
671 me
->me_type_malloced
= 1;
675 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
676 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
678 /* Add to the linked list. */
680 mtail
= &me
->me_next
;
685 /* The last fread() call must have failed. */
686 int saved_errno
= errno
;
692 if (fclose (fp
) == EOF
)
695 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP. */
697 #ifdef MOUNTED_GETMNTTBL /* DolphinOS goes its own way. */
699 struct mntent
**mnttbl
= getmnttbl (), **ent
;
700 for (ent
=mnttbl
;*ent
;ent
++)
702 me
= xmalloc (sizeof *me
);
703 me
->me_devname
= xstrdup ( (*ent
)->mt_resource
);
704 me
->me_mountdir
= xstrdup ( (*ent
)->mt_directory
);
705 me
->me_type
= xstrdup ((*ent
)->mt_fstype
);
706 me
->me_type_malloced
= 1;
707 me
->me_dummy
= ME_DUMMY (me
->me_devname
, me
->me_type
);
708 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
709 me
->me_dev
= (dev_t
) -1; /* Magic; means not known yet. */
711 /* Add to the linked list. */
713 mtail
= &me
->me_next
;
719 #ifdef MOUNTED_GETMNTENT2 /* SVR4. */
722 char *table
= MNTTAB
;
727 # if defined F_RDLCK && defined F_SETLKW
728 /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
729 e.g. Solaris 2.6. If the SVR4 folks ever define a macro
730 for this file name, we should use their macro name instead.
731 (Why not just lock MNTTAB directly? We don't know.) */
733 # define MNTTAB_LOCK "/etc/.mnttab.lock"
735 lockfd
= open (MNTTAB_LOCK
, O_RDONLY
);
739 flock
.l_type
= F_RDLCK
;
740 flock
.l_whence
= SEEK_SET
;
743 while (fcntl (lockfd
, F_SETLKW
, &flock
) == -1)
746 int saved_errno
= errno
;
752 else if (errno
!= ENOENT
)
757 fp
= fopen (table
, "r");
762 while ((ret
= getmntent (fp
, &mnt
)) == 0)
764 me
= xmalloc (sizeof *me
);
765 me
->me_devname
= xstrdup (mnt
.mnt_special
);
766 me
->me_mountdir
= xstrdup (mnt
.mnt_mountp
);
767 me
->me_type
= xstrdup (mnt
.mnt_fstype
);
768 me
->me_type_malloced
= 1;
769 me
->me_dummy
= MNT_IGNORE (&mnt
) != 0;
770 me
->me_remote
= ME_REMOTE (me
->me_devname
, me
->me_type
);
771 me
->me_dev
= dev_from_mount_options (mnt
.mnt_mntopts
);
773 /* Add to the linked list. */
775 mtail
= &me
->me_next
;
778 ret
= fclose (fp
) == EOF
? errno
: 0 < ret
? 0 : -1;
781 if (0 <= lockfd
&& close (lockfd
) != 0)
790 #endif /* MOUNTED_GETMNTENT2. */
792 #ifdef MOUNTED_VMOUNT /* AIX. */
795 char *entries
, *thisent
;
800 /* Ask how many bytes to allocate for the mounted file system info. */
801 if (mntctl (MCTL_QUERY
, sizeof bufsize
, (struct vmount
*) &bufsize
) != 0)
803 entries
= xmalloc (bufsize
);
805 /* Get the list of mounted file systems. */
806 n_entries
= mntctl (MCTL_QUERY
, bufsize
, (struct vmount
*) entries
);
809 int saved_errno
= errno
;
815 for (i
= 0, thisent
= entries
;
817 i
++, thisent
+= vmp
->vmt_length
)
819 char *options
, *ignore
;
821 vmp
= (struct vmount
*) thisent
;
822 me
= xmalloc (sizeof *me
);
823 if (vmp
->vmt_flags
& MNT_REMOTE
)
828 /* Prepend the remote dirname. */
829 host
= thisent
+ vmp
->vmt_data
[VMT_HOSTNAME
].vmt_off
;
830 dir
= thisent
+ vmp
->vmt_data
[VMT_OBJECT
].vmt_off
;
831 me
->me_devname
= xmalloc (strlen (host
) + strlen (dir
) + 2);
832 strcpy (me
->me_devname
, host
);
833 strcat (me
->me_devname
, ":");
834 strcat (me
->me_devname
, dir
);
839 me
->me_devname
= xstrdup (thisent
+
840 vmp
->vmt_data
[VMT_OBJECT
].vmt_off
);
842 me
->me_mountdir
= xstrdup (thisent
+ vmp
->vmt_data
[VMT_STUB
].vmt_off
);
843 me
->me_type
= xstrdup (fstype_to_string (vmp
->vmt_gfstype
));
844 me
->me_type_malloced
= 1;
845 options
= thisent
+ vmp
->vmt_data
[VMT_ARGS
].vmt_off
;
846 ignore
= strstr (options
, "ignore");
847 me
->me_dummy
= (ignore
848 && (ignore
== options
|| ignore
[-1] == ',')
849 && (ignore
[sizeof "ignore" - 1] == ','
850 || ignore
[sizeof "ignore" - 1] == '\0'));
851 me
->me_dev
= (dev_t
) -1; /* vmt_fsid might be the info we want. */
853 /* Add to the linked list. */
855 mtail
= &me
->me_next
;
859 #endif /* MOUNTED_VMOUNT. */
867 int saved_errno
= errno
;
872 me
= mount_list
->me_next
;
873 free (mount_list
->me_devname
);
874 free (mount_list
->me_mountdir
);
875 if (mount_list
->me_type_malloced
)
876 free (mount_list
->me_type
);