[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 <linuxram@us.ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Ram Pai 2005-11-07 17:21:20 -05:00 committed by Linus Torvalds
parent 5afe002213
commit 9676f0c638
5 changed files with 67 additions and 26 deletions

View File

@ -213,6 +213,16 @@ static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
return list_entry(next, struct vfsmount, mnt_child); 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, static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
int flag) int flag)
{ {
@ -650,6 +660,9 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
struct vfsmount *res, *p, *q, *r, *s; struct vfsmount *res, *p, *q, *r, *s;
struct nameidata nd; struct nameidata nd;
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
return NULL;
res = q = clone_mnt(mnt, dentry, flag); res = q = clone_mnt(mnt, dentry, flag);
if (!q) if (!q)
goto Enomem; goto Enomem;
@ -661,6 +674,10 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
continue; continue;
for (s = r; s; s = next_mnt(s, r)) { 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) { while (p != s->mnt_parent) {
p = p->mnt_parent; p = p->mnt_parent;
q = q->mnt_parent; q = q->mnt_parent;
@ -698,18 +715,18 @@ Enomem:
* *
* NOTE: in the table below explains the semantics when a source mount * 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. * of a given type is attached to a destination mount of a given type.
* ------------------------------------------------------------- * ---------------------------------------------------------------------------
* | BIND MOUNT OPERATION | * | BIND MOUNT OPERATION |
* |************************************************************* * |**************************************************************************
* | source-->| shared | private | slave | * | source-->| shared | private | slave | unbindable |
* | dest | | | | * | dest | | | | |
* | | | | | | * | | | | | | |
* | v | | | | * | v | | | | |
* |************************************************************* * |**************************************************************************
* | shared | shared (++) | shared (+) | shared(+++)| * | shared | shared (++) | shared (+) | shared(+++)| invalid |
* | | | | | * | | | | | |
* |non-shared| shared (+) | private | slave (*) | * |non-shared| shared (+) | private | slave (*) | invalid |
* ************************************************************** * ***************************************************************************
* A bind operation clones the source mount and mounts the clone on the * A bind operation clones the source mount and mounts the clone on the
* destination mount. * destination mount.
* *
@ -726,18 +743,18 @@ Enomem:
* (*) the cloned mount is made a slave of the same master as that of the * (*) the cloned mount is made a slave of the same master as that of the
* source mount. * source mount.
* *
* -------------------------------------------------------------- * ---------------------------------------------------------------------------
* | MOVE MOUNT OPERATION | * | MOVE MOUNT OPERATION |
* |************************************************************* * |**************************************************************************
* | source-->| shared | private | slave | * | source-->| shared | private | slave | unbindable |
* | dest | | | | * | dest | | | | |
* | | | | | | * | | | | | | |
* | v | | | | * | v | | | | |
* |************************************************************* * |**************************************************************************
* | shared | shared (+) | shared (+) | shared(+++) | * | shared | shared (+) | shared (+) | shared(+++) | invalid |
* | | | | | * | | | | | |
* |non-shared| shared (+*) | private | slave (*) | * |non-shared| shared (+*) | private | slave (*) | unbindable |
* ************************************************************** * ***************************************************************************
* *
* (+) the mount is moved to the destination. And is then propagated to * (+) the mount is moved to the destination. And is then propagated to
* all the mounts in the propagation tree of the destination mount. * 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); down_write(&namespace_sem);
err = -EINVAL; err = -EINVAL;
if (IS_MNT_UNBINDABLE(old_nd.mnt))
goto out;
if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt)) if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
goto out; goto out;
@ -911,6 +931,16 @@ static int do_remount(struct nameidata *nd, int flags, int mnt_flags,
return err; 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) static int do_move_mount(struct nameidata *nd, char *old_name)
{ {
struct nameidata old_nd, parent_nd; 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)) if (old_nd.mnt->mnt_parent && IS_MNT_SHARED(old_nd.mnt->mnt_parent))
goto out1; 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; err = -ELOOP;
for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent) for (p = nd->mnt; p->mnt_parent != p; p = p->mnt_parent)
if (p == old_nd.mnt) if (p == old_nd.mnt)
@ -1266,7 +1302,7 @@ long do_mount(char *dev_name, char *dir_name, char *type_page,
data_page); data_page);
else if (flags & MS_BIND) else if (flags & MS_BIND)
retval = do_loopback(&nd, dev_name, flags & MS_REC); 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); retval = do_change_type(&nd, flags);
else if (flags & MS_MOVE) else if (flags & MS_MOVE)
retval = do_move_mount(&nd, dev_name); retval = do_move_mount(&nd, dev_name);
@ -1311,7 +1347,7 @@ int copy_namespace(int flags, struct task_struct *tsk)
down_write(&namespace_sem); down_write(&namespace_sem);
/* First pass: copy the tree topology */ /* First pass: copy the tree topology */
new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root, new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root,
CL_EXPIRE); CL_COPY_ALL | CL_EXPIRE);
if (!new_ns->root) { if (!new_ns->root) {
up_write(&namespace_sem); up_write(&namespace_sem);
kfree(new_ns); kfree(new_ns);

View File

@ -82,6 +82,8 @@ void change_mnt_propagation(struct vfsmount *mnt, int type)
if (type != MS_SLAVE) { if (type != MS_SLAVE) {
list_del_init(&mnt->mnt_slave); list_del_init(&mnt->mnt_slave);
mnt->mnt_master = NULL; mnt->mnt_master = NULL;
if (type == MS_UNBINDABLE)
mnt->mnt_flags |= MNT_UNBINDABLE;
} }
} }

View File

@ -15,6 +15,7 @@
#define IS_MNT_SLAVE(mnt) (mnt->mnt_master) #define IS_MNT_SLAVE(mnt) (mnt->mnt_master)
#define IS_MNT_NEW(mnt) (!mnt->mnt_namespace) #define IS_MNT_NEW(mnt) (!mnt->mnt_namespace)
#define CLEAR_MNT_SHARED(mnt) (mnt->mnt_flags &= ~MNT_SHARED) #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_EXPIRE 0x01
#define CL_SLAVE 0x02 #define CL_SLAVE 0x02

View File

@ -104,6 +104,7 @@ extern int dir_notify_enable;
#define MS_MOVE 8192 #define MS_MOVE 8192
#define MS_REC 16384 #define MS_REC 16384
#define MS_VERBOSE 32768 #define MS_VERBOSE 32768
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
#define MS_PRIVATE (1<<18) /* change to private */ #define MS_PRIVATE (1<<18) /* change to private */
#define MS_SLAVE (1<<19) /* change to slave */ #define MS_SLAVE (1<<19) /* change to slave */
#define MS_SHARED (1<<20) /* change to shared */ #define MS_SHARED (1<<20) /* change to shared */

View File

@ -21,6 +21,7 @@
#define MNT_NODEV 0x02 #define MNT_NODEV 0x02
#define MNT_NOEXEC 0x04 #define MNT_NOEXEC 0x04
#define MNT_SHARED 0x10 /* if the vfsmount is a shared mount */ #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 */ #define MNT_PNODE_MASK 0x30 /* propogation flag mask */
struct vfsmount { struct vfsmount {