Skip to content

Commit

Permalink
[PATCH] unbindable mounts
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ram Pai authored and Linus Torvalds committed Nov 8, 2005
1 parent 5afe002 commit 9676f0c
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 26 deletions.
88 changes: 62 additions & 26 deletions fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -698,18 +715,18 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
*
* 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.
*
Expand All @@ -726,18 +743,18 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
* (*) 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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions fs/pnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
1 change: 1 addition & 0 deletions fs/pnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions include/linux/mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 9676f0c

Please sign in to comment.