From 07b20889e3052c7e77d6a6a54e7e83446eb1ba84 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 7 Nov 2005 17:19:07 -0500 Subject: [PATCH] beginning of the shared-subtree proper A private mount does not forward or receive propagation. This patch provides user the ability to convert any mount to private. Signed-off-by: Ram Pai Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/pnode.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 fs/pnode.c (limited to 'fs/pnode.c') diff --git a/fs/pnode.c b/fs/pnode.c new file mode 100644 index 000000000000..aaa0dffda12a --- /dev/null +++ b/fs/pnode.c @@ -0,0 +1,17 @@ +/* + * linux/fs/pnode.c + * + * (C) Copyright IBM Corporation 2005. + * Released under GPL v2. + * Author : Ram Pai (linuxram@us.ibm.com) + * + */ +#include +#include +#include +#include "pnode.h" + +void change_mnt_propagation(struct vfsmount *mnt, int type) +{ + mnt->mnt_flags &= ~MNT_PNODE_MASK; +} -- cgit v1.2.3 From 03e06e68ff76294e53ffa898cb844d2a997b043e Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 7 Nov 2005 17:19:33 -0500 Subject: [PATCH] introduce shared mounts This creates shared mounts. A shared mount when bind-mounted to some mountpoint, propagates mount/umount events to each other. All the shared mounts that propagate events to each other belong to the same peer-group. Signed-off-by: Ram Pai Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/namespace.c | 3 ++- fs/pnode.c | 13 ++++++++++++- fs/pnode.h | 4 ++++ include/linux/fs.h | 1 + include/linux/mount.h | 2 ++ 5 files changed, 21 insertions(+), 2 deletions(-) (limited to 'fs/pnode.c') diff --git a/fs/namespace.c b/fs/namespace.c index 3782923d6d4d..f6861a5487df 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -68,6 +68,7 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_mounts); INIT_LIST_HEAD(&mnt->mnt_list); INIT_LIST_HEAD(&mnt->mnt_expire); + INIT_LIST_HEAD(&mnt->mnt_share); if (name) { int size = strlen(name) + 1; char *newname = kmalloc(size, GFP_KERNEL); @@ -1113,7 +1114,7 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, data_page); else if (flags & MS_BIND) retval = do_loopback(&nd, dev_name, flags & MS_REC); - else if (flags & MS_PRIVATE) + else if (flags & (MS_SHARED | MS_PRIVATE)) retval = do_change_type(&nd, flags); else if (flags & MS_MOVE) retval = do_move_mount(&nd, dev_name); diff --git a/fs/pnode.c b/fs/pnode.c index aaa0dffda12a..1e22165ea41f 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -11,7 +11,18 @@ #include #include "pnode.h" +/* return the next shared peer mount of @p */ +static inline struct vfsmount *next_peer(struct vfsmount *p) +{ + return list_entry(p->mnt_share.next, struct vfsmount, mnt_share); +} + void change_mnt_propagation(struct vfsmount *mnt, int type) { - mnt->mnt_flags &= ~MNT_PNODE_MASK; + if (type == MS_SHARED) { + mnt->mnt_flags |= MNT_SHARED; + } else { + list_del_init(&mnt->mnt_share); + mnt->mnt_flags &= ~MNT_PNODE_MASK; + } } diff --git a/fs/pnode.h b/fs/pnode.h index 33549a3a74eb..ab1bdaee4e08 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -10,5 +10,9 @@ #include #include + +#define IS_MNT_SHARED(mnt) (mnt->mnt_flags & MNT_SHARED) +#define CLEAR_MNT_SHARED(mnt) (mnt->mnt_flags &= ~MNT_SHARED) + void change_mnt_propagation(struct vfsmount *, int); #endif /* _LINUX_PNODE_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 6c431086abb3..551fba303cf8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -105,6 +105,7 @@ extern int dir_notify_enable; #define MS_REC 16384 #define MS_VERBOSE 32768 #define MS_PRIVATE (1<<18) /* change to private */ +#define MS_SHARED (1<<20) /* change to shared */ #define MS_POSIXACL (1<<16) /* VFS does not apply the umask */ #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) diff --git a/include/linux/mount.h b/include/linux/mount.h index 8eadd3b65899..2582559718fc 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -20,6 +20,7 @@ #define MNT_NOSUID 0x01 #define MNT_NODEV 0x02 #define MNT_NOEXEC 0x04 +#define MNT_SHARED 0x10 /* if the vfsmount is a shared mount */ #define MNT_PNODE_MASK 0x30 /* propogation flag mask */ struct vfsmount { @@ -36,6 +37,7 @@ struct vfsmount { char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ + struct list_head mnt_share; /* circular list of shared mounts */ struct namespace *mnt_namespace; /* containing namespace */ int mnt_pinned; }; -- cgit v1.2.3 From b90fa9ae8f51f098ee480bbaabd6867992e9fc58 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 7 Nov 2005 17:19:50 -0500 Subject: [PATCH] shared mount handling: bind and rbind Implement handling of MS_BIND in presense of shared mounts (see Documentation/sharedsubtree.txt in the end of patch series for detailed description). Signed-off-by: Ram Pai Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/namespace.c | 126 ++++++++++++++++++++++++++++++++++++++++++++--------- fs/pnode.c | 81 +++++++++++++++++++++++++++++++++- fs/pnode.h | 14 ++++++ include/linux/fs.h | 5 +++ 4 files changed, 204 insertions(+), 22 deletions(-) (limited to 'fs/pnode.c') diff --git a/fs/namespace.c b/fs/namespace.c index f6861a5487df..9f5a084b239f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -28,8 +28,6 @@ extern int __init init_rootfs(void); -#define CL_EXPIRE 0x01 - #ifdef CONFIG_SYSFS extern int __init sysfs_init(void); #else @@ -145,13 +143,43 @@ static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd) old_nd->dentry->d_mounted--; } +void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry, + struct vfsmount *child_mnt) +{ + child_mnt->mnt_parent = mntget(mnt); + child_mnt->mnt_mountpoint = dget(dentry); + dentry->d_mounted++; +} + static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd) { - mnt->mnt_parent = mntget(nd->mnt); - mnt->mnt_mountpoint = dget(nd->dentry); - list_add(&mnt->mnt_hash, mount_hashtable + hash(nd->mnt, nd->dentry)); + mnt_set_mountpoint(nd->mnt, nd->dentry, mnt); + list_add_tail(&mnt->mnt_hash, mount_hashtable + + hash(nd->mnt, nd->dentry)); list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts); - nd->dentry->d_mounted++; +} + +/* + * the caller must hold vfsmount_lock + */ +static void commit_tree(struct vfsmount *mnt) +{ + struct vfsmount *parent = mnt->mnt_parent; + struct vfsmount *m; + LIST_HEAD(head); + struct namespace *n = parent->mnt_namespace; + + BUG_ON(parent == mnt); + + list_add_tail(&head, &mnt->mnt_list); + list_for_each_entry(m, &head, mnt_list) + m->mnt_namespace = n; + list_splice(&head, n->list.prev); + + list_add_tail(&mnt->mnt_hash, mount_hashtable + + hash(parent, mnt->mnt_mountpoint)); + list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); + touch_namespace(n); } static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) @@ -183,7 +211,11 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, mnt->mnt_root = dget(root); mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; - mnt->mnt_namespace = current->namespace; + + if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old)) + list_add(&mnt->mnt_share, &old->mnt_share); + if (flag & CL_MAKE_SHARED) + set_mnt_shared(mnt); /* stick the duplicate mount on the same expiry list * as the original if that was on one */ @@ -379,7 +411,7 @@ int may_umount(struct vfsmount *mnt) EXPORT_SYMBOL(may_umount); -static void release_mounts(struct list_head *head) +void release_mounts(struct list_head *head) { struct vfsmount *mnt; while(!list_empty(head)) { @@ -401,7 +433,7 @@ static void release_mounts(struct list_head *head) } } -static void umount_tree(struct vfsmount *mnt, struct list_head *kill) +void umount_tree(struct vfsmount *mnt, struct list_head *kill) { struct vfsmount *p; @@ -581,7 +613,7 @@ static int lives_below_in_same_fs(struct dentry *d, struct dentry *dentry) } } -static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, +struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, int flag) { struct vfsmount *res, *p, *q, *r, *s; @@ -626,6 +658,67 @@ Enomem: return NULL; } +/* + * @source_mnt : mount tree to be attached + * @nd : place the mount tree @source_mnt is attached + * + * NOTE: in the table below explains the semantics when a source mount + * of a given type is attached to a destination mount of a given type. + * --------------------------------------------- + * | BIND MOUNT OPERATION | + * |******************************************** + * | source-->| shared | private | + * | dest | | | + * | | | | | + * | v | | | + * |******************************************** + * | shared | shared (++) | shared (+) | + * | | | | + * |non-shared| shared (+) | private | + * ********************************************* + * A bind operation clones the source mount and mounts the clone on the + * destination mount. + * + * (++) the cloned mount is propagated to all the mounts in the propagation + * tree of the destination mount and the cloned mount is added to + * the peer group of the source mount. + * (+) the cloned mount is created under the destination mount and is marked + * as shared. The cloned mount is added to the peer group of the source + * mount. + * + * if the source mount is a tree, the operations explained above is + * applied to each mount in the tree. + * Must be called without spinlocks held, since this function can sleep + * in allocations. + */ +static int attach_recursive_mnt(struct vfsmount *source_mnt, + struct nameidata *nd) +{ + LIST_HEAD(tree_list); + struct vfsmount *dest_mnt = nd->mnt; + struct dentry *dest_dentry = nd->dentry; + struct vfsmount *child, *p; + + if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list)) + return -EINVAL; + + if (IS_MNT_SHARED(dest_mnt)) { + for (p = source_mnt; p; p = next_mnt(p, source_mnt)) + set_mnt_shared(p); + } + + spin_lock(&vfsmount_lock); + mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt); + commit_tree(source_mnt); + + list_for_each_entry_safe(child, p, &tree_list, mnt_hash) { + list_del_init(&child->mnt_hash); + commit_tree(child); + } + spin_unlock(&vfsmount_lock); + return 0; +} + static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) { int err; @@ -646,17 +739,8 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) goto out_unlock; err = -ENOENT; - spin_lock(&vfsmount_lock); - if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) { - struct list_head head; - - attach_mnt(mnt, nd); - list_add_tail(&head, &mnt->mnt_list); - list_splice(&head, current->namespace->list.prev); - err = 0; - touch_namespace(current->namespace); - } - spin_unlock(&vfsmount_lock); + if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) + err = attach_recursive_mnt(mnt, nd); out_unlock: up(&nd->dentry->d_inode->i_sem); if (!err) diff --git a/fs/pnode.c b/fs/pnode.c index 1e22165ea41f..2d572b88e6f6 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -20,9 +20,88 @@ static inline struct vfsmount *next_peer(struct vfsmount *p) void change_mnt_propagation(struct vfsmount *mnt, int type) { if (type == MS_SHARED) { - mnt->mnt_flags |= MNT_SHARED; + set_mnt_shared(mnt); } else { list_del_init(&mnt->mnt_share); mnt->mnt_flags &= ~MNT_PNODE_MASK; } } + +/* + * get the next mount in the propagation tree. + * @m: the mount seen last + * @origin: the original mount from where the tree walk initiated + */ +static struct vfsmount *propagation_next(struct vfsmount *m, + struct vfsmount *origin) +{ + m = next_peer(m); + if (m == origin) + return NULL; + return m; +} + +/* + * mount 'source_mnt' under the destination 'dest_mnt' at + * dentry 'dest_dentry'. And propagate that mount to + * all the peer and slave mounts of 'dest_mnt'. + * Link all the new mounts into a propagation tree headed at + * source_mnt. Also link all the new mounts using ->mnt_list + * headed at source_mnt's ->mnt_list + * + * @dest_mnt: destination mount. + * @dest_dentry: destination dentry. + * @source_mnt: source mount. + * @tree_list : list of heads of trees to be attached. + */ +int propagate_mnt(struct vfsmount *dest_mnt, struct dentry *dest_dentry, + struct vfsmount *source_mnt, struct list_head *tree_list) +{ + struct vfsmount *m, *child; + int ret = 0; + struct vfsmount *prev_dest_mnt = dest_mnt; + struct vfsmount *prev_src_mnt = source_mnt; + LIST_HEAD(tmp_list); + LIST_HEAD(umount_list); + + for (m = propagation_next(dest_mnt, dest_mnt); m; + m = propagation_next(m, dest_mnt)) { + int type = CL_PROPAGATION; + + if (IS_MNT_NEW(m)) + continue; + + if (IS_MNT_SHARED(m)) + type |= CL_MAKE_SHARED; + + if (!(child = copy_tree(source_mnt, source_mnt->mnt_root, + type))) { + ret = -ENOMEM; + list_splice(tree_list, tmp_list.prev); + goto out; + } + + if (is_subdir(dest_dentry, m->mnt_root)) { + mnt_set_mountpoint(m, dest_dentry, child); + list_add_tail(&child->mnt_hash, tree_list); + } else { + /* + * This can happen if the parent mount was bind mounted + * on some subdirectory of a shared/slave mount. + */ + list_add_tail(&child->mnt_hash, &tmp_list); + } + prev_dest_mnt = m; + prev_src_mnt = child; + } +out: + spin_lock(&vfsmount_lock); + while (!list_empty(&tmp_list)) { + child = list_entry(tmp_list.next, struct vfsmount, mnt_hash); + list_del_init(&child->mnt_hash); + umount_tree(child, &umount_list); + } + spin_unlock(&vfsmount_lock); + release_mounts(&umount_list); + return ret; +} diff --git a/fs/pnode.h b/fs/pnode.h index ab1bdaee4e08..c62c72fad212 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -12,7 +12,21 @@ #include #define IS_MNT_SHARED(mnt) (mnt->mnt_flags & MNT_SHARED) +#define IS_MNT_NEW(mnt) (!mnt->mnt_namespace) #define CLEAR_MNT_SHARED(mnt) (mnt->mnt_flags &= ~MNT_SHARED) +#define CL_EXPIRE 0x01 +#define CL_COPY_ALL 0x04 +#define CL_MAKE_SHARED 0x08 +#define CL_PROPAGATION 0x10 + +static inline void set_mnt_shared(struct vfsmount *mnt) +{ + mnt->mnt_flags &= ~MNT_PNODE_MASK; + mnt->mnt_flags |= MNT_SHARED; +} + void change_mnt_propagation(struct vfsmount *, int); +int propagate_mnt(struct vfsmount *, struct dentry *, struct vfsmount *, + struct list_head *); #endif /* _LINUX_PNODE_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 551fba303cf8..5e188b773e9c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1251,7 +1251,12 @@ extern int unregister_filesystem(struct file_system_type *); extern struct vfsmount *kern_mount(struct file_system_type *); extern int may_umount_tree(struct vfsmount *); extern int may_umount(struct vfsmount *); +extern void umount_tree(struct vfsmount *, struct list_head *); +extern void release_mounts(struct list_head *); extern long do_mount(char *, char *, char *, unsigned long, void *); +extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); +extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *, + struct vfsmount *); extern int vfs_statfs(struct super_block *, struct kstatfs *); -- cgit v1.2.3 From a05964f3917c7c55368c229d7985f8e7c9977e97 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 7 Nov 2005 17:20:17 -0500 Subject: [PATCH] shared mounts handling: umount An unmount of a mount creates a umount event on the parent. If the parent is a shared mount, it gets propagated to all mounts in the peer group. Signed-off-by: Ram Pai Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/namespace.c | 56 +++++++++++++++++++++----------- fs/pnode.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++- fs/pnode.h | 2 ++ include/linux/dcache.h | 1 + include/linux/fs.h | 2 +- 5 files changed, 128 insertions(+), 20 deletions(-) (limited to 'fs/pnode.c') diff --git a/fs/namespace.c b/fs/namespace.c index 1487982dbc24..4b1af01c2fb4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -86,31 +86,44 @@ void free_vfsmnt(struct vfsmount *mnt) } /* - * Now, lookup_mnt increments the ref count before returning - * the vfsmount struct. + * find the first or last mount at @dentry on vfsmount @mnt depending on + * @dir. If @dir is set return the first mount else return the last mount. */ -struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) +struct vfsmount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry, + int dir) { struct list_head *head = mount_hashtable + hash(mnt, dentry); struct list_head *tmp = head; struct vfsmount *p, *found = NULL; - spin_lock(&vfsmount_lock); for (;;) { - tmp = tmp->next; + tmp = dir ? tmp->next : tmp->prev; p = NULL; if (tmp == head) break; p = list_entry(tmp, struct vfsmount, mnt_hash); if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { - found = mntget(p); + found = p; break; } } - spin_unlock(&vfsmount_lock); return found; } +/* + * lookup_mnt increments the ref count before returning + * the vfsmount struct. + */ +struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry) +{ + struct vfsmount *child_mnt; + spin_lock(&vfsmount_lock); + if ((child_mnt = __lookup_mnt(mnt, dentry, 1))) + mntget(child_mnt); + spin_unlock(&vfsmount_lock); + return child_mnt; +} + static inline int check_mnt(struct vfsmount *mnt) { return mnt->mnt_namespace == current->namespace; @@ -404,9 +417,12 @@ EXPORT_SYMBOL(may_umount_tree); */ int may_umount(struct vfsmount *mnt) { - if (atomic_read(&mnt->mnt_count) > 2) - return -EBUSY; - return 0; + int ret = 0; + spin_lock(&vfsmount_lock); + if (propagate_mount_busy(mnt, 2)) + ret = -EBUSY; + spin_unlock(&vfsmount_lock); + return ret; } EXPORT_SYMBOL(may_umount); @@ -433,7 +449,7 @@ void release_mounts(struct list_head *head) } } -void umount_tree(struct vfsmount *mnt, struct list_head *kill) +void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) { struct vfsmount *p; @@ -442,6 +458,9 @@ void umount_tree(struct vfsmount *mnt, struct list_head *kill) list_add(&p->mnt_hash, kill); } + if (propagate) + propagate_umount(kill); + list_for_each_entry(p, kill, mnt_hash) { list_del_init(&p->mnt_expire); list_del_init(&p->mnt_list); @@ -450,6 +469,7 @@ void umount_tree(struct vfsmount *mnt, struct list_head *kill) list_del_init(&p->mnt_child); if (p->mnt_parent != p) mnt->mnt_mountpoint->d_mounted--; + change_mnt_propagation(p, MS_PRIVATE); } } @@ -526,9 +546,9 @@ static int do_umount(struct vfsmount *mnt, int flags) event++; retval = -EBUSY; - if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) { + if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) { if (!list_empty(&mnt->mnt_list)) - umount_tree(mnt, &umount_list); + umount_tree(mnt, 1, &umount_list); retval = 0; } spin_unlock(&vfsmount_lock); @@ -651,7 +671,7 @@ Enomem: if (res) { LIST_HEAD(umount_list); spin_lock(&vfsmount_lock); - umount_tree(res, &umount_list); + umount_tree(res, 0, &umount_list); spin_unlock(&vfsmount_lock); release_mounts(&umount_list); } @@ -827,7 +847,7 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse) if (err) { LIST_HEAD(umount_list); spin_lock(&vfsmount_lock); - umount_tree(mnt, &umount_list); + umount_tree(mnt, 0, &umount_list); spin_unlock(&vfsmount_lock); release_mounts(&umount_list); } @@ -1023,12 +1043,12 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts, * Check that it is still dead: the count should now be 2 - as * contributed by the vfsmount parent and the mntget above */ - if (atomic_read(&mnt->mnt_count) == 2) { + if (!propagate_mount_busy(mnt, 2)) { /* delete from the namespace */ touch_namespace(mnt->mnt_namespace); list_del_init(&mnt->mnt_list); mnt->mnt_namespace = NULL; - umount_tree(mnt, umounts); + umount_tree(mnt, 1, umounts); spin_unlock(&vfsmount_lock); } else { /* @@ -1647,7 +1667,7 @@ void __put_namespace(struct namespace *namespace) spin_unlock(&vfsmount_lock); down_write(&namespace_sem); spin_lock(&vfsmount_lock); - umount_tree(root, &umount_list); + umount_tree(root, 0, &umount_list); spin_unlock(&vfsmount_lock); up_write(&namespace_sem); release_mounts(&umount_list); diff --git a/fs/pnode.c b/fs/pnode.c index 2d572b88e6f6..7bc942d047cd 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -99,9 +99,94 @@ out: while (!list_empty(&tmp_list)) { child = list_entry(tmp_list.next, struct vfsmount, mnt_hash); list_del_init(&child->mnt_hash); - umount_tree(child, &umount_list); + umount_tree(child, 0, &umount_list); } spin_unlock(&vfsmount_lock); release_mounts(&umount_list); return ret; } + +/* + * return true if the refcount is greater than count + */ +static inline int do_refcount_check(struct vfsmount *mnt, int count) +{ + int mycount = atomic_read(&mnt->mnt_count); + return (mycount > count); +} + +/* + * check if the mount 'mnt' can be unmounted successfully. + * @mnt: the mount to be checked for unmount + * NOTE: unmounting 'mnt' would naturally propagate to all + * other mounts its parent propagates to. + * Check if any of these mounts that **do not have submounts** + * have more references than 'refcnt'. If so return busy. + */ +int propagate_mount_busy(struct vfsmount *mnt, int refcnt) +{ + struct vfsmount *m, *child; + struct vfsmount *parent = mnt->mnt_parent; + int ret = 0; + + if (mnt == parent) + return do_refcount_check(mnt, refcnt); + + /* + * quickly check if the current mount can be unmounted. + * If not, we don't have to go checking for all other + * mounts + */ + if (!list_empty(&mnt->mnt_mounts) || do_refcount_check(mnt, refcnt)) + return 1; + + for (m = propagation_next(parent, parent); m; + m = propagation_next(m, parent)) { + child = __lookup_mnt(m, mnt->mnt_mountpoint, 0); + if (child && list_empty(&child->mnt_mounts) && + (ret = do_refcount_check(child, 1))) + break; + } + return ret; +} + +/* + * NOTE: unmounting 'mnt' naturally propagates to all other mounts its + * parent propagates to. + */ +static void __propagate_umount(struct vfsmount *mnt) +{ + struct vfsmount *parent = mnt->mnt_parent; + struct vfsmount *m; + + BUG_ON(parent == mnt); + + for (m = propagation_next(parent, parent); m; + m = propagation_next(m, parent)) { + + struct vfsmount *child = __lookup_mnt(m, + mnt->mnt_mountpoint, 0); + /* + * umount the child only if the child has no + * other children + */ + if (child && list_empty(&child->mnt_mounts)) { + list_del(&child->mnt_hash); + list_add_tail(&child->mnt_hash, &mnt->mnt_hash); + } + } +} + +/* + * collect all mounts that receive propagation from the mount in @list, + * and return these additional mounts in the same list. + * @list: the list of mounts to be unmounted. + */ +int propagate_umount(struct list_head *list) +{ + struct vfsmount *mnt; + + list_for_each_entry(mnt, list, mnt_hash) + __propagate_umount(mnt); + return 0; +} diff --git a/fs/pnode.h b/fs/pnode.h index c62c72fad212..9b88ba06794a 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -29,4 +29,6 @@ static inline void set_mnt_shared(struct vfsmount *mnt) void change_mnt_propagation(struct vfsmount *, int); int propagate_mnt(struct vfsmount *, struct dentry *, struct vfsmount *, struct list_head *); +int propagate_umount(struct list_head *); +int propagate_mount_busy(struct vfsmount *, int); #endif /* _LINUX_PNODE_H */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index ab04b4f9b0db..46a2ba617595 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -329,6 +329,7 @@ static inline int d_mountpoint(struct dentry *dentry) } extern struct vfsmount *lookup_mnt(struct vfsmount *, struct dentry *); +extern struct vfsmount *__lookup_mnt(struct vfsmount *, struct dentry *, int); extern struct dentry *lookup_create(struct nameidata *nd, int is_dir); extern int sysctl_vfs_cache_pressure; diff --git a/include/linux/fs.h b/include/linux/fs.h index 5e188b773e9c..8bdb504be73b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1251,7 +1251,7 @@ extern int unregister_filesystem(struct file_system_type *); extern struct vfsmount *kern_mount(struct file_system_type *); extern int may_umount_tree(struct vfsmount *); extern int may_umount(struct vfsmount *); -extern void umount_tree(struct vfsmount *, struct list_head *); +extern void umount_tree(struct vfsmount *, int, struct list_head *); extern void release_mounts(struct list_head *); extern long do_mount(char *, char *, char *, unsigned long, void *); extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); -- cgit v1.2.3 From a58b0eb8e64b78d9315a5491955e78b1391d42e5 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 7 Nov 2005 17:20:48 -0500 Subject: [PATCH] introduce slave mounts A slave mount always has a master mount from which it receives mount/umount events. Unlike shared mount the event propagation does not flow from the slave mount to the master. Signed-off-by: Ram Pai Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/namespace.c | 4 +++- fs/pnode.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++--- fs/pnode.h | 2 ++ include/linux/fs.h | 1 + include/linux/mount.h | 3 +++ 5 files changed, 60 insertions(+), 4 deletions(-) (limited to 'fs/pnode.c') diff --git a/fs/namespace.c b/fs/namespace.c index 4b1af01c2fb4..46f99bc585bd 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -67,6 +67,8 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_list); INIT_LIST_HEAD(&mnt->mnt_expire); INIT_LIST_HEAD(&mnt->mnt_share); + INIT_LIST_HEAD(&mnt->mnt_slave_list); + INIT_LIST_HEAD(&mnt->mnt_slave); if (name) { int size = strlen(name) + 1; char *newname = kmalloc(size, GFP_KERNEL); @@ -1243,7 +1245,7 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, data_page); else if (flags & MS_BIND) retval = do_loopback(&nd, dev_name, flags & MS_REC); - else if (flags & (MS_SHARED | MS_PRIVATE)) + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) retval = do_change_type(&nd, flags); else if (flags & MS_MOVE) retval = do_move_mount(&nd, dev_name); diff --git a/fs/pnode.c b/fs/pnode.c index 7bc942d047cd..f73eba24f1d1 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -17,13 +17,61 @@ static inline struct vfsmount *next_peer(struct vfsmount *p) return list_entry(p->mnt_share.next, struct vfsmount, mnt_share); } +static int do_make_slave(struct vfsmount *mnt) +{ + struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; + struct vfsmount *slave_mnt; + + /* + * slave 'mnt' to a peer mount that has the + * same root dentry. If none is available than + * slave it to anything that is available. + */ + while ((peer_mnt = next_peer(peer_mnt)) != mnt && + peer_mnt->mnt_root != mnt->mnt_root) ; + + if (peer_mnt == mnt) { + peer_mnt = next_peer(mnt); + if (peer_mnt == mnt) + peer_mnt = NULL; + } + list_del_init(&mnt->mnt_share); + + if (peer_mnt) + master = peer_mnt; + + if (master) { + list_for_each_entry(slave_mnt, &mnt->mnt_slave_list, mnt_slave) + slave_mnt->mnt_master = master; + list_del(&mnt->mnt_slave); + list_add(&mnt->mnt_slave, &master->mnt_slave_list); + list_splice(&mnt->mnt_slave_list, master->mnt_slave_list.prev); + INIT_LIST_HEAD(&mnt->mnt_slave_list); + } else { + struct list_head *p = &mnt->mnt_slave_list; + while (!list_empty(p)) { + slave_mnt = list_entry(p->next, + struct vfsmount, mnt_slave); + list_del_init(&slave_mnt->mnt_slave); + slave_mnt->mnt_master = NULL; + } + } + mnt->mnt_master = master; + CLEAR_MNT_SHARED(mnt); + INIT_LIST_HEAD(&mnt->mnt_slave_list); + return 0; +} + void change_mnt_propagation(struct vfsmount *mnt, int type) { if (type == MS_SHARED) { set_mnt_shared(mnt); - } else { - list_del_init(&mnt->mnt_share); - mnt->mnt_flags &= ~MNT_PNODE_MASK; + return; + } + do_make_slave(mnt); + if (type != MS_SLAVE) { + list_del_init(&mnt->mnt_slave); + mnt->mnt_master = NULL; } } diff --git a/fs/pnode.h b/fs/pnode.h index 9b88ba06794a..b59f0e9fe6b1 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -12,10 +12,12 @@ #include #define IS_MNT_SHARED(mnt) (mnt->mnt_flags & MNT_SHARED) +#define IS_MNT_SLAVE(mnt) (mnt->mnt_master) #define IS_MNT_NEW(mnt) (!mnt->mnt_namespace) #define CLEAR_MNT_SHARED(mnt) (mnt->mnt_flags &= ~MNT_SHARED) #define CL_EXPIRE 0x01 +#define CL_SLAVE 0x02 #define CL_COPY_ALL 0x04 #define CL_MAKE_SHARED 0x08 #define CL_PROPAGATION 0x10 diff --git a/include/linux/fs.h b/include/linux/fs.h index 8bdb504be73b..eef66f54c017 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -105,6 +105,7 @@ extern int dir_notify_enable; #define MS_REC 16384 #define MS_VERBOSE 32768 #define MS_PRIVATE (1<<18) /* change to private */ +#define MS_SLAVE (1<<19) /* change to slave */ #define MS_SHARED (1<<20) /* change to shared */ #define MS_POSIXACL (1<<16) /* VFS does not apply the umask */ #define MS_ACTIVE (1<<30) diff --git a/include/linux/mount.h b/include/linux/mount.h index 2582559718fc..7e133ae2a94f 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -38,6 +38,9 @@ struct vfsmount { struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ struct list_head mnt_share; /* circular list of shared mounts */ + struct list_head mnt_slave_list;/* list of slave mounts */ + struct list_head mnt_slave; /* slave list entry */ + struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */ struct namespace *mnt_namespace; /* containing namespace */ int mnt_pinned; }; -- cgit v1.2.3 From 5afe00221389998a25d611dc7941c06580c29eb6 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 7 Nov 2005 17:21:01 -0500 Subject: [PATCH] handling of slave mounts This makes bind, rbind, move, clone namespace and umount operations aware of the semantics of slave mount (see Documentation/sharedsubtree.txt in the last patch of the series for detailed description). Signed-off-by: Ram Pai Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/namespace.c | 77 +++++++++++++++++++++++++++++++++++-------------------- fs/pnode.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 121 insertions(+), 37 deletions(-) (limited to 'fs/pnode.c') diff --git a/fs/namespace.c b/fs/namespace.c index 46f99bc585bd..089670363704 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -227,8 +227,17 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; - if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old)) - list_add(&mnt->mnt_share, &old->mnt_share); + if (flag & CL_SLAVE) { + list_add(&mnt->mnt_slave, &old->mnt_slave_list); + mnt->mnt_master = old; + CLEAR_MNT_SHARED(mnt); + } else { + if ((flag & CL_PROPAGATION) || 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); + mnt->mnt_master = old->mnt_master; + } if (flag & CL_MAKE_SHARED) set_mnt_shared(mnt); @@ -689,18 +698,18 @@ Enomem: * * NOTE: in the table below explains the semantics when a source mount * of a given type is attached to a destination mount of a given type. - * --------------------------------------------- - * | BIND MOUNT OPERATION | - * |******************************************** - * | source-->| shared | private | - * | dest | | | - * | | | | | - * | v | | | - * |******************************************** - * | shared | shared (++) | shared (+) | - * | | | | - * |non-shared| shared (+) | private | - * ********************************************* + * ------------------------------------------------------------- + * | BIND MOUNT OPERATION | + * |************************************************************* + * | source-->| shared | private | slave | + * | dest | | | | + * | | | | | | + * | v | | | | + * |************************************************************* + * | shared | shared (++) | shared (+) | shared(+++)| + * | | | | | + * |non-shared| shared (+) | private | slave (*) | + * ************************************************************** * A bind operation clones the source mount and mounts the clone on the * destination mount. * @@ -710,21 +719,33 @@ Enomem: * (+) the cloned mount is created under the destination mount and is marked * as shared. The cloned mount is added to the peer group of the source * mount. - * --------------------------------------------- - * | MOVE MOUNT OPERATION | - * |******************************************** - * | source-->| shared | private | - * | dest | | | - * | | | | | - * | v | | | - * |******************************************** - * | shared | shared (+) | shared (+) | - * | | | | - * |non-shared| shared (+*) | private | - * ********************************************* - * (+) the mount is moved to the destination. And is then propagated to all - * the mounts in the propagation tree of the destination mount. + * (+++) the mount is propagated to all the mounts in the propagation tree + * of the destination mount and the cloned mount is made slave + * of the same master as that of the source mount. The cloned mount + * is marked as 'shared and slave'. + * (*) the cloned mount is made a slave of the same master as that of the + * source mount. + * + * -------------------------------------------------------------- + * | MOVE MOUNT OPERATION | + * |************************************************************* + * | source-->| shared | private | slave | + * | dest | | | | + * | | | | | | + * | v | | | | + * |************************************************************* + * | shared | shared (+) | shared (+) | shared(+++) | + * | | | | | + * |non-shared| shared (+*) | private | slave (*) | + * ************************************************************** + * + * (+) the mount is moved to the destination. And is then propagated to + * all the mounts in the propagation tree of the destination mount. * (+*) the mount is moved to the destination. + * (+++) the mount is moved to the destination and is then propagated to + * all the mounts belonging to the destination mount's propagation tree. + * the mount is marked as 'shared and slave'. + * (*) the mount continues to be a slave at the new location. * * if the source mount is a tree, the operations explained above is * applied to each mount in the tree. diff --git a/fs/pnode.c b/fs/pnode.c index f73eba24f1d1..3e266c5a3071 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -17,6 +17,16 @@ static inline struct vfsmount *next_peer(struct vfsmount *p) return list_entry(p->mnt_share.next, struct vfsmount, mnt_share); } +static inline struct vfsmount *first_slave(struct vfsmount *p) +{ + return list_entry(p->mnt_slave_list.next, struct vfsmount, mnt_slave); +} + +static inline struct vfsmount *next_slave(struct vfsmount *p) +{ + return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave); +} + static int do_make_slave(struct vfsmount *mnt) { struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; @@ -83,10 +93,64 @@ void change_mnt_propagation(struct vfsmount *mnt, int type) static struct vfsmount *propagation_next(struct vfsmount *m, struct vfsmount *origin) { - m = next_peer(m); - if (m == origin) - return NULL; - return m; + /* are there any slaves of this mount? */ + if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list)) + return first_slave(m); + + while (1) { + struct vfsmount *next; + struct vfsmount *master = m->mnt_master; + + if ( master == origin->mnt_master ) { + next = next_peer(m); + return ((next == origin) ? NULL : next); + } else if (m->mnt_slave.next != &master->mnt_slave_list) + return next_slave(m); + + /* back at master */ + m = master; + } +} + +/* + * return the source mount to be used for cloning + * + * @dest the current destination mount + * @last_dest the last seen destination mount + * @last_src the last seen source mount + * @type return CL_SLAVE if the new mount has to be + * cloned as a slave. + */ +static struct vfsmount *get_source(struct vfsmount *dest, + struct vfsmount *last_dest, + struct vfsmount *last_src, + int *type) +{ + struct vfsmount *p_last_src = NULL; + struct vfsmount *p_last_dest = NULL; + *type = CL_PROPAGATION;; + + if (IS_MNT_SHARED(dest)) + *type |= CL_MAKE_SHARED; + + while (last_dest != dest->mnt_master) { + p_last_dest = last_dest; + p_last_src = last_src; + last_dest = last_dest->mnt_master; + last_src = last_src->mnt_master; + } + + if (p_last_dest) { + do { + p_last_dest = next_peer(p_last_dest); + } while (IS_MNT_NEW(p_last_dest)); + } + + if (dest != p_last_dest) { + *type |= CL_SLAVE; + return last_src; + } else + return p_last_src; } /* @@ -114,16 +178,15 @@ int propagate_mnt(struct vfsmount *dest_mnt, struct dentry *dest_dentry, for (m = propagation_next(dest_mnt, dest_mnt); m; m = propagation_next(m, dest_mnt)) { - int type = CL_PROPAGATION; + int type; + struct vfsmount *source; if (IS_MNT_NEW(m)) continue; - if (IS_MNT_SHARED(m)) - type |= CL_MAKE_SHARED; + source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); - if (!(child = copy_tree(source_mnt, source_mnt->mnt_root, - type))) { + if (!(child = copy_tree(source, source->mnt_root, type))) { ret = -ENOMEM; list_splice(tree_list, tmp_list.prev); goto out; -- cgit v1.2.3 From 9676f0c6389b62bd6b24d77d4b3abdbcfa32d0f2 Mon Sep 17 00:00:00 2001 From: Ram Pai Date: Mon, 7 Nov 2005 17:21:20 -0500 Subject: [PATCH] unbindable mounts An unbindable mount does not forward or receive propagation. Also unbindable mount disallows bind mounts. The semantics is as follows. Bind semantics: It is invalid to bind mount an unbindable mount. Move semantics: It is invalid to move an unbindable mount under shared mount. Clone-namespace semantics: If a mount is unbindable in the parent namespace, the corresponding cloned mount in the child namespace becomes unbindable too. Note: there is subtle difference, unbindable mounts cannot be bind mounted but can be cloned during clone-namespace. Signed-off-by: Ram Pai Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- fs/namespace.c | 88 ++++++++++++++++++++++++++++++++++++--------------- fs/pnode.c | 2 ++ fs/pnode.h | 1 + include/linux/fs.h | 1 + include/linux/mount.h | 1 + 5 files changed, 67 insertions(+), 26 deletions(-) (limited to 'fs/pnode.c') diff --git a/fs/namespace.c b/fs/namespace.c index 089670363704..caa9187f67e5 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -213,6 +213,16 @@ static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root) return list_entry(next, struct vfsmount, mnt_child); } +static struct vfsmount *skip_mnt_tree(struct vfsmount *p) +{ + struct list_head *prev = p->mnt_mounts.prev; + while (prev != &p->mnt_mounts) { + p = list_entry(prev, struct vfsmount, mnt_child); + prev = p->mnt_mounts.prev; + } + return p; +} + static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, int flag) { @@ -650,6 +660,9 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, struct vfsmount *res, *p, *q, *r, *s; struct nameidata nd; + if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt)) + return NULL; + res = q = clone_mnt(mnt, dentry, flag); if (!q) goto Enomem; @@ -661,6 +674,10 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry, continue; for (s = r; s; s = next_mnt(s, r)) { + if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) { + s = skip_mnt_tree(s); + continue; + } while (p != s->mnt_parent) { p = p->mnt_parent; q = q->mnt_parent; @@ -698,18 +715,18 @@ Enomem: * * NOTE: in the table below explains the semantics when a source mount * of a given type is attached to a destination mount of a given type. - * ------------------------------------------------------------- - * | BIND MOUNT OPERATION | - * |************************************************************* - * | source-->| shared | private | slave | - * | dest | | | | - * | | | | | | - * | v | | | | - * |************************************************************* - * | shared | shared (++) | shared (+) | shared(+++)| - * | | | | | - * |non-shared| shared (+) | private | slave (*) | - * ************************************************************** + * --------------------------------------------------------------------------- + * | BIND MOUNT OPERATION | + * |************************************************************************** + * | source-->| shared | private | slave | unbindable | + * | dest | | | | | + * | | | | | | | + * | v | | | | | + * |************************************************************************** + * | shared | shared (++) | shared (+) | shared(+++)| invalid | + * | | | | | | + * |non-shared| shared (+) | private | slave (*) | invalid | + * *************************************************************************** * A bind operation clones the source mount and mounts the clone on the * destination mount. * @@ -726,18 +743,18 @@ Enomem: * (*) the cloned mount is made a slave of the same master as that of the * source mount. * - * -------------------------------------------------------------- - * | MOVE MOUNT OPERATION | - * |************************************************************* - * | source-->| shared | private | slave | - * | dest | | | | - * | | | | | | - * | v | | | | - * |************************************************************* - * | shared | shared (+) | shared (+) | shared(+++) | - * | | | | | - * |non-shared| shared (+*) | private | slave (*) | - * ************************************************************** + * --------------------------------------------------------------------------- + * | MOVE MOUNT OPERATION | + * |************************************************************************** + * | source-->| shared | private | slave | unbindable | + * | dest | | | | | + * | | | | | | | + * | v | | | | | + * |************************************************************************** + * | shared | shared (+) | shared (+) | shared(+++) | invalid | + * | | | | | | + * |non-shared| shared (+*) | private | slave (*) | unbindable | + * *************************************************************************** * * (+) the mount is moved to the destination. And is then propagated to * all the mounts in the propagation tree of the destination mount. @@ -854,6 +871,9 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse) down_write(&namespace_sem); err = -EINVAL; + if (IS_MNT_UNBINDABLE(old_nd.mnt)) + goto out; + if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt)) goto out; @@ -911,6 +931,16 @@ static int do_remount(struct nameidata *nd, int flags, int mnt_flags, return err; } +static inline int tree_contains_unbindable(struct vfsmount *mnt) +{ + struct vfsmount *p; + for (p = mnt; p; p = next_mnt(p, mnt)) { + if (IS_MNT_UNBINDABLE(p)) + return 1; + } + return 0; +} + static int do_move_mount(struct nameidata *nd, char *old_name) { struct nameidata old_nd, parent_nd; @@ -954,6 +984,12 @@ static int do_move_mount(struct nameidata *nd, char *old_name) */ if (old_nd.mnt->mnt_parent && IS_MNT_SHARED(old_nd.mnt->mnt_parent)) goto out1; + /* + * Don't move a mount tree containing unbindable mounts to a destination + * mount which is shared. + */ + if (IS_MNT_SHARED(nd->mnt) && tree_contains_unbindable(old_nd.mnt)) + goto out1; err = -ELOOP; for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent) if (p == old_nd.mnt) @@ -1266,7 +1302,7 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, data_page); else if (flags & MS_BIND) retval = do_loopback(&nd, dev_name, flags & MS_REC); - else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) + else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) retval = do_change_type(&nd, flags); else if (flags & MS_MOVE) retval = do_move_mount(&nd, dev_name); @@ -1311,7 +1347,7 @@ int copy_namespace(int flags, struct task_struct *tsk) down_write(&namespace_sem); /* First pass: copy the tree topology */ new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root, - CL_EXPIRE); + CL_COPY_ALL | CL_EXPIRE); if (!new_ns->root) { up_write(&namespace_sem); kfree(new_ns); diff --git a/fs/pnode.c b/fs/pnode.c index 3e266c5a3071..aeeec8ba8dd2 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -82,6 +82,8 @@ void change_mnt_propagation(struct vfsmount *mnt, int type) if (type != MS_SLAVE) { list_del_init(&mnt->mnt_slave); mnt->mnt_master = NULL; + if (type == MS_UNBINDABLE) + mnt->mnt_flags |= MNT_UNBINDABLE; } } diff --git a/fs/pnode.h b/fs/pnode.h index b59f0e9fe6b1..020e1bb60fdb 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -15,6 +15,7 @@ #define IS_MNT_SLAVE(mnt) (mnt->mnt_master) #define IS_MNT_NEW(mnt) (!mnt->mnt_namespace) #define CLEAR_MNT_SHARED(mnt) (mnt->mnt_flags &= ~MNT_SHARED) +#define IS_MNT_UNBINDABLE(mnt) (mnt->mnt_flags & MNT_UNBINDABLE) #define CL_EXPIRE 0x01 #define CL_SLAVE 0x02 diff --git a/include/linux/fs.h b/include/linux/fs.h index eef66f54c017..1b5f502a4b8f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -104,6 +104,7 @@ extern int dir_notify_enable; #define MS_MOVE 8192 #define MS_REC 16384 #define MS_VERBOSE 32768 +#define MS_UNBINDABLE (1<<17) /* change to unbindable */ #define MS_PRIVATE (1<<18) /* change to private */ #define MS_SLAVE (1<<19) /* change to slave */ #define MS_SHARED (1<<20) /* change to shared */ diff --git a/include/linux/mount.h b/include/linux/mount.h index 7e133ae2a94f..dd4e83eba933 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -21,6 +21,7 @@ #define MNT_NODEV 0x02 #define MNT_NOEXEC 0x04 #define MNT_SHARED 0x10 /* if the vfsmount is a shared mount */ +#define MNT_UNBINDABLE 0x20 /* if the vfsmount is a unbindable mount */ #define MNT_PNODE_MASK 0x30 /* propogation flag mask */ struct vfsmount { -- cgit v1.2.3