Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 346316
b: refs/heads/master
c: 8823c07
h: refs/heads/master
v: v3
  • Loading branch information
Eric W. Biederman committed Nov 19, 2012
1 parent eac8aa8 commit 37ed14c
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: a85fb273c94648cbf20a5f9bcf8bbbb075f271ad
refs/heads/master: 8823c079ba7136dc1948d6f6dcb5f8022bde438e
1 change: 1 addition & 0 deletions trunk/fs/mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ struct mnt_namespace {
atomic_t count;
struct mount * root;
struct list_head list;
u64 seq; /* Sequence number to prevent loops */
wait_queue_head_t poll;
int event;
};
Expand Down
95 changes: 95 additions & 0 deletions trunk/fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/fs_struct.h> /* get_fs_root et.al. */
#include <linux/fsnotify.h> /* fsnotify_vfsmount_delete */
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include "pnode.h"
#include "internal.h"

Expand Down Expand Up @@ -1308,6 +1309,26 @@ static int mount_is_safe(struct path *path)
#endif
}

static bool mnt_ns_loop(struct path *path)
{
/* Could bind mounting the mount namespace inode cause a
* mount namespace loop?
*/
struct inode *inode = path->dentry->d_inode;
struct proc_inode *ei;
struct mnt_namespace *mnt_ns;

if (!proc_ns_inode(inode))
return false;

ei = PROC_I(inode);
if (ei->ns_ops != &mntns_operations)
return false;

mnt_ns = ei->ns;
return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
}

struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
int flag)
{
Expand Down Expand Up @@ -1655,6 +1676,10 @@ static int do_loopback(struct path *path, const char *old_name,
if (err)
return err;

err = -EINVAL;
if (mnt_ns_loop(&old_path))
goto out;

err = lock_mount(path);
if (err)
goto out;
Expand Down Expand Up @@ -2261,13 +2286,23 @@ long do_mount(const char *dev_name, const char *dir_name,
return retval;
}

/*
* Assign a sequence number so we can detect when we attempt to bind
* mount a reference to an older mount namespace into the current
* mount namespace, preventing reference counting loops. A 64bit
* number incrementing at 10Ghz will take 12,427 years to wrap which
* is effectively never, so we can ignore the possibility.
*/
static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1);

static struct mnt_namespace *alloc_mnt_ns(void)
{
struct mnt_namespace *new_ns;

new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
if (!new_ns)
return ERR_PTR(-ENOMEM);
new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);
atomic_set(&new_ns->count, 1);
new_ns->root = NULL;
INIT_LIST_HEAD(&new_ns->list);
Expand Down Expand Up @@ -2681,3 +2716,63 @@ bool our_mnt(struct vfsmount *mnt)
{
return check_mnt(real_mount(mnt));
}

static void *mntns_get(struct task_struct *task)
{
struct mnt_namespace *ns = NULL;
struct nsproxy *nsproxy;

rcu_read_lock();
nsproxy = task_nsproxy(task);
if (nsproxy) {
ns = nsproxy->mnt_ns;
get_mnt_ns(ns);
}
rcu_read_unlock();

return ns;
}

static void mntns_put(void *ns)
{
put_mnt_ns(ns);
}

static int mntns_install(struct nsproxy *nsproxy, void *ns)
{
struct fs_struct *fs = current->fs;
struct mnt_namespace *mnt_ns = ns;
struct path root;

if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_CHROOT))
return -EINVAL;

if (fs->users != 1)
return -EINVAL;

get_mnt_ns(mnt_ns);
put_mnt_ns(nsproxy->mnt_ns);
nsproxy->mnt_ns = mnt_ns;

/* Find the root */
root.mnt = &mnt_ns->root->mnt;
root.dentry = mnt_ns->root->mnt.mnt_root;
path_get(&root);
while(d_mountpoint(root.dentry) && follow_down_one(&root))
;

/* Update the pwd and root */
set_fs_pwd(fs, &root);
set_fs_root(fs, &root);

path_put(&root);
return 0;
}

const struct proc_ns_operations mntns_operations = {
.name = "mnt",
.type = CLONE_NEWNS,
.get = mntns_get,
.put = mntns_put,
.install = mntns_install,
};
5 changes: 5 additions & 0 deletions trunk/fs/proc/namespaces.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ static const struct proc_ns_operations *ns_entries[] = {
#ifdef CONFIG_PID_NS
&pidns_operations,
#endif
&mntns_operations,
};

static const struct file_operations ns_file_operations = {
Expand Down Expand Up @@ -201,3 +202,7 @@ struct file *proc_ns_fget(int fd)
return ERR_PTR(-EINVAL);
}

bool proc_ns_inode(struct inode *inode)
{
return inode->i_fop == &ns_file_operations;
}
7 changes: 7 additions & 0 deletions trunk/include/linux/proc_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ extern struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
struct proc_dir_entry *parent);

extern struct file *proc_ns_fget(int fd);
extern bool proc_ns_inode(struct inode *inode);

#else

Expand Down Expand Up @@ -229,6 +230,11 @@ static inline struct file *proc_ns_fget(int fd)
return ERR_PTR(-EINVAL);
}

static inline bool proc_ns_inode(struct inode *inode)
{
return false;
}

#endif /* CONFIG_PROC_FS */

#if !defined(CONFIG_PROC_KCORE)
Expand All @@ -252,6 +258,7 @@ extern const struct proc_ns_operations netns_operations;
extern const struct proc_ns_operations utsns_operations;
extern const struct proc_ns_operations ipcns_operations;
extern const struct proc_ns_operations pidns_operations;
extern const struct proc_ns_operations mntns_operations;

union proc_op {
int (*proc_get_link)(struct dentry *, struct path *);
Expand Down

0 comments on commit 37ed14c

Please sign in to comment.