diff options
-rw-r--r-- | fs/namespace.c | 30 | ||||
-rw-r--r-- | fs/proc/base.c | 62 | ||||
-rw-r--r-- | include/linux/namespace.h | 2 |
3 files changed, 78 insertions, 16 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 611f777bbd61..d1aca685aacf 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -37,7 +37,9 @@ static inline int sysfs_init(void) | |||
37 | #endif | 37 | #endif |
38 | 38 | ||
39 | /* spinlock for vfsmount related operations, inplace of dcache_lock */ | 39 | /* spinlock for vfsmount related operations, inplace of dcache_lock */ |
40 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); | 40 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); |
41 | |||
42 | static int event; | ||
41 | 43 | ||
42 | static struct list_head *mount_hashtable; | 44 | static struct list_head *mount_hashtable; |
43 | static int hash_mask __read_mostly, hash_bits __read_mostly; | 45 | static int hash_mask __read_mostly, hash_bits __read_mostly; |
@@ -111,6 +113,22 @@ static inline int check_mnt(struct vfsmount *mnt) | |||
111 | return mnt->mnt_namespace == current->namespace; | 113 | return mnt->mnt_namespace == current->namespace; |
112 | } | 114 | } |
113 | 115 | ||
116 | static void touch_namespace(struct namespace *ns) | ||
117 | { | ||
118 | if (ns) { | ||
119 | ns->event = ++event; | ||
120 | wake_up_interruptible(&ns->poll); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static void __touch_namespace(struct namespace *ns) | ||
125 | { | ||
126 | if (ns && ns->event != event) { | ||
127 | ns->event = event; | ||
128 | wake_up_interruptible(&ns->poll); | ||
129 | } | ||
130 | } | ||
131 | |||
114 | static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) | 132 | static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) |
115 | { | 133 | { |
116 | old_nd->dentry = mnt->mnt_mountpoint; | 134 | old_nd->dentry = mnt->mnt_mountpoint; |
@@ -384,6 +402,7 @@ static void umount_tree(struct vfsmount *mnt) | |||
384 | for (p = mnt; p; p = next_mnt(p, mnt)) { | 402 | for (p = mnt; p; p = next_mnt(p, mnt)) { |
385 | list_del(&p->mnt_list); | 403 | list_del(&p->mnt_list); |
386 | list_add(&p->mnt_list, &kill); | 404 | list_add(&p->mnt_list, &kill); |
405 | __touch_namespace(p->mnt_namespace); | ||
387 | p->mnt_namespace = NULL; | 406 | p->mnt_namespace = NULL; |
388 | } | 407 | } |
389 | 408 | ||
@@ -473,6 +492,7 @@ static int do_umount(struct vfsmount *mnt, int flags) | |||
473 | 492 | ||
474 | down_write(¤t->namespace->sem); | 493 | down_write(¤t->namespace->sem); |
475 | spin_lock(&vfsmount_lock); | 494 | spin_lock(&vfsmount_lock); |
495 | event++; | ||
476 | 496 | ||
477 | retval = -EBUSY; | 497 | retval = -EBUSY; |
478 | if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { | 498 | 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 | list_splice(&head, current->namespace->list.prev); | 654 | list_splice(&head, current->namespace->list.prev); |
635 | mntget(mnt); | 655 | mntget(mnt); |
636 | err = 0; | 656 | err = 0; |
657 | touch_namespace(current->namespace); | ||
637 | } | 658 | } |
638 | spin_unlock(&vfsmount_lock); | 659 | spin_unlock(&vfsmount_lock); |
639 | out_unlock: | 660 | out_unlock: |
@@ -771,6 +792,7 @@ static int do_move_mount(struct nameidata *nd, char *old_name) | |||
771 | 792 | ||
772 | detach_mnt(old_nd.mnt, &parent_nd); | 793 | detach_mnt(old_nd.mnt, &parent_nd); |
773 | attach_mnt(old_nd.mnt, nd); | 794 | attach_mnt(old_nd.mnt, nd); |
795 | touch_namespace(current->namespace); | ||
774 | 796 | ||
775 | /* if the mount is moved, it should no longer be expire | 797 | /* if the mount is moved, it should no longer be expire |
776 | * automatically */ | 798 | * automatically */ |
@@ -877,6 +899,7 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts) | |||
877 | struct nameidata old_nd; | 899 | struct nameidata old_nd; |
878 | 900 | ||
879 | /* delete from the namespace */ | 901 | /* delete from the namespace */ |
902 | touch_namespace(mnt->mnt_namespace); | ||
880 | list_del_init(&mnt->mnt_list); | 903 | list_del_init(&mnt->mnt_list); |
881 | mnt->mnt_namespace = NULL; | 904 | mnt->mnt_namespace = NULL; |
882 | detach_mnt(mnt, &old_nd); | 905 | detach_mnt(mnt, &old_nd); |
@@ -1114,6 +1137,8 @@ int copy_namespace(int flags, struct task_struct *tsk) | |||
1114 | atomic_set(&new_ns->count, 1); | 1137 | atomic_set(&new_ns->count, 1); |
1115 | init_rwsem(&new_ns->sem); | 1138 | init_rwsem(&new_ns->sem); |
1116 | INIT_LIST_HEAD(&new_ns->list); | 1139 | INIT_LIST_HEAD(&new_ns->list); |
1140 | init_waitqueue_head(&new_ns->poll); | ||
1141 | new_ns->event = 0; | ||
1117 | 1142 | ||
1118 | down_write(&tsk->namespace->sem); | 1143 | down_write(&tsk->namespace->sem); |
1119 | /* First pass: copy the tree topology */ | 1144 | /* 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 | detach_mnt(user_nd.mnt, &root_parent); | 1402 | detach_mnt(user_nd.mnt, &root_parent); |
1378 | attach_mnt(user_nd.mnt, &old_nd); /* mount old root on put_old */ | 1403 | attach_mnt(user_nd.mnt, &old_nd); /* mount old root on put_old */ |
1379 | attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */ | 1404 | attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */ |
1405 | touch_namespace(current->namespace); | ||
1380 | spin_unlock(&vfsmount_lock); | 1406 | spin_unlock(&vfsmount_lock); |
1381 | chroot_fs_refs(&user_nd, &new_nd); | 1407 | chroot_fs_refs(&user_nd, &new_nd); |
1382 | security_sb_post_pivotroot(&user_nd, &new_nd); | 1408 | security_sb_post_pivotroot(&user_nd, &new_nd); |
@@ -1413,6 +1439,8 @@ static void __init init_mount_tree(void) | |||
1413 | atomic_set(&namespace->count, 1); | 1439 | atomic_set(&namespace->count, 1); |
1414 | INIT_LIST_HEAD(&namespace->list); | 1440 | INIT_LIST_HEAD(&namespace->list); |
1415 | init_rwsem(&namespace->sem); | 1441 | init_rwsem(&namespace->sem); |
1442 | init_waitqueue_head(&namespace->poll); | ||
1443 | namespace->event = 0; | ||
1416 | list_add(&mnt->mnt_list, &namespace->list); | 1444 | list_add(&mnt->mnt_list, &namespace->list); |
1417 | namespace->root = mnt; | 1445 | namespace->root = mnt; |
1418 | mnt->mnt_namespace = namespace; | 1446 | mnt->mnt_namespace = namespace; |
diff --git a/fs/proc/base.c b/fs/proc/base.c index a170450aadb1..634355e16986 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -70,6 +70,7 @@ | |||
70 | #include <linux/seccomp.h> | 70 | #include <linux/seccomp.h> |
71 | #include <linux/cpuset.h> | 71 | #include <linux/cpuset.h> |
72 | #include <linux/audit.h> | 72 | #include <linux/audit.h> |
73 | #include <linux/poll.h> | ||
73 | #include "internal.h" | 74 | #include "internal.h" |
74 | 75 | ||
75 | /* | 76 | /* |
@@ -660,26 +661,38 @@ static struct file_operations proc_smaps_operations = { | |||
660 | #endif | 661 | #endif |
661 | 662 | ||
662 | extern struct seq_operations mounts_op; | 663 | extern struct seq_operations mounts_op; |
664 | struct proc_mounts { | ||
665 | struct seq_file m; | ||
666 | int event; | ||
667 | }; | ||
668 | |||
663 | static int mounts_open(struct inode *inode, struct file *file) | 669 | static int mounts_open(struct inode *inode, struct file *file) |
664 | { | 670 | { |
665 | struct task_struct *task = proc_task(inode); | 671 | struct task_struct *task = proc_task(inode); |
666 | int ret = seq_open(file, &mounts_op); | 672 | struct namespace *namespace; |
673 | struct proc_mounts *p; | ||
674 | int ret = -EINVAL; | ||
667 | 675 | ||
668 | if (!ret) { | 676 | task_lock(task); |
669 | struct seq_file *m = file->private_data; | 677 | namespace = task->namespace; |
670 | struct namespace *namespace; | 678 | if (namespace) |
671 | task_lock(task); | 679 | get_namespace(namespace); |
672 | namespace = task->namespace; | 680 | task_unlock(task); |
673 | if (namespace) | 681 | |
674 | get_namespace(namespace); | 682 | if (namespace) { |
675 | task_unlock(task); | 683 | ret = -ENOMEM; |
676 | 684 | p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); | |
677 | if (namespace) | 685 | if (p) { |
678 | m->private = namespace; | 686 | file->private_data = &p->m; |
679 | else { | 687 | ret = seq_open(file, &mounts_op); |
680 | seq_release(inode, file); | 688 | if (!ret) { |
681 | ret = -EINVAL; | 689 | p->m.private = namespace; |
690 | p->event = namespace->event; | ||
691 | return 0; | ||
692 | } | ||
693 | kfree(p); | ||
682 | } | 694 | } |
695 | put_namespace(namespace); | ||
683 | } | 696 | } |
684 | return ret; | 697 | return ret; |
685 | } | 698 | } |
@@ -692,11 +705,30 @@ static int mounts_release(struct inode *inode, struct file *file) | |||
692 | return seq_release(inode, file); | 705 | return seq_release(inode, file); |
693 | } | 706 | } |
694 | 707 | ||
708 | static unsigned mounts_poll(struct file *file, poll_table *wait) | ||
709 | { | ||
710 | struct proc_mounts *p = file->private_data; | ||
711 | struct namespace *ns = p->m.private; | ||
712 | unsigned res = 0; | ||
713 | |||
714 | poll_wait(file, &ns->poll, wait); | ||
715 | |||
716 | spin_lock(&vfsmount_lock); | ||
717 | if (p->event != ns->event) { | ||
718 | p->event = ns->event; | ||
719 | res = POLLERR; | ||
720 | } | ||
721 | spin_unlock(&vfsmount_lock); | ||
722 | |||
723 | return res; | ||
724 | } | ||
725 | |||
695 | static struct file_operations proc_mounts_operations = { | 726 | static struct file_operations proc_mounts_operations = { |
696 | .open = mounts_open, | 727 | .open = mounts_open, |
697 | .read = seq_read, | 728 | .read = seq_read, |
698 | .llseek = seq_lseek, | 729 | .llseek = seq_lseek, |
699 | .release = mounts_release, | 730 | .release = mounts_release, |
731 | .poll = mounts_poll, | ||
700 | }; | 732 | }; |
701 | 733 | ||
702 | #define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ | 734 | #define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ |
diff --git a/include/linux/namespace.h b/include/linux/namespace.h index 0e5a86f13b2f..6f0f25d64c38 100644 --- a/include/linux/namespace.h +++ b/include/linux/namespace.h | |||
@@ -10,6 +10,8 @@ struct namespace { | |||
10 | struct vfsmount * root; | 10 | struct vfsmount * root; |
11 | struct list_head list; | 11 | struct list_head list; |
12 | struct rw_semaphore sem; | 12 | struct rw_semaphore sem; |
13 | wait_queue_head_t poll; | ||
14 | int event; | ||
13 | }; | 15 | }; |
14 | 16 | ||
15 | extern int copy_namespace(int, struct task_struct *); | 17 | extern int copy_namespace(int, struct task_struct *); |