aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2012-06-25 07:55:18 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-07-14 08:37:27 -0400
commitbe34d1a3bc4b6f357a49acb55ae870c81337e4f0 (patch)
tree39caaf1b4965d47be64ce45491477fce555b2d17 /fs
parent55e4def0a6e79e7eb53017c4935adfed76510cd7 (diff)
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: David Howells <dhowells@redhat.com> Cc: Valerie Aurora <valerie.aurora@gmail.com> Cc: Andreas Gruenbacher <agruen@suse.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/namespace.c120
-rw-r--r--fs/pnode.c5
2 files changed, 68 insertions, 57 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 8f412abcb67f..be1b07a774f1 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -708,56 +708,60 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
708 int flag) 708 int flag)
709{ 709{
710 struct super_block *sb = old->mnt.mnt_sb; 710 struct super_block *sb = old->mnt.mnt_sb;
711 struct mount *mnt = alloc_vfsmnt(old->mnt_devname); 711 struct mount *mnt;
712 int err;
712 713
713 if (mnt) { 714 mnt = alloc_vfsmnt(old->mnt_devname);
714 if (flag & (CL_SLAVE | CL_PRIVATE)) 715 if (!mnt)
715 mnt->mnt_group_id = 0; /* not a peer of original */ 716 return ERR_PTR(-ENOMEM);
716 else
717 mnt->mnt_group_id = old->mnt_group_id;
718
719 if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
720 int err = mnt_alloc_group_id(mnt);
721 if (err)
722 goto out_free;
723 }
724 717
725 mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD; 718 if (flag & (CL_SLAVE | CL_PRIVATE))
726 atomic_inc(&sb->s_active); 719 mnt->mnt_group_id = 0; /* not a peer of original */
727 mnt->mnt.mnt_sb = sb; 720 else
728 mnt->mnt.mnt_root = dget(root); 721 mnt->mnt_group_id = old->mnt_group_id;
729 mnt->mnt_mountpoint = mnt->mnt.mnt_root;
730 mnt->mnt_parent = mnt;
731 br_write_lock(&vfsmount_lock);
732 list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
733 br_write_unlock(&vfsmount_lock);
734 722
735 if (flag & CL_SLAVE) { 723 if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
736 list_add(&mnt->mnt_slave, &old->mnt_slave_list); 724 err = mnt_alloc_group_id(mnt);
737 mnt->mnt_master = old; 725 if (err)
738 CLEAR_MNT_SHARED(mnt); 726 goto out_free;
739 } else if (!(flag & CL_PRIVATE)) { 727 }
740 if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old)) 728
741 list_add(&mnt->mnt_share, &old->mnt_share); 729 mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
742 if (IS_MNT_SLAVE(old)) 730 atomic_inc(&sb->s_active);
743 list_add(&mnt->mnt_slave, &old->mnt_slave); 731 mnt->mnt.mnt_sb = sb;
744 mnt->mnt_master = old->mnt_master; 732 mnt->mnt.mnt_root = dget(root);
745 } 733 mnt->mnt_mountpoint = mnt->mnt.mnt_root;
746 if (flag & CL_MAKE_SHARED) 734 mnt->mnt_parent = mnt;
747 set_mnt_shared(mnt); 735 br_write_lock(&vfsmount_lock);
748 736 list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
749 /* stick the duplicate mount on the same expiry list 737 br_write_unlock(&vfsmount_lock);
750 * as the original if that was on one */ 738
751 if (flag & CL_EXPIRE) { 739 if (flag & CL_SLAVE) {
752 if (!list_empty(&old->mnt_expire)) 740 list_add(&mnt->mnt_slave, &old->mnt_slave_list);
753 list_add(&mnt->mnt_expire, &old->mnt_expire); 741 mnt->mnt_master = old;
754 } 742 CLEAR_MNT_SHARED(mnt);
743 } else if (!(flag & CL_PRIVATE)) {
744 if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old))
745 list_add(&mnt->mnt_share, &old->mnt_share);
746 if (IS_MNT_SLAVE(old))
747 list_add(&mnt->mnt_slave, &old->mnt_slave);
748 mnt->mnt_master = old->mnt_master;
749 }
750 if (flag & CL_MAKE_SHARED)
751 set_mnt_shared(mnt);
752
753 /* stick the duplicate mount on the same expiry list
754 * as the original if that was on one */
755 if (flag & CL_EXPIRE) {
756 if (!list_empty(&old->mnt_expire))
757 list_add(&mnt->mnt_expire, &old->mnt_expire);
755 } 758 }
759
756 return mnt; 760 return mnt;
757 761
758 out_free: 762 out_free:
759 free_vfsmnt(mnt); 763 free_vfsmnt(mnt);
760 return NULL; 764 return ERR_PTR(err);
761} 765}
762 766
763static inline void mntfree(struct mount *mnt) 767static inline void mntfree(struct mount *mnt)
@@ -1242,11 +1246,12 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
1242 struct path path; 1246 struct path path;
1243 1247
1244 if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt)) 1248 if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
1245 return NULL; 1249 return ERR_PTR(-EINVAL);
1246 1250
1247 res = q = clone_mnt(mnt, dentry, flag); 1251 res = q = clone_mnt(mnt, dentry, flag);
1248 if (!q) 1252 if (IS_ERR(q))
1249 goto Enomem; 1253 return q;
1254
1250 q->mnt_mountpoint = mnt->mnt_mountpoint; 1255 q->mnt_mountpoint = mnt->mnt_mountpoint;
1251 1256
1252 p = mnt; 1257 p = mnt;
@@ -1268,8 +1273,8 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
1268 path.mnt = &q->mnt; 1273 path.mnt = &q->mnt;
1269 path.dentry = p->mnt_mountpoint; 1274 path.dentry = p->mnt_mountpoint;
1270 q = clone_mnt(p, p->mnt.mnt_root, flag); 1275 q = clone_mnt(p, p->mnt.mnt_root, flag);
1271 if (!q) 1276 if (IS_ERR(q))
1272 goto Enomem; 1277 goto out;
1273 br_write_lock(&vfsmount_lock); 1278 br_write_lock(&vfsmount_lock);
1274 list_add_tail(&q->mnt_list, &res->mnt_list); 1279 list_add_tail(&q->mnt_list, &res->mnt_list);
1275 attach_mnt(q, &path); 1280 attach_mnt(q, &path);
@@ -1277,7 +1282,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
1277 } 1282 }
1278 } 1283 }
1279 return res; 1284 return res;
1280Enomem: 1285out:
1281 if (res) { 1286 if (res) {
1282 LIST_HEAD(umount_list); 1287 LIST_HEAD(umount_list);
1283 br_write_lock(&vfsmount_lock); 1288 br_write_lock(&vfsmount_lock);
@@ -1285,9 +1290,11 @@ Enomem:
1285 br_write_unlock(&vfsmount_lock); 1290 br_write_unlock(&vfsmount_lock);
1286 release_mounts(&umount_list); 1291 release_mounts(&umount_list);
1287 } 1292 }
1288 return NULL; 1293 return q;
1289} 1294}
1290 1295
1296/* Caller should check returned pointer for errors */
1297
1291struct vfsmount *collect_mounts(struct path *path) 1298struct vfsmount *collect_mounts(struct path *path)
1292{ 1299{
1293 struct mount *tree; 1300 struct mount *tree;
@@ -1295,7 +1302,9 @@ struct vfsmount *collect_mounts(struct path *path)
1295 tree = copy_tree(real_mount(path->mnt), path->dentry, 1302 tree = copy_tree(real_mount(path->mnt), path->dentry,
1296 CL_COPY_ALL | CL_PRIVATE); 1303 CL_COPY_ALL | CL_PRIVATE);
1297 up_write(&namespace_sem); 1304 up_write(&namespace_sem);
1298 return tree ? &tree->mnt : NULL; 1305 if (IS_ERR(tree))
1306 return NULL;
1307 return &tree->mnt;
1299} 1308}
1300 1309
1301void drop_collected_mounts(struct vfsmount *mnt) 1310void drop_collected_mounts(struct vfsmount *mnt)
@@ -1590,14 +1599,15 @@ static int do_loopback(struct path *path, char *old_name,
1590 if (!check_mnt(real_mount(path->mnt)) || !check_mnt(old)) 1599 if (!check_mnt(real_mount(path->mnt)) || !check_mnt(old))
1591 goto out2; 1600 goto out2;
1592 1601
1593 err = -ENOMEM;
1594 if (recurse) 1602 if (recurse)
1595 mnt = copy_tree(old, old_path.dentry, 0); 1603 mnt = copy_tree(old, old_path.dentry, 0);
1596 else 1604 else
1597 mnt = clone_mnt(old, old_path.dentry, 0); 1605 mnt = clone_mnt(old, old_path.dentry, 0);
1598 1606
1599 if (!mnt) 1607 if (IS_ERR(mnt)) {
1600 goto out2; 1608 err = PTR_ERR(mnt);
1609 goto out;
1610 }
1601 1611
1602 err = graft_tree(mnt, path); 1612 err = graft_tree(mnt, path);
1603 if (err) { 1613 if (err) {
@@ -2211,10 +2221,10 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
2211 down_write(&namespace_sem); 2221 down_write(&namespace_sem);
2212 /* First pass: copy the tree topology */ 2222 /* First pass: copy the tree topology */
2213 new = copy_tree(old, old->mnt.mnt_root, CL_COPY_ALL | CL_EXPIRE); 2223 new = copy_tree(old, old->mnt.mnt_root, CL_COPY_ALL | CL_EXPIRE);
2214 if (!new) { 2224 if (IS_ERR(new)) {
2215 up_write(&namespace_sem); 2225 up_write(&namespace_sem);
2216 kfree(new_ns); 2226 kfree(new_ns);
2217 return ERR_PTR(-ENOMEM); 2227 return ERR_CAST(new);
2218 } 2228 }
2219 new_ns->root = new; 2229 new_ns->root = new;
2220 br_write_lock(&vfsmount_lock); 2230 br_write_lock(&vfsmount_lock);
diff --git a/fs/pnode.c b/fs/pnode.c
index bed378db0758..3e000a51ac0d 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -237,8 +237,9 @@ int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry,
237 237
238 source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); 238 source = get_source(m, prev_dest_mnt, prev_src_mnt, &type);
239 239
240 if (!(child = copy_tree(source, source->mnt.mnt_root, type))) { 240 child = copy_tree(source, source->mnt.mnt_root, type);
241 ret = -ENOMEM; 241 if (IS_ERR(child)) {
242 ret = PTR_ERR(child);
242 list_splice(tree_list, tmp_list.prev); 243 list_splice(tree_list, tmp_list.prev);
243 goto out; 244 goto out;
244 } 245 }