|
@@ -708,56 +708,60 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
|
int flag)
|
|
int flag)
|
|
{
|
|
{
|
|
struct super_block *sb = old->mnt.mnt_sb;
|
|
struct super_block *sb = old->mnt.mnt_sb;
|
|
- struct mount *mnt = alloc_vfsmnt(old->mnt_devname);
|
|
|
|
|
|
+ struct mount *mnt;
|
|
|
|
+ int err;
|
|
|
|
|
|
- if (mnt) {
|
|
|
|
- if (flag & (CL_SLAVE | CL_PRIVATE))
|
|
|
|
- mnt->mnt_group_id = 0; /* not a peer of original */
|
|
|
|
- else
|
|
|
|
- mnt->mnt_group_id = old->mnt_group_id;
|
|
|
|
-
|
|
|
|
- if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
|
|
|
|
- int err = mnt_alloc_group_id(mnt);
|
|
|
|
- if (err)
|
|
|
|
- goto out_free;
|
|
|
|
- }
|
|
|
|
|
|
+ mnt = alloc_vfsmnt(old->mnt_devname);
|
|
|
|
+ if (!mnt)
|
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
|
|
|
|
- atomic_inc(&sb->s_active);
|
|
|
|
- mnt->mnt.mnt_sb = sb;
|
|
|
|
- mnt->mnt.mnt_root = dget(root);
|
|
|
|
- mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
|
|
|
- mnt->mnt_parent = mnt;
|
|
|
|
- br_write_lock(&vfsmount_lock);
|
|
|
|
- list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
|
|
|
|
- br_write_unlock(&vfsmount_lock);
|
|
|
|
|
|
+ if (flag & (CL_SLAVE | CL_PRIVATE))
|
|
|
|
+ mnt->mnt_group_id = 0; /* not a peer of original */
|
|
|
|
+ else
|
|
|
|
+ mnt->mnt_group_id = old->mnt_group_id;
|
|
|
|
|
|
- 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_PRIVATE)) {
|
|
|
|
- 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);
|
|
|
|
- mnt->mnt_master = old->mnt_master;
|
|
|
|
- }
|
|
|
|
- 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 */
|
|
|
|
- if (flag & CL_EXPIRE) {
|
|
|
|
- if (!list_empty(&old->mnt_expire))
|
|
|
|
- list_add(&mnt->mnt_expire, &old->mnt_expire);
|
|
|
|
- }
|
|
|
|
|
|
+ if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
|
|
|
|
+ err = mnt_alloc_group_id(mnt);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
|
|
|
|
+ atomic_inc(&sb->s_active);
|
|
|
|
+ mnt->mnt.mnt_sb = sb;
|
|
|
|
+ mnt->mnt.mnt_root = dget(root);
|
|
|
|
+ mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
|
|
|
+ mnt->mnt_parent = mnt;
|
|
|
|
+ br_write_lock(&vfsmount_lock);
|
|
|
|
+ list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
|
|
|
|
+ br_write_unlock(&vfsmount_lock);
|
|
|
|
+
|
|
|
|
+ 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_PRIVATE)) {
|
|
|
|
+ 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);
|
|
|
|
+ mnt->mnt_master = old->mnt_master;
|
|
|
|
+ }
|
|
|
|
+ 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 */
|
|
|
|
+ if (flag & CL_EXPIRE) {
|
|
|
|
+ if (!list_empty(&old->mnt_expire))
|
|
|
|
+ list_add(&mnt->mnt_expire, &old->mnt_expire);
|
|
}
|
|
}
|
|
|
|
+
|
|
return mnt;
|
|
return mnt;
|
|
|
|
|
|
out_free:
|
|
out_free:
|
|
free_vfsmnt(mnt);
|
|
free_vfsmnt(mnt);
|
|
- return NULL;
|
|
|
|
|
|
+ return ERR_PTR(err);
|
|
}
|
|
}
|
|
|
|
|
|
static inline void mntfree(struct mount *mnt)
|
|
static inline void mntfree(struct mount *mnt)
|
|
@@ -1242,11 +1246,12 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
|
struct path path;
|
|
struct path path;
|
|
|
|
|
|
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
|
|
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
|
|
- return NULL;
|
|
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
|
|
res = q = clone_mnt(mnt, dentry, flag);
|
|
res = q = clone_mnt(mnt, dentry, flag);
|
|
- if (!q)
|
|
|
|
- goto Enomem;
|
|
|
|
|
|
+ if (IS_ERR(q))
|
|
|
|
+ return q;
|
|
|
|
+
|
|
q->mnt_mountpoint = mnt->mnt_mountpoint;
|
|
q->mnt_mountpoint = mnt->mnt_mountpoint;
|
|
|
|
|
|
p = mnt;
|
|
p = mnt;
|
|
@@ -1268,8 +1273,8 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
|
path.mnt = &q->mnt;
|
|
path.mnt = &q->mnt;
|
|
path.dentry = p->mnt_mountpoint;
|
|
path.dentry = p->mnt_mountpoint;
|
|
q = clone_mnt(p, p->mnt.mnt_root, flag);
|
|
q = clone_mnt(p, p->mnt.mnt_root, flag);
|
|
- if (!q)
|
|
|
|
- goto Enomem;
|
|
|
|
|
|
+ if (IS_ERR(q))
|
|
|
|
+ goto out;
|
|
br_write_lock(&vfsmount_lock);
|
|
br_write_lock(&vfsmount_lock);
|
|
list_add_tail(&q->mnt_list, &res->mnt_list);
|
|
list_add_tail(&q->mnt_list, &res->mnt_list);
|
|
attach_mnt(q, &path);
|
|
attach_mnt(q, &path);
|
|
@@ -1277,7 +1282,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
return res;
|
|
-Enomem:
|
|
|
|
|
|
+out:
|
|
if (res) {
|
|
if (res) {
|
|
LIST_HEAD(umount_list);
|
|
LIST_HEAD(umount_list);
|
|
br_write_lock(&vfsmount_lock);
|
|
br_write_lock(&vfsmount_lock);
|
|
@@ -1285,9 +1290,11 @@ Enomem:
|
|
br_write_unlock(&vfsmount_lock);
|
|
br_write_unlock(&vfsmount_lock);
|
|
release_mounts(&umount_list);
|
|
release_mounts(&umount_list);
|
|
}
|
|
}
|
|
- return NULL;
|
|
|
|
|
|
+ return q;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Caller should check returned pointer for errors */
|
|
|
|
+
|
|
struct vfsmount *collect_mounts(struct path *path)
|
|
struct vfsmount *collect_mounts(struct path *path)
|
|
{
|
|
{
|
|
struct mount *tree;
|
|
struct mount *tree;
|
|
@@ -1295,7 +1302,9 @@ struct vfsmount *collect_mounts(struct path *path)
|
|
tree = copy_tree(real_mount(path->mnt), path->dentry,
|
|
tree = copy_tree(real_mount(path->mnt), path->dentry,
|
|
CL_COPY_ALL | CL_PRIVATE);
|
|
CL_COPY_ALL | CL_PRIVATE);
|
|
up_write(&namespace_sem);
|
|
up_write(&namespace_sem);
|
|
- return tree ? &tree->mnt : NULL;
|
|
|
|
|
|
+ if (IS_ERR(tree))
|
|
|
|
+ return NULL;
|
|
|
|
+ return &tree->mnt;
|
|
}
|
|
}
|
|
|
|
|
|
void drop_collected_mounts(struct vfsmount *mnt)
|
|
void drop_collected_mounts(struct vfsmount *mnt)
|
|
@@ -1590,14 +1599,15 @@ static int do_loopback(struct path *path, char *old_name,
|
|
if (!check_mnt(real_mount(path->mnt)) || !check_mnt(old))
|
|
if (!check_mnt(real_mount(path->mnt)) || !check_mnt(old))
|
|
goto out2;
|
|
goto out2;
|
|
|
|
|
|
- err = -ENOMEM;
|
|
|
|
if (recurse)
|
|
if (recurse)
|
|
mnt = copy_tree(old, old_path.dentry, 0);
|
|
mnt = copy_tree(old, old_path.dentry, 0);
|
|
else
|
|
else
|
|
mnt = clone_mnt(old, old_path.dentry, 0);
|
|
mnt = clone_mnt(old, old_path.dentry, 0);
|
|
|
|
|
|
- if (!mnt)
|
|
|
|
- goto out2;
|
|
|
|
|
|
+ if (IS_ERR(mnt)) {
|
|
|
|
+ err = PTR_ERR(mnt);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
|
|
err = graft_tree(mnt, path);
|
|
err = graft_tree(mnt, path);
|
|
if (err) {
|
|
if (err) {
|
|
@@ -2211,10 +2221,10 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
|
|
down_write(&namespace_sem);
|
|
down_write(&namespace_sem);
|
|
/* First pass: copy the tree topology */
|
|
/* First pass: copy the tree topology */
|
|
new = copy_tree(old, old->mnt.mnt_root, CL_COPY_ALL | CL_EXPIRE);
|
|
new = copy_tree(old, old->mnt.mnt_root, CL_COPY_ALL | CL_EXPIRE);
|
|
- if (!new) {
|
|
|
|
|
|
+ if (IS_ERR(new)) {
|
|
up_write(&namespace_sem);
|
|
up_write(&namespace_sem);
|
|
kfree(new_ns);
|
|
kfree(new_ns);
|
|
- return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
+ return ERR_CAST(new);
|
|
}
|
|
}
|
|
new_ns->root = new;
|
|
new_ns->root = new;
|
|
br_write_lock(&vfsmount_lock);
|
|
br_write_lock(&vfsmount_lock);
|