|
@@ -1355,14 +1355,11 @@ SYSCALL_DEFINE1(oldumount, char __user *, name)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
-static bool mnt_ns_loop(struct path *path)
|
|
|
+static bool is_mnt_ns_file(struct dentry *dentry)
|
|
|
{
|
|
|
- /* Could bind mounting the mount namespace inode cause a
|
|
|
- * mount namespace loop?
|
|
|
- */
|
|
|
- struct inode *inode = path->dentry->d_inode;
|
|
|
+ /* Is this a proxy for a mount namespace? */
|
|
|
+ struct inode *inode = dentry->d_inode;
|
|
|
struct proc_ns *ei;
|
|
|
- struct mnt_namespace *mnt_ns;
|
|
|
|
|
|
if (!proc_ns_inode(inode))
|
|
|
return false;
|
|
@@ -1371,7 +1368,19 @@ static bool mnt_ns_loop(struct path *path)
|
|
|
if (ei->ns_ops != &mntns_operations)
|
|
|
return false;
|
|
|
|
|
|
- mnt_ns = ei->ns;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static bool mnt_ns_loop(struct dentry *dentry)
|
|
|
+{
|
|
|
+ /* Could bind mounting the mount namespace inode cause a
|
|
|
+ * mount namespace loop?
|
|
|
+ */
|
|
|
+ struct mnt_namespace *mnt_ns;
|
|
|
+ if (!is_mnt_ns_file(dentry))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ mnt_ns = get_proc_ns(dentry->d_inode)->ns;
|
|
|
return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
|
|
|
}
|
|
|
|
|
@@ -1380,7 +1389,10 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
|
|
{
|
|
|
struct mount *res, *p, *q, *r, *parent;
|
|
|
|
|
|
- if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
|
|
|
+ if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(mnt))
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ if (!(flag & CL_COPY_MNT_NS_FILE) && is_mnt_ns_file(dentry))
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
res = q = clone_mnt(mnt, dentry, flag);
|
|
@@ -1397,7 +1409,13 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
|
|
continue;
|
|
|
|
|
|
for (s = r; s; s = next_mnt(s, r)) {
|
|
|
- if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
|
|
|
+ if (!(flag & CL_COPY_UNBINDABLE) &&
|
|
|
+ IS_MNT_UNBINDABLE(s)) {
|
|
|
+ s = skip_mnt_tree(s);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!(flag & CL_COPY_MNT_NS_FILE) &&
|
|
|
+ is_mnt_ns_file(s->mnt.mnt_root)) {
|
|
|
s = skip_mnt_tree(s);
|
|
|
continue;
|
|
|
}
|
|
@@ -1733,7 +1751,7 @@ static int do_loopback(struct path *path, const char *old_name,
|
|
|
return err;
|
|
|
|
|
|
err = -EINVAL;
|
|
|
- if (mnt_ns_loop(&old_path))
|
|
|
+ if (mnt_ns_loop(old_path.dentry))
|
|
|
goto out;
|
|
|
|
|
|
mp = lock_mount(path);
|
|
@@ -1755,7 +1773,7 @@ static int do_loopback(struct path *path, const char *old_name,
|
|
|
goto out2;
|
|
|
|
|
|
if (recurse)
|
|
|
- mnt = copy_tree(old, old_path.dentry, 0);
|
|
|
+ mnt = copy_tree(old, old_path.dentry, CL_COPY_MNT_NS_FILE);
|
|
|
else
|
|
|
mnt = clone_mnt(old, old_path.dentry, 0);
|
|
|
|
|
@@ -2417,7 +2435,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
|
|
|
|
|
|
namespace_lock();
|
|
|
/* First pass: copy the tree topology */
|
|
|
- copy_flags = CL_COPY_ALL | CL_EXPIRE;
|
|
|
+ copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
|
|
|
if (user_ns != mnt_ns->user_ns)
|
|
|
copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
|
|
|
new = copy_tree(old, old->mnt.mnt_root, copy_flags);
|
|
@@ -2452,6 +2470,10 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
|
|
|
}
|
|
|
p = next_mnt(p, old);
|
|
|
q = next_mnt(q, new);
|
|
|
+ if (!q)
|
|
|
+ break;
|
|
|
+ while (p->mnt.mnt_root != q->mnt.mnt_root)
|
|
|
+ p = next_mnt(p, old);
|
|
|
}
|
|
|
namespace_unlock();
|
|
|
|