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 |
