Hide unreachable mount points in /proc/mounts and /proc/$PID/mountstats What's mounted on unreachable mount points isn't interesting to processes: they can't get there in the first place. This patch hides unreachable mounts from processes. Processes living in the root namespace whill still see all mounts they were seeing before except for the rootfs mount, which is never reachable from an "ordinary" process. Only the initial initrd init process will actually have access to the rootfs mount. For this process that mount *is* reachable, and so it will show in. This patch also removes some code duplication between mounts_open() and mountstats_open(). Signed-off-by: Andreas Gruenbacher Index: b/fs/namespace.c =================================================================== --- a/fs/namespace.c +++ b/fs/namespace.c @@ -348,8 +348,16 @@ static inline void mangle(struct seq_fil seq_escape(m, s, " \t\n\\"); } +/* Keep in sync with fs/proc/base.c! */ +struct proc_mounts { + struct seq_file m; + void *page; + int event; +}; + static int show_vfsmnt(struct seq_file *m, void *v) { + void *page = container_of(m, struct proc_mounts, m)->page; struct vfsmount *mnt = v; int err = 0; static struct proc_fs_info { @@ -372,9 +380,13 @@ static int show_vfsmnt(struct seq_file * }; struct proc_fs_info *fs_infop; + char *path = d_path(mnt->mnt_root, mnt, page, PAGE_SIZE); + if (IS_ERR(path) || *path != '/') + return err; + mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); seq_putc(m, ' '); - seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); + mangle(m, path); seq_putc(m, ' '); mangle(m, mnt->mnt_sb->s_type->name); seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw"); @@ -401,9 +413,14 @@ struct seq_operations mounts_op = { static int show_vfsstat(struct seq_file *m, void *v) { + void *page = container_of(m, struct proc_mounts, m)->page; struct vfsmount *mnt = v; int err = 0; + char *path = d_path(mnt->mnt_root, mnt, page, PAGE_SIZE); + if (IS_ERR(path) || *path != '/') + return err; /* error or path unreachable from chroot */ + /* device */ if (mnt->mnt_devname) { seq_puts(m, "device "); @@ -413,7 +430,7 @@ static int show_vfsstat(struct seq_file /* mount point */ seq_puts(m, " mounted on "); - seq_path(m, mnt, mnt->mnt_root, " \t\n\\"); + mangle(m, path); seq_putc(m, ' '); /* file system type */ Index: b/fs/proc/base.c =================================================================== --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -356,13 +356,16 @@ static const struct inode_operations pro .setattr = proc_setattr, }; +/* Keep in sync with fs/namespace.c! */ extern struct seq_operations mounts_op; struct proc_mounts { struct seq_file m; + void *page; int event; }; -static int mounts_open(struct inode *inode, struct file *file) +static int __mounts_open(struct inode *inode, struct file *file, + struct seq_operations *seq_ops) { struct task_struct *task = get_proc_task(inode); struct mnt_namespace *ns = NULL; @@ -385,12 +388,16 @@ static int mounts_open(struct inode *ino p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); if (p) { file->private_data = &p->m; - ret = seq_open(file, &mounts_op); + p->page = (void *)__get_free_page(GFP_KERNEL); + if (p->page) + ret = seq_open(file, seq_ops); if (!ret) { p->m.private = ns; p->event = ns->event; return 0; } + if (p->page) + free_page((unsigned long)p->page); kfree(p); } put_mnt_ns(ns); @@ -398,17 +405,25 @@ static int mounts_open(struct inode *ino return ret; } +static int mounts_open(struct inode *inode, struct file *file) +{ + return __mounts_open(inode, file, &mounts_op); +} + static int mounts_release(struct inode *inode, struct file *file) { - struct seq_file *m = file->private_data; - struct mnt_namespace *ns = m->private; + struct proc_mounts *p = + container_of(file->private_data, struct proc_mounts, m); + struct mnt_namespace *ns = p->m.private; + free_page((unsigned long)p->page); put_mnt_ns(ns); return seq_release(inode, file); } static unsigned mounts_poll(struct file *file, poll_table *wait) { - struct proc_mounts *p = file->private_data; + struct proc_mounts *p = + container_of(file->private_data, struct proc_mounts, m); struct mnt_namespace *ns = p->m.private; unsigned res = 0; @@ -435,31 +450,7 @@ static const struct file_operations proc extern struct seq_operations mountstats_op; static int mountstats_open(struct inode *inode, struct file *file) { - int ret = seq_open(file, &mountstats_op); - - if (!ret) { - struct seq_file *m = file->private_data; - struct mnt_namespace *mnt_ns = NULL; - struct task_struct *task = get_proc_task(inode); - - if (task) { - task_lock(task); - if (task->nsproxy) - mnt_ns = task->nsproxy->mnt_ns; - if (mnt_ns) - get_mnt_ns(mnt_ns); - task_unlock(task); - put_task_struct(task); - } - - if (mnt_ns) - m->private = mnt_ns; - else { - seq_release(inode, file); - ret = -EINVAL; - } - } - return ret; + return __mounts_open(inode, file, &mountstats_op); } static const struct file_operations proc_mountstats_operations = {