From 796a6b521d0eadb338adf8cf7e482351c3a8a7b4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 16 Jan 2010 13:28:47 -0500 Subject: Kill CL_PROPAGATION, sanitize fs/pnode.c:get_source() First of all, get_source() never results in CL_PROPAGATION alone. We either get CL_MAKE_SHARED (for the continuation of peer group) or CL_SLAVE (slave that is not shared) or both (beginning of peer group among slaves). Massage the code to make that explicit, kill CL_PROPAGATION test in clone_mnt() (nothing sets CL_MAKE_SHARED without CL_PROPAGATION and in clone_mnt() we are checking CL_PROPAGATION after we'd found that there's no CL_SLAVE, so the check for CL_MAKE_SHARED would do just as well). Fix comments, while we are at it... Signed-off-by: Al Viro --- fs/namespace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index c768f733c8d6..25c1dcf9e9eb 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -573,7 +573,7 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, mnt->mnt_master = old; CLEAR_MNT_SHARED(mnt); } else if (!(flag & CL_PRIVATE)) { - if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old)) + if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old)) list_add(&mnt->mnt_share, &old->mnt_share); if (IS_MNT_SLAVE(old)) list_add(&mnt->mnt_slave, &old->mnt_slave); -- cgit v1.2.3 From 495d6c9c6595ec7b37910dfd42634839431d21fd Mon Sep 17 00:00:00 2001 From: Valerie Aurora Date: Tue, 26 Jan 2010 14:20:47 -0500 Subject: VFS: Clean up shared mount flag propagation The handling of mount flags in set_mnt_shared() got a little tangled up during previous cleanups, with the following problems: * MNT_PNODE_MASK is defined as a literal constant when it should be a bitwise xor of other MNT_* flags * set_mnt_shared() clears and then sets MNT_SHARED (part of MNT_PNODE_MASK) * MNT_PNODE_MASK could use a comment in mount.h * MNT_PNODE_MASK is a terrible name, change to MNT_SHARED_MASK This patch fixes these problems. Signed-off-by: Al Viro --- fs/namespace.c | 2 +- fs/pnode.h | 2 +- include/linux/mount.h | 11 ++++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index 25c1dcf9e9eb..d25d4602ab50 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1538,7 +1538,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags, err = do_remount_sb(sb, flags, data, 0); if (!err) { spin_lock(&vfsmount_lock); - mnt_flags |= path->mnt->mnt_flags & MNT_PNODE_MASK; + mnt_flags |= path->mnt->mnt_flags & MNT_PROPAGATION_MASK; path->mnt->mnt_flags = mnt_flags; spin_unlock(&vfsmount_lock); } diff --git a/fs/pnode.h b/fs/pnode.h index 6c7ef3252a26..1ea4ae1efcd3 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -25,7 +25,7 @@ static inline void set_mnt_shared(struct vfsmount *mnt) { - mnt->mnt_flags &= ~MNT_PNODE_MASK; + mnt->mnt_flags &= ~MNT_SHARED_MASK; mnt->mnt_flags |= MNT_SHARED; } diff --git a/include/linux/mount.h b/include/linux/mount.h index 5d5275364867..375d43a5d802 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -34,7 +34,16 @@ struct mnt_namespace; #define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */ #define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */ -#define MNT_PNODE_MASK 0x3000 /* propagation flag mask */ +/* + * MNT_SHARED_MASK is the set of flags that should be cleared when a + * mount becomes shared. Currently, this is only the flag that says a + * mount cannot be bind mounted, since this is how we create a mount + * that shares events with another mount. If you add a new MNT_* + * flag, consider how it interacts with shared mounts. + */ +#define MNT_SHARED_MASK (MNT_UNBINDABLE) +#define MNT_PROPAGATION_MASK (MNT_SHARED | MNT_UNBINDABLE) + struct vfsmount { struct list_head mnt_hash; -- cgit v1.2.3 From 1f707137b55764740981d022d29c622832a61880 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 30 Jan 2010 22:51:25 -0500 Subject: new helper: iterate_mounts() apply function to vfsmounts in set returned by collect_mounts(), stop if it returns non-zero. Signed-off-by: Al Viro --- fs/namespace.c | 15 +++++++++++++++ include/linux/fs.h | 3 ++- kernel/audit_tree.c | 49 ++++++++++++++++--------------------------------- 3 files changed, 33 insertions(+), 34 deletions(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index d25d4602ab50..d5906c19e08e 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1246,6 +1246,21 @@ void drop_collected_mounts(struct vfsmount *mnt) release_mounts(&umount_list); } +int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, + struct vfsmount *root) +{ + struct vfsmount *mnt; + int res = f(root, arg); + if (res) + return res; + list_for_each_entry(mnt, &root->mnt_list, mnt_list) { + res = f(mnt, arg); + if (res) + return res; + } + return 0; +} + static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end) { struct vfsmount *p; diff --git a/include/linux/fs.h b/include/linux/fs.h index 8d53bc17f93f..e764f247d0ab 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1794,7 +1794,8 @@ extern int may_umount(struct vfsmount *); extern long do_mount(char *, char *, char *, unsigned long, void *); extern struct vfsmount *collect_mounts(struct path *); extern void drop_collected_mounts(struct vfsmount *); - +extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *, + struct vfsmount *); extern int vfs_statfs(struct dentry *, struct kstatfs *); extern int current_umask(void); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index f09b42d9c32d..028e85663f27 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -548,6 +548,11 @@ int audit_remove_tree_rule(struct audit_krule *rule) return 0; } +static int compare_root(struct vfsmount *mnt, void *arg) +{ + return mnt->mnt_root->d_inode == arg; +} + void audit_trim_trees(void) { struct list_head cursor; @@ -559,7 +564,6 @@ void audit_trim_trees(void) struct path path; struct vfsmount *root_mnt; struct node *node; - struct list_head list; int err; tree = container_of(cursor.next, struct audit_tree, list); @@ -577,24 +581,16 @@ void audit_trim_trees(void) if (!root_mnt) goto skip_it; - list_add_tail(&list, &root_mnt->mnt_list); spin_lock(&hash_lock); list_for_each_entry(node, &tree->chunks, list) { - struct audit_chunk *chunk = find_chunk(node); - struct inode *inode = chunk->watch.inode; - struct vfsmount *mnt; + struct inode *inode = find_chunk(node)->watch.inode; node->index |= 1U<<31; - list_for_each_entry(mnt, &list, mnt_list) { - if (mnt->mnt_root->d_inode == inode) { - node->index &= ~(1U<<31); - break; - } - } + if (iterate_mounts(compare_root, inode, root_mnt)) + node->index &= ~(1U<<31); } spin_unlock(&hash_lock); trim_marked(tree); put_tree(tree); - list_del_init(&list); drop_collected_mounts(root_mnt); skip_it: mutex_lock(&audit_filter_mutex); @@ -622,13 +618,17 @@ void audit_put_tree(struct audit_tree *tree) put_tree(tree); } +static int tag_mount(struct vfsmount *mnt, void *arg) +{ + return tag_chunk(mnt->mnt_root->d_inode, arg); +} + /* called with audit_filter_mutex */ int audit_add_tree_rule(struct audit_krule *rule) { struct audit_tree *seed = rule->tree, *tree; struct path path; - struct vfsmount *mnt, *p; - struct list_head list; + struct vfsmount *mnt; int err; list_for_each_entry(tree, &tree_list, list) { @@ -654,16 +654,9 @@ int audit_add_tree_rule(struct audit_krule *rule) err = -ENOMEM; goto Err; } - list_add_tail(&list, &mnt->mnt_list); get_tree(tree); - list_for_each_entry(p, &list, mnt_list) { - err = tag_chunk(p->mnt_root->d_inode, tree); - if (err) - break; - } - - list_del(&list); + err = iterate_mounts(tag_mount, tree, mnt); drop_collected_mounts(mnt); if (!err) { @@ -700,7 +693,6 @@ int audit_tag_tree(char *old, char *new) int failed = 0; struct path path1, path2; struct vfsmount *tagged; - struct list_head list; int err; err = kern_path(new, 0, &path2); @@ -717,15 +709,12 @@ int audit_tag_tree(char *old, char *new) return err; } - list_add_tail(&list, &tagged->mnt_list); - mutex_lock(&audit_filter_mutex); list_add(&barrier, &tree_list); list_add(&cursor, &barrier); while (cursor.next != &tree_list) { struct audit_tree *tree; - struct vfsmount *p; int good_one = 0; tree = container_of(cursor.next, struct audit_tree, list); @@ -746,12 +735,7 @@ int audit_tag_tree(char *old, char *new) continue; } - list_for_each_entry(p, &list, mnt_list) { - failed = tag_chunk(p->mnt_root->d_inode, tree); - if (failed) - break; - } - + failed = iterate_mounts(tag_mount, tree, tagged); if (failed) { put_tree(tree); mutex_lock(&audit_filter_mutex); @@ -792,7 +776,6 @@ int audit_tag_tree(char *old, char *new) } list_del(&barrier); list_del(&cursor); - list_del(&list); mutex_unlock(&audit_filter_mutex); path_put(&path1); drop_collected_mounts(tagged); -- cgit v1.2.3 From 9f5596af44514f99e3a654a4f7cb813354b9e516 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 5 Feb 2010 00:40:25 -0500 Subject: take check for new events in namespace (guts of mounts_poll()) to namespace.c Signed-off-by: Al Viro --- fs/namespace.c | 15 +++++++++++++++ fs/proc/base.c | 10 ++-------- include/linux/mnt_namespace.h | 1 + 3 files changed, 18 insertions(+), 8 deletions(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index d5906c19e08e..970fe79d7867 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -737,6 +737,21 @@ static void m_stop(struct seq_file *m, void *v) up_read(&namespace_sem); } +int mnt_had_events(struct proc_mounts *p) +{ + struct mnt_namespace *ns = p->ns; + int res = 0; + + spin_lock(&vfsmount_lock); + if (p->event != ns->event) { + p->event = ns->event; + res = 1; + } + spin_unlock(&vfsmount_lock); + + return res; +} + struct proc_fs_info { int flag; const char *str; diff --git a/fs/proc/base.c b/fs/proc/base.c index 58324c299165..746895ddfda1 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -647,17 +647,11 @@ static int mounts_release(struct inode *inode, struct file *file) static unsigned mounts_poll(struct file *file, poll_table *wait) { struct proc_mounts *p = file->private_data; - struct mnt_namespace *ns = p->ns; unsigned res = POLLIN | POLLRDNORM; - poll_wait(file, &ns->poll, wait); - - spin_lock(&vfsmount_lock); - if (p->event != ns->event) { - p->event = ns->event; + poll_wait(file, &p->ns->poll, wait); + if (mnt_had_events(p)) res |= POLLERR | POLLPRI; - } - spin_unlock(&vfsmount_lock); return res; } diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h index d74785c2393a..0b89efc6f215 100644 --- a/include/linux/mnt_namespace.h +++ b/include/linux/mnt_namespace.h @@ -35,6 +35,7 @@ static inline void get_mnt_ns(struct mnt_namespace *ns) extern const struct seq_operations mounts_op; extern const struct seq_operations mountinfo_op; extern const struct seq_operations mountstats_op; +extern int mnt_had_events(struct proc_mounts *); #endif #endif -- cgit v1.2.3 From d498b25a4f877be535246c4e5b6ee5890781a477 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 5 Feb 2010 02:21:06 -0500 Subject: get rid of useless vfsmount_lock use in put_mnt_ns() It hadn't been needed since we'd sanitized the logics in mark_mounts_for_expiry() (which, in turn, used to be a rudiment of bad old times when namespace_sem was per-ns). Signed-off-by: Al Viro --- fs/namespace.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index 970fe79d7867..b0b15cc2117c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2344,17 +2344,13 @@ void __init mnt_init(void) void put_mnt_ns(struct mnt_namespace *ns) { - struct vfsmount *root; LIST_HEAD(umount_list); - if (!atomic_dec_and_lock(&ns->count, &vfsmount_lock)) + if (!atomic_dec_and_test(&ns->count)) return; - root = ns->root; - ns->root = NULL; - spin_unlock(&vfsmount_lock); down_write(&namespace_sem); spin_lock(&vfsmount_lock); - umount_tree(root, 0, &umount_list); + umount_tree(ns->root, 0, &umount_list); spin_unlock(&vfsmount_lock); up_write(&namespace_sem); release_mounts(&umount_list); -- cgit v1.2.3 From 8089352a13b785d4e0df63d87bd2b71c76bb9aee Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 5 Feb 2010 09:30:46 -0500 Subject: Mirror MS_KERNMOUNT in ->mnt_flags Signed-off-by: Al Viro --- fs/namespace.c | 2 +- fs/super.c | 3 +++ include/linux/mount.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index b0b15cc2117c..ffa3843404e0 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1701,7 +1701,7 @@ int do_add_mount(struct vfsmount *newmnt, struct path *path, { int err; - mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD); + mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL); down_write(&namespace_sem); /* Something was mounted here while we slept */ diff --git a/fs/super.c b/fs/super.c index 903896ec7c73..f35ac6022109 100644 --- a/fs/super.c +++ b/fs/super.c @@ -937,6 +937,9 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void if (!mnt) goto out; + if (flags & MS_KERNMOUNT) + mnt->mnt_flags = MNT_INTERNAL; + if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) { secdata = alloc_secdata(); if (!secdata) diff --git a/include/linux/mount.h b/include/linux/mount.h index 163896137ab5..ca726ebf50a3 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -45,6 +45,8 @@ struct mnt_namespace; #define MNT_PROPAGATION_MASK (MNT_SHARED | MNT_UNBINDABLE) +#define MNT_INTERNAL 0x4000 + struct vfsmount { struct list_head mnt_hash; struct vfsmount *mnt_parent; /* fs we are mounted on */ -- cgit v1.2.3 From db1f05bb85d7966b9176e293f3ceead1cb8b5d79 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 10 Feb 2010 12:15:53 +0100 Subject: vfs: add NOFOLLOW flag to umount(2) Add a new UMOUNT_NOFOLLOW flag to umount(2). This is needed to prevent symlink attacks in unprivileged unmounts (fuse, samba, ncpfs). Additionally, return -EINVAL if an unknown flag is used (and specify an explicitly unused flag: UMOUNT_UNUSED). This makes it possible for the caller to determine if a flag is supported or not. CC: Eugene Teo CC: Michael Kerrisk Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/namespace.c | 9 ++++++++- include/linux/fs.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index ffa3843404e0..8174c8ab5c70 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1136,8 +1136,15 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags) { struct path path; int retval; + int lookup_flags = 0; - retval = user_path(name, &path); + if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) + return -EINVAL; + + if (!(flags & UMOUNT_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + + retval = user_path_at(AT_FDCWD, name, lookup_flags, &path); if (retval) goto out; retval = -EINVAL; diff --git a/include/linux/fs.h b/include/linux/fs.h index e764f247d0ab..5b3182c7eb5f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1305,6 +1305,8 @@ extern int send_sigurg(struct fown_struct *fown); #define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */ #define MNT_DETACH 0x00000002 /* Just detach from the tree */ #define MNT_EXPIRE 0x00000004 /* Mark for expiry */ +#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */ +#define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */ extern struct list_head super_blocks; extern spinlock_t sb_lock; -- cgit v1.2.3