Commit 5addc5dd authored by Al Viro's avatar Al Viro Committed by Linus Torvalds

[PATCH] make /proc/mounts pollable

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 1abe77b0
...@@ -37,7 +37,9 @@ static inline int sysfs_init(void) ...@@ -37,7 +37,9 @@ static inline int sysfs_init(void)
#endif #endif
/* spinlock for vfsmount related operations, inplace of dcache_lock */ /* spinlock for vfsmount related operations, inplace of dcache_lock */
__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
static int event;
static struct list_head *mount_hashtable; static struct list_head *mount_hashtable;
static int hash_mask __read_mostly, hash_bits __read_mostly; static int hash_mask __read_mostly, hash_bits __read_mostly;
...@@ -111,6 +113,22 @@ static inline int check_mnt(struct vfsmount *mnt) ...@@ -111,6 +113,22 @@ static inline int check_mnt(struct vfsmount *mnt)
return mnt->mnt_namespace == current->namespace; return mnt->mnt_namespace == current->namespace;
} }
static void touch_namespace(struct namespace *ns)
{
if (ns) {
ns->event = ++event;
wake_up_interruptible(&ns->poll);
}
}
static void __touch_namespace(struct namespace *ns)
{
if (ns && ns->event != event) {
ns->event = event;
wake_up_interruptible(&ns->poll);
}
}
static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
{ {
old_nd->dentry = mnt->mnt_mountpoint; old_nd->dentry = mnt->mnt_mountpoint;
...@@ -384,6 +402,7 @@ static void umount_tree(struct vfsmount *mnt) ...@@ -384,6 +402,7 @@ static void umount_tree(struct vfsmount *mnt)
for (p = mnt; p; p = next_mnt(p, mnt)) { for (p = mnt; p; p = next_mnt(p, mnt)) {
list_del(&p->mnt_list); list_del(&p->mnt_list);
list_add(&p->mnt_list, &kill); list_add(&p->mnt_list, &kill);
__touch_namespace(p->mnt_namespace);
p->mnt_namespace = NULL; p->mnt_namespace = NULL;
} }
...@@ -473,6 +492,7 @@ static int do_umount(struct vfsmount *mnt, int flags) ...@@ -473,6 +492,7 @@ static int do_umount(struct vfsmount *mnt, int flags)
down_write(&current->namespace->sem); down_write(&current->namespace->sem);
spin_lock(&vfsmount_lock); spin_lock(&vfsmount_lock);
event++;
retval = -EBUSY; retval = -EBUSY;
if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) {
...@@ -634,6 +654,7 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) ...@@ -634,6 +654,7 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
list_splice(&head, current->namespace->list.prev); list_splice(&head, current->namespace->list.prev);
mntget(mnt); mntget(mnt);
err = 0; err = 0;
touch_namespace(current->namespace);
} }
spin_unlock(&vfsmount_lock); spin_unlock(&vfsmount_lock);
out_unlock: out_unlock:
...@@ -771,6 +792,7 @@ static int do_move_mount(struct nameidata *nd, char *old_name) ...@@ -771,6 +792,7 @@ static int do_move_mount(struct nameidata *nd, char *old_name)
detach_mnt(old_nd.mnt, &parent_nd); detach_mnt(old_nd.mnt, &parent_nd);
attach_mnt(old_nd.mnt, nd); attach_mnt(old_nd.mnt, nd);
touch_namespace(current->namespace);
/* if the mount is moved, it should no longer be expire /* if the mount is moved, it should no longer be expire
* automatically */ * automatically */
...@@ -877,6 +899,7 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts) ...@@ -877,6 +899,7 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts)
struct nameidata old_nd; struct nameidata old_nd;
/* delete from the namespace */ /* delete from the namespace */
touch_namespace(mnt->mnt_namespace);
list_del_init(&mnt->mnt_list); list_del_init(&mnt->mnt_list);
mnt->mnt_namespace = NULL; mnt->mnt_namespace = NULL;
detach_mnt(mnt, &old_nd); detach_mnt(mnt, &old_nd);
...@@ -1114,6 +1137,8 @@ int copy_namespace(int flags, struct task_struct *tsk) ...@@ -1114,6 +1137,8 @@ int copy_namespace(int flags, struct task_struct *tsk)
atomic_set(&new_ns->count, 1); atomic_set(&new_ns->count, 1);
init_rwsem(&new_ns->sem); init_rwsem(&new_ns->sem);
INIT_LIST_HEAD(&new_ns->list); INIT_LIST_HEAD(&new_ns->list);
init_waitqueue_head(&new_ns->poll);
new_ns->event = 0;
down_write(&tsk->namespace->sem); down_write(&tsk->namespace->sem);
/* First pass: copy the tree topology */ /* First pass: copy the tree topology */
...@@ -1377,6 +1402,7 @@ asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *p ...@@ -1377,6 +1402,7 @@ asmlinkage long sys_pivot_root(const char __user *new_root, const char __user *p
detach_mnt(user_nd.mnt, &root_parent); detach_mnt(user_nd.mnt, &root_parent);
attach_mnt(user_nd.mnt, &old_nd); /* mount old root on put_old */ attach_mnt(user_nd.mnt, &old_nd); /* mount old root on put_old */
attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */ attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */
touch_namespace(current->namespace);
spin_unlock(&vfsmount_lock); spin_unlock(&vfsmount_lock);
chroot_fs_refs(&user_nd, &new_nd); chroot_fs_refs(&user_nd, &new_nd);
security_sb_post_pivotroot(&user_nd, &new_nd); security_sb_post_pivotroot(&user_nd, &new_nd);
...@@ -1413,6 +1439,8 @@ static void __init init_mount_tree(void) ...@@ -1413,6 +1439,8 @@ static void __init init_mount_tree(void)
atomic_set(&namespace->count, 1); atomic_set(&namespace->count, 1);
INIT_LIST_HEAD(&namespace->list); INIT_LIST_HEAD(&namespace->list);
init_rwsem(&namespace->sem); init_rwsem(&namespace->sem);
init_waitqueue_head(&namespace->poll);
namespace->event = 0;
list_add(&mnt->mnt_list, &namespace->list); list_add(&mnt->mnt_list, &namespace->list);
namespace->root = mnt; namespace->root = mnt;
mnt->mnt_namespace = namespace; mnt->mnt_namespace = namespace;
......
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
#include <linux/seccomp.h> #include <linux/seccomp.h>
#include <linux/cpuset.h> #include <linux/cpuset.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/poll.h>
#include "internal.h" #include "internal.h"
/* /*
...@@ -660,26 +661,38 @@ static struct file_operations proc_smaps_operations = { ...@@ -660,26 +661,38 @@ static struct file_operations proc_smaps_operations = {
#endif #endif
extern struct seq_operations mounts_op; extern struct seq_operations mounts_op;
struct proc_mounts {
struct seq_file m;
int event;
};
static int mounts_open(struct inode *inode, struct file *file) static int mounts_open(struct inode *inode, struct file *file)
{ {
struct task_struct *task = proc_task(inode); struct task_struct *task = proc_task(inode);
int ret = seq_open(file, &mounts_op);
if (!ret) {
struct seq_file *m = file->private_data;
struct namespace *namespace; struct namespace *namespace;
struct proc_mounts *p;
int ret = -EINVAL;
task_lock(task); task_lock(task);
namespace = task->namespace; namespace = task->namespace;
if (namespace) if (namespace)
get_namespace(namespace); get_namespace(namespace);
task_unlock(task); task_unlock(task);
if (namespace) if (namespace) {
m->private = namespace; ret = -ENOMEM;
else { p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL);
seq_release(inode, file); if (p) {
ret = -EINVAL; file->private_data = &p->m;
ret = seq_open(file, &mounts_op);
if (!ret) {
p->m.private = namespace;
p->event = namespace->event;
return 0;
}
kfree(p);
} }
put_namespace(namespace);
} }
return ret; return ret;
} }
...@@ -692,11 +705,30 @@ static int mounts_release(struct inode *inode, struct file *file) ...@@ -692,11 +705,30 @@ static int mounts_release(struct inode *inode, struct file *file)
return seq_release(inode, file); return seq_release(inode, file);
} }
static unsigned mounts_poll(struct file *file, poll_table *wait)
{
struct proc_mounts *p = file->private_data;
struct namespace *ns = p->m.private;
unsigned res = 0;
poll_wait(file, &ns->poll, wait);
spin_lock(&vfsmount_lock);
if (p->event != ns->event) {
p->event = ns->event;
res = POLLERR;
}
spin_unlock(&vfsmount_lock);
return res;
}
static struct file_operations proc_mounts_operations = { static struct file_operations proc_mounts_operations = {
.open = mounts_open, .open = mounts_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = mounts_release, .release = mounts_release,
.poll = mounts_poll,
}; };
#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ #define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */
......
...@@ -10,6 +10,8 @@ struct namespace { ...@@ -10,6 +10,8 @@ struct namespace {
struct vfsmount * root; struct vfsmount * root;
struct list_head list; struct list_head list;
struct rw_semaphore sem; struct rw_semaphore sem;
wait_queue_head_t poll;
int event;
}; };
extern int copy_namespace(int, struct task_struct *); extern int copy_namespace(int, struct task_struct *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment