2007-08-08 10:10:38

by Bharata B Rao

[permalink] [raw]
Subject: Re: [RFC 18/26] union-mount: Changes to the namespace handling

On Mon, Jul 30, 2007 at 06:13:41PM +0200, Jan Blunck wrote:
> Creates the proper struct union_mount when mounting something into a
> union. If the topmost filesystem isn't capable of handling the white-out
> filetype it could only be mount read-only.
>

Jan,

I think it is important to allow pivot_root of union mount points. Here
is an attempt to achieve that.

From: Bharata B Rao <[email protected]>

Allow pivot_root to work with union mount points.

If the current root filesystem is a union, then allow pivot_root
only if it's last component is a root, which allows it to be detached as a
complete union. Similarly if the new root filesystem is a union,
it's last component should be a root, so that it can be completely detached
from it's current mount point as a union and mounted back as root filesystem.

Signed-off-by: Bharata B Rao <[email protected]>
---
fs/namespace.c | 17 +++++++++++++++--
fs/union.c | 12 ++++++++++++
include/linux/union.h | 2 ++
3 files changed, 29 insertions(+), 2 deletions(-)

--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -171,6 +171,13 @@ static void detach_mnt(struct vfsmount *
old_nd->dentry->d_mounted--;
}

+static void detach_last_mnt(struct nameidata *nd, struct nameidata *old_nd)
+{
+ if (IS_MNT_UNION(nd->mnt))
+ while (follow_union_down(&nd->mnt, &nd->dentry));
+ detach_mnt(nd->mnt, old_nd);
+}
+
void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
struct vfsmount *child_mnt)
{
@@ -1878,6 +1885,9 @@ asmlinkage long sys_pivot_root(const cha
if (!check_mnt(new_nd.mnt))
goto out1;

+ if (IS_MNT_UNION(new_nd.mnt) && !last_union_is_root(&new_nd))
+ goto out1;
+
error = __user_walk(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_nd);
if (error)
goto out1;
@@ -1901,6 +1911,9 @@ asmlinkage long sys_pivot_root(const cha
goto out2;
if (!check_mnt(user_nd.mnt))
goto out2;
+ if (IS_MNT_UNION(user_nd.mnt) && !last_union_is_root(&user_nd))
+ goto out2;
+
error = -ENOENT;
if (IS_DEADDIR(new_nd.dentry->d_inode))
goto out2;
@@ -1934,8 +1947,8 @@ asmlinkage long sys_pivot_root(const cha
goto out3;
} else if (!is_subdir(old_nd.dentry, new_nd.dentry))
goto out3;
- detach_mnt(new_nd.mnt, &parent_nd);
- detach_mnt(user_nd.mnt, &root_parent);
+ detach_last_mnt(&new_nd, &parent_nd);
+ detach_last_mnt(&user_nd, &root_parent);
attach_mnt(user_nd.mnt, &old_nd); /* mount old root on put_old */
attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */
touch_mnt_namespace(current->nsproxy->mnt_ns);
--- a/fs/union.c
+++ b/fs/union.c
@@ -507,6 +507,18 @@ void detach_mnt_union(struct vfsmount *m
return;
}

+/*
+ * last_union_is_root - Check if the last component of the union stack
+ * is a root.
+ */
+int last_union_is_root(struct nameidata *nd)
+{
+ struct vfsmount *mnt = nd->mnt;
+ struct dentry *dentry = nd->dentry;
+
+ while (follow_union_down(&mnt, &dentry));
+ return IS_ROOT(dentry) ? 1: 0;
+}

/*
* Union mounts support for readdir.
--- a/include/linux/union.h
+++ b/include/linux/union.h
@@ -53,6 +53,7 @@ extern void __shrink_d_unions(struct den
extern int attach_mnt_union(struct vfsmount *, struct vfsmount *,
struct dentry *);
extern void detach_mnt_union(struct vfsmount *);
+extern int last_union_is_root(struct nameidata *nd);
extern int readdir_union(struct file *, void *, filldir_t);
extern int is_dir_unioned(struct path *);
extern int union_relookup_topmost(struct nameidata *, int);
@@ -74,6 +75,7 @@ extern int union_copyup(struct nameidata
#define __shrink_d_unions(x) do { } while (0)
#define attach_mnt_union(x, y, z) do { } while (0)
#define detach_mnt_union(x) do { } while (0)
+#define last_union_is_root(x) ({ (0); })
#define union_relookup_topmost(x, y) ({ BUG(); (0); })
#define union_create_topmost(x, y, z) ({ BUG(); (NULL); })
#define __union_copyup(x, y, z) ({ BUG(); (0); })

This applies on top of your patchset plus my subsequent fixes [1] and
[2].

[1] http://lkml.org/lkml/2007/8/6/10.
[2] http://lkml.org/lkml/2007/8/6/114.

Regards,
Bharata.