diff options
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 24960626bb6b..d287e7e74644 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/fs_struct.h> /* get_fs_root et.al. */ | 20 | #include <linux/fs_struct.h> /* get_fs_root et.al. */ |
21 | #include <linux/fsnotify.h> /* fsnotify_vfsmount_delete */ | 21 | #include <linux/fsnotify.h> /* fsnotify_vfsmount_delete */ |
22 | #include <linux/uaccess.h> | 22 | #include <linux/uaccess.h> |
23 | #include <linux/proc_fs.h> | ||
23 | #include "pnode.h" | 24 | #include "pnode.h" |
24 | #include "internal.h" | 25 | #include "internal.h" |
25 | 26 | ||
@@ -1308,6 +1309,26 @@ static int mount_is_safe(struct path *path) | |||
1308 | #endif | 1309 | #endif |
1309 | } | 1310 | } |
1310 | 1311 | ||
1312 | static bool mnt_ns_loop(struct path *path) | ||
1313 | { | ||
1314 | /* Could bind mounting the mount namespace inode cause a | ||
1315 | * mount namespace loop? | ||
1316 | */ | ||
1317 | struct inode *inode = path->dentry->d_inode; | ||
1318 | struct proc_inode *ei; | ||
1319 | struct mnt_namespace *mnt_ns; | ||
1320 | |||
1321 | if (!proc_ns_inode(inode)) | ||
1322 | return false; | ||
1323 | |||
1324 | ei = PROC_I(inode); | ||
1325 | if (ei->ns_ops != &mntns_operations) | ||
1326 | return false; | ||
1327 | |||
1328 | mnt_ns = ei->ns; | ||
1329 | return current->nsproxy->mnt_ns->seq >= mnt_ns->seq; | ||
1330 | } | ||
1331 | |||
1311 | struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, | 1332 | struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, |
1312 | int flag) | 1333 | int flag) |
1313 | { | 1334 | { |
@@ -1655,6 +1676,10 @@ static int do_loopback(struct path *path, const char *old_name, | |||
1655 | if (err) | 1676 | if (err) |
1656 | return err; | 1677 | return err; |
1657 | 1678 | ||
1679 | err = -EINVAL; | ||
1680 | if (mnt_ns_loop(&old_path)) | ||
1681 | goto out; | ||
1682 | |||
1658 | err = lock_mount(path); | 1683 | err = lock_mount(path); |
1659 | if (err) | 1684 | if (err) |
1660 | goto out; | 1685 | goto out; |
@@ -2261,6 +2286,15 @@ dput_out: | |||
2261 | return retval; | 2286 | return retval; |
2262 | } | 2287 | } |
2263 | 2288 | ||
2289 | /* | ||
2290 | * Assign a sequence number so we can detect when we attempt to bind | ||
2291 | * mount a reference to an older mount namespace into the current | ||
2292 | * mount namespace, preventing reference counting loops. A 64bit | ||
2293 | * number incrementing at 10Ghz will take 12,427 years to wrap which | ||
2294 | * is effectively never, so we can ignore the possibility. | ||
2295 | */ | ||
2296 | static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1); | ||
2297 | |||
2264 | static struct mnt_namespace *alloc_mnt_ns(void) | 2298 | static struct mnt_namespace *alloc_mnt_ns(void) |
2265 | { | 2299 | { |
2266 | struct mnt_namespace *new_ns; | 2300 | struct mnt_namespace *new_ns; |
@@ -2268,6 +2302,7 @@ static struct mnt_namespace *alloc_mnt_ns(void) | |||
2268 | new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); | 2302 | new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL); |
2269 | if (!new_ns) | 2303 | if (!new_ns) |
2270 | return ERR_PTR(-ENOMEM); | 2304 | return ERR_PTR(-ENOMEM); |
2305 | new_ns->seq = atomic64_add_return(1, &mnt_ns_seq); | ||
2271 | atomic_set(&new_ns->count, 1); | 2306 | atomic_set(&new_ns->count, 1); |
2272 | new_ns->root = NULL; | 2307 | new_ns->root = NULL; |
2273 | INIT_LIST_HEAD(&new_ns->list); | 2308 | INIT_LIST_HEAD(&new_ns->list); |
@@ -2681,3 +2716,63 @@ bool our_mnt(struct vfsmount *mnt) | |||
2681 | { | 2716 | { |
2682 | return check_mnt(real_mount(mnt)); | 2717 | return check_mnt(real_mount(mnt)); |
2683 | } | 2718 | } |
2719 | |||
2720 | static void *mntns_get(struct task_struct *task) | ||
2721 | { | ||
2722 | struct mnt_namespace *ns = NULL; | ||
2723 | struct nsproxy *nsproxy; | ||
2724 | |||
2725 | rcu_read_lock(); | ||
2726 | nsproxy = task_nsproxy(task); | ||
2727 | if (nsproxy) { | ||
2728 | ns = nsproxy->mnt_ns; | ||
2729 | get_mnt_ns(ns); | ||
2730 | } | ||
2731 | rcu_read_unlock(); | ||
2732 | |||
2733 | return ns; | ||
2734 | } | ||
2735 | |||
2736 | static void mntns_put(void *ns) | ||
2737 | { | ||
2738 | put_mnt_ns(ns); | ||
2739 | } | ||
2740 | |||
2741 | static int mntns_install(struct nsproxy *nsproxy, void *ns) | ||
2742 | { | ||
2743 | struct fs_struct *fs = current->fs; | ||
2744 | struct mnt_namespace *mnt_ns = ns; | ||
2745 | struct path root; | ||
2746 | |||
2747 | if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_CHROOT)) | ||
2748 | return -EINVAL; | ||
2749 | |||
2750 | if (fs->users != 1) | ||
2751 | return -EINVAL; | ||
2752 | |||
2753 | get_mnt_ns(mnt_ns); | ||
2754 | put_mnt_ns(nsproxy->mnt_ns); | ||
2755 | nsproxy->mnt_ns = mnt_ns; | ||
2756 | |||
2757 | /* Find the root */ | ||
2758 | root.mnt = &mnt_ns->root->mnt; | ||
2759 | root.dentry = mnt_ns->root->mnt.mnt_root; | ||
2760 | path_get(&root); | ||
2761 | while(d_mountpoint(root.dentry) && follow_down_one(&root)) | ||
2762 | ; | ||
2763 | |||
2764 | /* Update the pwd and root */ | ||
2765 | set_fs_pwd(fs, &root); | ||
2766 | set_fs_root(fs, &root); | ||
2767 | |||
2768 | path_put(&root); | ||
2769 | return 0; | ||
2770 | } | ||
2771 | |||
2772 | const struct proc_ns_operations mntns_operations = { | ||
2773 | .name = "mnt", | ||
2774 | .type = CLONE_NEWNS, | ||
2775 | .get = mntns_get, | ||
2776 | .put = mntns_put, | ||
2777 | .install = mntns_install, | ||
2778 | }; | ||