diff options
author | David Howells <dhowells@redhat.com> | 2012-06-25 07:55:18 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-07-14 08:37:27 -0400 |
commit | be34d1a3bc4b6f357a49acb55ae870c81337e4f0 (patch) | |
tree | 39caaf1b4965d47be64ce45491477fce555b2d17 /fs/namespace.c | |
parent | 55e4def0a6e79e7eb53017c4935adfed76510cd7 (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/namespace.c')
-rw-r--r-- | fs/namespace.c | 120 |
1 files changed, 65 insertions, 55 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 | ||
763 | static inline void mntfree(struct mount *mnt) | 767 | static 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; |
1280 | Enomem: | 1285 | out: |
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 | |||
1291 | struct vfsmount *collect_mounts(struct path *path) | 1298 | struct 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 | ||
1301 | void drop_collected_mounts(struct vfsmount *mnt) | 1310 | void 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); |