diff options
author | Ram Pai <linuxram@us.ibm.com> | 2008-03-27 08:06:25 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-04-23 00:05:03 -0400 |
commit | 2d4d4864ac08caff5c204a752bd004eed4f08760 (patch) | |
tree | a8907c33afae589146fdcd06eacd740aff48c6a1 | |
parent | a1a2c409b666befc58c2db9c7fbddf200f153470 (diff) |
[patch 6/7] vfs: mountinfo: add /proc/<pid>/mountinfo
[mszeredi@suse.cz] rewrite and split big patch into managable chunks
/proc/mounts in its current form lacks important information:
- propagation state
- root of mount for bind mounts
- the st_dev value used within the filesystem
- identifier for each mount and it's parent
It also suffers from the following problems:
- not easily extendable
- ambiguity of mountpoints within a chrooted environment
- doesn't distinguish between filesystem dependent and independent options
- doesn't distinguish between per mount and per super block options
This patch introduces /proc/<pid>/mountinfo which attempts to address
all these deficiencies.
Code shared between /proc/<pid>/mounts and /proc/<pid>/mountinfo is
extracted into separate functions.
Thanks to Al Viro for the help in getting the design right.
Signed-off-by: Ram Pai <linuxram@us.ibm.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | Documentation/filesystems/proc.txt | 32 | ||||
-rw-r--r-- | fs/namespace.c | 119 | ||||
-rw-r--r-- | fs/proc/base.c | 15 | ||||
-rw-r--r-- | include/linux/mnt_namespace.h | 1 |
4 files changed, 144 insertions, 23 deletions
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 518ebe609e2b..2cd920f92e5e 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt | |||
@@ -43,6 +43,7 @@ Table of Contents | |||
43 | 2.13 /proc/<pid>/oom_score - Display current oom-killer score | 43 | 2.13 /proc/<pid>/oom_score - Display current oom-killer score |
44 | 2.14 /proc/<pid>/io - Display the IO accounting fields | 44 | 2.14 /proc/<pid>/io - Display the IO accounting fields |
45 | 2.15 /proc/<pid>/coredump_filter - Core dump filtering settings | 45 | 2.15 /proc/<pid>/coredump_filter - Core dump filtering settings |
46 | 2.16 /proc/<pid>/mountinfo - Information about mounts | ||
46 | 47 | ||
47 | ------------------------------------------------------------------------------ | 48 | ------------------------------------------------------------------------------ |
48 | Preface | 49 | Preface |
@@ -2348,4 +2349,35 @@ For example: | |||
2348 | $ echo 0x7 > /proc/self/coredump_filter | 2349 | $ echo 0x7 > /proc/self/coredump_filter |
2349 | $ ./some_program | 2350 | $ ./some_program |
2350 | 2351 | ||
2352 | 2.16 /proc/<pid>/mountinfo - Information about mounts | ||
2353 | -------------------------------------------------------- | ||
2354 | |||
2355 | This file contains lines of the form: | ||
2356 | |||
2357 | 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue | ||
2358 | (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) | ||
2359 | |||
2360 | (1) mount ID: unique identifier of the mount (may be reused after umount) | ||
2361 | (2) parent ID: ID of parent (or of self for the top of the mount tree) | ||
2362 | (3) major:minor: value of st_dev for files on filesystem | ||
2363 | (4) root: root of the mount within the filesystem | ||
2364 | (5) mount point: mount point relative to the process's root | ||
2365 | (6) mount options: per mount options | ||
2366 | (7) optional fields: zero or more fields of the form "tag[:value]" | ||
2367 | (8) separator: marks the end of the optional fields | ||
2368 | (9) filesystem type: name of filesystem of the form "type[.subtype]" | ||
2369 | (10) mount source: filesystem specific information or "none" | ||
2370 | (11) super options: per super block options | ||
2371 | |||
2372 | Parsers should ignore all unrecognised optional fields. Currently the | ||
2373 | possible optional fields are: | ||
2374 | |||
2375 | shared:X mount is shared in peer group X | ||
2376 | master:X mount is slave to peer group X | ||
2377 | unbindable mount is unbindable | ||
2378 | |||
2379 | For more information on mount propagation see: | ||
2380 | |||
2381 | Documentation/filesystems/sharedsubtree.txt | ||
2382 | |||
2351 | ------------------------------------------------------------------------------ | 2383 | ------------------------------------------------------------------------------ |
diff --git a/fs/namespace.c b/fs/namespace.c index dfdf51e81c1c..c807b8d5f891 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -746,20 +746,30 @@ static void m_stop(struct seq_file *m, void *v) | |||
746 | up_read(&namespace_sem); | 746 | up_read(&namespace_sem); |
747 | } | 747 | } |
748 | 748 | ||
749 | static int show_vfsmnt(struct seq_file *m, void *v) | 749 | struct proc_fs_info { |
750 | int flag; | ||
751 | const char *str; | ||
752 | }; | ||
753 | |||
754 | static void show_sb_opts(struct seq_file *m, struct super_block *sb) | ||
750 | { | 755 | { |
751 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 756 | static const struct proc_fs_info fs_info[] = { |
752 | int err = 0; | ||
753 | static struct proc_fs_info { | ||
754 | int flag; | ||
755 | char *str; | ||
756 | } fs_info[] = { | ||
757 | { MS_SYNCHRONOUS, ",sync" }, | 757 | { MS_SYNCHRONOUS, ",sync" }, |
758 | { MS_DIRSYNC, ",dirsync" }, | 758 | { MS_DIRSYNC, ",dirsync" }, |
759 | { MS_MANDLOCK, ",mand" }, | 759 | { MS_MANDLOCK, ",mand" }, |
760 | { 0, NULL } | 760 | { 0, NULL } |
761 | }; | 761 | }; |
762 | static struct proc_fs_info mnt_info[] = { | 762 | const struct proc_fs_info *fs_infop; |
763 | |||
764 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | ||
765 | if (sb->s_flags & fs_infop->flag) | ||
766 | seq_puts(m, fs_infop->str); | ||
767 | } | ||
768 | } | ||
769 | |||
770 | static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) | ||
771 | { | ||
772 | static const struct proc_fs_info mnt_info[] = { | ||
763 | { MNT_NOSUID, ",nosuid" }, | 773 | { MNT_NOSUID, ",nosuid" }, |
764 | { MNT_NODEV, ",nodev" }, | 774 | { MNT_NODEV, ",nodev" }, |
765 | { MNT_NOEXEC, ",noexec" }, | 775 | { MNT_NOEXEC, ",noexec" }, |
@@ -768,27 +778,37 @@ static int show_vfsmnt(struct seq_file *m, void *v) | |||
768 | { MNT_RELATIME, ",relatime" }, | 778 | { MNT_RELATIME, ",relatime" }, |
769 | { 0, NULL } | 779 | { 0, NULL } |
770 | }; | 780 | }; |
771 | struct proc_fs_info *fs_infop; | 781 | const struct proc_fs_info *fs_infop; |
782 | |||
783 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
784 | if (mnt->mnt_flags & fs_infop->flag) | ||
785 | seq_puts(m, fs_infop->str); | ||
786 | } | ||
787 | } | ||
788 | |||
789 | static void show_type(struct seq_file *m, struct super_block *sb) | ||
790 | { | ||
791 | mangle(m, sb->s_type->name); | ||
792 | if (sb->s_subtype && sb->s_subtype[0]) { | ||
793 | seq_putc(m, '.'); | ||
794 | mangle(m, sb->s_subtype); | ||
795 | } | ||
796 | } | ||
797 | |||
798 | static int show_vfsmnt(struct seq_file *m, void *v) | ||
799 | { | ||
800 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
801 | int err = 0; | ||
772 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | 802 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; |
773 | 803 | ||
774 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | 804 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); |
775 | seq_putc(m, ' '); | 805 | seq_putc(m, ' '); |
776 | seq_path(m, &mnt_path, " \t\n\\"); | 806 | seq_path(m, &mnt_path, " \t\n\\"); |
777 | seq_putc(m, ' '); | 807 | seq_putc(m, ' '); |
778 | mangle(m, mnt->mnt_sb->s_type->name); | 808 | show_type(m, mnt->mnt_sb); |
779 | if (mnt->mnt_sb->s_subtype && mnt->mnt_sb->s_subtype[0]) { | ||
780 | seq_putc(m, '.'); | ||
781 | mangle(m, mnt->mnt_sb->s_subtype); | ||
782 | } | ||
783 | seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); | 809 | seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); |
784 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | 810 | show_sb_opts(m, mnt->mnt_sb); |
785 | if (mnt->mnt_sb->s_flags & fs_infop->flag) | 811 | show_mnt_opts(m, mnt); |
786 | seq_puts(m, fs_infop->str); | ||
787 | } | ||
788 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
789 | if (mnt->mnt_flags & fs_infop->flag) | ||
790 | seq_puts(m, fs_infop->str); | ||
791 | } | ||
792 | if (mnt->mnt_sb->s_op->show_options) | 812 | if (mnt->mnt_sb->s_op->show_options) |
793 | err = mnt->mnt_sb->s_op->show_options(m, mnt); | 813 | err = mnt->mnt_sb->s_op->show_options(m, mnt); |
794 | seq_puts(m, " 0 0\n"); | 814 | seq_puts(m, " 0 0\n"); |
@@ -802,6 +822,59 @@ const struct seq_operations mounts_op = { | |||
802 | .show = show_vfsmnt | 822 | .show = show_vfsmnt |
803 | }; | 823 | }; |
804 | 824 | ||
825 | static int show_mountinfo(struct seq_file *m, void *v) | ||
826 | { | ||
827 | struct proc_mounts *p = m->private; | ||
828 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
829 | struct super_block *sb = mnt->mnt_sb; | ||
830 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | ||
831 | struct path root = p->root; | ||
832 | int err = 0; | ||
833 | |||
834 | seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id, | ||
835 | MAJOR(sb->s_dev), MINOR(sb->s_dev)); | ||
836 | seq_dentry(m, mnt->mnt_root, " \t\n\\"); | ||
837 | seq_putc(m, ' '); | ||
838 | seq_path_root(m, &mnt_path, &root, " \t\n\\"); | ||
839 | if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { | ||
840 | /* | ||
841 | * Mountpoint is outside root, discard that one. Ugly, | ||
842 | * but less so than trying to do that in iterator in a | ||
843 | * race-free way (due to renames). | ||
844 | */ | ||
845 | return SEQ_SKIP; | ||
846 | } | ||
847 | seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); | ||
848 | show_mnt_opts(m, mnt); | ||
849 | |||
850 | /* Tagged fields ("foo:X" or "bar") */ | ||
851 | if (IS_MNT_SHARED(mnt)) | ||
852 | seq_printf(m, " shared:%i", mnt->mnt_group_id); | ||
853 | if (IS_MNT_SLAVE(mnt)) | ||
854 | seq_printf(m, " master:%i", mnt->mnt_master->mnt_group_id); | ||
855 | if (IS_MNT_UNBINDABLE(mnt)) | ||
856 | seq_puts(m, " unbindable"); | ||
857 | |||
858 | /* Filesystem specific data */ | ||
859 | seq_puts(m, " - "); | ||
860 | show_type(m, sb); | ||
861 | seq_putc(m, ' '); | ||
862 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | ||
863 | seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw"); | ||
864 | show_sb_opts(m, sb); | ||
865 | if (sb->s_op->show_options) | ||
866 | err = sb->s_op->show_options(m, mnt); | ||
867 | seq_putc(m, '\n'); | ||
868 | return err; | ||
869 | } | ||
870 | |||
871 | const struct seq_operations mountinfo_op = { | ||
872 | .start = m_start, | ||
873 | .next = m_next, | ||
874 | .stop = m_stop, | ||
875 | .show = show_mountinfo, | ||
876 | }; | ||
877 | |||
805 | static int show_vfsstat(struct seq_file *m, void *v) | 878 | static int show_vfsstat(struct seq_file *m, void *v) |
806 | { | 879 | { |
807 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 880 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); |
@@ -822,7 +895,7 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
822 | 895 | ||
823 | /* file system type */ | 896 | /* file system type */ |
824 | seq_puts(m, "with fstype "); | 897 | seq_puts(m, "with fstype "); |
825 | mangle(m, mnt->mnt_sb->s_type->name); | 898 | show_type(m, mnt->mnt_sb); |
826 | 899 | ||
827 | /* optional statistics */ | 900 | /* optional statistics */ |
828 | if (mnt->mnt_sb->s_op->show_stats) { | 901 | if (mnt->mnt_sb->s_op->show_stats) { |
diff --git a/fs/proc/base.c b/fs/proc/base.c index a04b3db7a296..c5e412a00b17 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -604,6 +604,19 @@ static const struct file_operations proc_mounts_operations = { | |||
604 | .poll = mounts_poll, | 604 | .poll = mounts_poll, |
605 | }; | 605 | }; |
606 | 606 | ||
607 | static int mountinfo_open(struct inode *inode, struct file *file) | ||
608 | { | ||
609 | return mounts_open_common(inode, file, &mountinfo_op); | ||
610 | } | ||
611 | |||
612 | static const struct file_operations proc_mountinfo_operations = { | ||
613 | .open = mountinfo_open, | ||
614 | .read = seq_read, | ||
615 | .llseek = seq_lseek, | ||
616 | .release = mounts_release, | ||
617 | .poll = mounts_poll, | ||
618 | }; | ||
619 | |||
607 | static int mountstats_open(struct inode *inode, struct file *file) | 620 | static int mountstats_open(struct inode *inode, struct file *file) |
608 | { | 621 | { |
609 | return mounts_open_common(inode, file, &mountstats_op); | 622 | return mounts_open_common(inode, file, &mountstats_op); |
@@ -2303,6 +2316,7 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
2303 | LNK("root", root), | 2316 | LNK("root", root), |
2304 | LNK("exe", exe), | 2317 | LNK("exe", exe), |
2305 | REG("mounts", S_IRUGO, mounts), | 2318 | REG("mounts", S_IRUGO, mounts), |
2319 | REG("mountinfo", S_IRUGO, mountinfo), | ||
2306 | REG("mountstats", S_IRUSR, mountstats), | 2320 | REG("mountstats", S_IRUSR, mountstats), |
2307 | #ifdef CONFIG_PROC_PAGE_MONITOR | 2321 | #ifdef CONFIG_PROC_PAGE_MONITOR |
2308 | REG("clear_refs", S_IWUSR, clear_refs), | 2322 | REG("clear_refs", S_IWUSR, clear_refs), |
@@ -2635,6 +2649,7 @@ static const struct pid_entry tid_base_stuff[] = { | |||
2635 | LNK("root", root), | 2649 | LNK("root", root), |
2636 | LNK("exe", exe), | 2650 | LNK("exe", exe), |
2637 | REG("mounts", S_IRUGO, mounts), | 2651 | REG("mounts", S_IRUGO, mounts), |
2652 | REG("mountinfo", S_IRUGO, mountinfo), | ||
2638 | #ifdef CONFIG_PROC_PAGE_MONITOR | 2653 | #ifdef CONFIG_PROC_PAGE_MONITOR |
2639 | REG("clear_refs", S_IWUSR, clear_refs), | 2654 | REG("clear_refs", S_IWUSR, clear_refs), |
2640 | REG("smaps", S_IRUGO, smaps), | 2655 | REG("smaps", S_IRUGO, smaps), |
diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h index c078aacc8116..830bbcd449d6 100644 --- a/include/linux/mnt_namespace.h +++ b/include/linux/mnt_namespace.h | |||
@@ -46,6 +46,7 @@ static inline void get_mnt_ns(struct mnt_namespace *ns) | |||
46 | } | 46 | } |
47 | 47 | ||
48 | extern const struct seq_operations mounts_op; | 48 | extern const struct seq_operations mounts_op; |
49 | extern const struct seq_operations mountinfo_op; | ||
49 | extern const struct seq_operations mountstats_op; | 50 | extern const struct seq_operations mountstats_op; |
50 | 51 | ||
51 | #endif | 52 | #endif |