Commit be34d1a3 authored by David Howells's avatar David Howells Committed by Al Viro

VFS: Make clone_mnt()/copy_tree()/collect_mounts() return errors

copy_tree() can theoretically fail in a case other than ENOMEM, but always
returns NULL which is interpreted by callers as -ENOMEM.  Change it to return
an explicit error.

Also change clone_mnt() for consistency and because union mounts will add new
error cases.

Thanks to Andreas Gruenbacher <agruen@suse.de> for a bug fix.
[AV: folded braino fix by Dan Carpenter]

Original-author: Valerie Aurora <vaurora@redhat.com>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: Valerie Aurora <valerie.aurora@gmail.com>
Cc: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 55e4def0
...@@ -708,56 +708,60 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, ...@@ -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) { mnt = alloc_vfsmnt(old->mnt_devname);
if (flag & (CL_SLAVE | CL_PRIVATE)) if (!mnt)
mnt->mnt_group_id = 0; /* not a peer of original */ return ERR_PTR(-ENOMEM);
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->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD; if (flag & (CL_SLAVE | CL_PRIVATE))
atomic_inc(&sb->s_active); mnt->mnt_group_id = 0; /* not a peer of original */
mnt->mnt.mnt_sb = sb; else
mnt->mnt.mnt_root = dget(root); mnt->mnt_group_id = old->mnt_group_id;
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) { if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
list_add(&mnt->mnt_slave, &old->mnt_slave_list); err = mnt_alloc_group_id(mnt);
mnt->mnt_master = old; if (err)
CLEAR_MNT_SHARED(mnt); goto out_free;
} else if (!(flag & CL_PRIVATE)) { }
if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old))
list_add(&mnt->mnt_share, &old->mnt_share); mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
if (IS_MNT_SLAVE(old)) atomic_inc(&sb->s_active);
list_add(&mnt->mnt_slave, &old->mnt_slave); mnt->mnt.mnt_sb = sb;
mnt->mnt_master = old->mnt_master; mnt->mnt.mnt_root = dget(root);
} mnt->mnt_mountpoint = mnt->mnt.mnt_root;
if (flag & CL_MAKE_SHARED) mnt->mnt_parent = mnt;
set_mnt_shared(mnt); br_write_lock(&vfsmount_lock);
list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
/* stick the duplicate mount on the same expiry list br_write_unlock(&vfsmount_lock);
* as the original if that was on one */
if (flag & CL_EXPIRE) { if (flag & CL_SLAVE) {
if (!list_empty(&old->mnt_expire)) list_add(&mnt->mnt_slave, &old->mnt_slave_list);
list_add(&mnt->mnt_expire, &old->mnt_expire); 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, ...@@ -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) if (IS_ERR(q))
goto Enomem; 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, ...@@ -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) if (IS_ERR(q))
goto Enomem; 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, ...@@ -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: ...@@ -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) ...@@ -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, ...@@ -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) if (IS_ERR(mnt)) {
goto out2; 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, ...@@ -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);
......
...@@ -237,8 +237,9 @@ int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry, ...@@ -237,8 +237,9 @@ int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry,
source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); source = get_source(m, prev_dest_mnt, prev_src_mnt, &type);
if (!(child = copy_tree(source, source->mnt.mnt_root, type))) { child = copy_tree(source, source->mnt.mnt_root, type);
ret = -ENOMEM; if (IS_ERR(child)) {
ret = PTR_ERR(child);
list_splice(tree_list, tmp_list.prev); list_splice(tree_list, tmp_list.prev);
goto out; goto out;
} }
......
...@@ -595,7 +595,7 @@ void audit_trim_trees(void) ...@@ -595,7 +595,7 @@ void audit_trim_trees(void)
root_mnt = collect_mounts(&path); root_mnt = collect_mounts(&path);
path_put(&path); path_put(&path);
if (!root_mnt) if (IS_ERR(root_mnt))
goto skip_it; goto skip_it;
spin_lock(&hash_lock); spin_lock(&hash_lock);
...@@ -669,8 +669,8 @@ int audit_add_tree_rule(struct audit_krule *rule) ...@@ -669,8 +669,8 @@ int audit_add_tree_rule(struct audit_krule *rule)
goto Err; goto Err;
mnt = collect_mounts(&path); mnt = collect_mounts(&path);
path_put(&path); path_put(&path);
if (!mnt) { if (IS_ERR(mnt)) {
err = -ENOMEM; err = PTR_ERR(mnt);
goto Err; goto Err;
} }
...@@ -719,8 +719,8 @@ int audit_tag_tree(char *old, char *new) ...@@ -719,8 +719,8 @@ int audit_tag_tree(char *old, char *new)
return err; return err;
tagged = collect_mounts(&path2); tagged = collect_mounts(&path2);
path_put(&path2); path_put(&path2);
if (!tagged) if (IS_ERR(tagged))
return -ENOMEM; return PTR_ERR(tagged);
err = kern_path(old, 0, &path1); err = kern_path(old, 0, &path1);
if (err) { if (err) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment