Merge "proc: Allow proc_free_inum to be called from any context"

This commit is contained in:
Linux Build Service Account
2015-09-12 10:23:47 -07:00
committed by Gerrit - the friendly Code Review server
24 changed files with 481 additions and 82 deletions
+3
View File
@@ -4,8 +4,11 @@
struct mnt_namespace {
atomic_t count;
unsigned int proc_inum;
struct mount * root;
struct list_head list;
struct user_namespace *user_ns;
u64 seq; /* Sequence number to prevent loops */
wait_queue_head_t poll;
int event;
};
+176 -36
View File
@@ -12,6 +12,7 @@
#include <linux/export.h>
#include <linux/capability.h>
#include <linux/mnt_namespace.h>
#include <linux/user_namespace.h>
#include <linux/namei.h>
#include <linux/security.h>
#include <linux/idr.h>
@@ -20,6 +21,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"
@@ -728,7 +730,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
struct mount *mnt = alloc_vfsmnt(old->mnt_devname);
if (mnt) {
if (flag & (CL_SLAVE | CL_PRIVATE))
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
mnt->mnt_group_id = 0; /* not a peer of original */
else
mnt->mnt_group_id = old->mnt_group_id;
@@ -749,7 +751,8 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
br_write_unlock(vfsmount_lock);
if (flag & CL_SLAVE) {
if ((flag & CL_SLAVE) ||
((flag & CL_SHARED_TO_SLAVE) && IS_MNT_SHARED(old))) {
list_add(&mnt->mnt_slave, &old->mnt_slave_list);
mnt->mnt_master = old;
CLEAR_MNT_SHARED(mnt);
@@ -1209,7 +1212,7 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
goto dput_and_out;
retval = -EPERM;
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(mnt->mnt_ns->user_ns, CAP_SYS_ADMIN))
goto dput_and_out;
retval = do_umount(mnt, flags);
@@ -1235,7 +1238,7 @@ SYSCALL_DEFINE1(oldumount, char __user *, name)
static int mount_is_safe(struct path *path)
{
if (capable(CAP_SYS_ADMIN))
if (ns_capable(real_mount(path->mnt)->mnt_ns->user_ns, CAP_SYS_ADMIN))
return 0;
return -EPERM;
#ifdef notyet
@@ -1251,6 +1254,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)
{
@@ -1548,7 +1571,7 @@ static int do_change_type(struct path *path, int flag)
int type;
int err = 0;
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(mnt->mnt_ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
if (path->dentry != path->mnt->mnt_root)
@@ -1593,6 +1616,10 @@ static int do_loopback(struct path *path, 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;
@@ -1707,7 +1734,7 @@ static int do_move_mount(struct path *path, char *old_name)
struct mount *p;
struct mount *old;
int err = 0;
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(real_mount(path->mnt)->mnt_ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
if (!old_name || !*old_name)
return -EINVAL;
@@ -1794,21 +1821,6 @@ static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
return ERR_PTR(err);
}
static struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
struct file_system_type *type = get_fs_type(fstype);
struct vfsmount *mnt;
if (!type)
return ERR_PTR(-ENODEV);
mnt = vfs_kern_mount(type, flags, name, data);
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
mnt = fs_set_subtype(mnt, fstype);
put_filesystem(type);
return mnt;
}
/*
* add a mount into a namespace's mount tree
*/
@@ -1848,20 +1860,46 @@ unlock:
* create a new mount for userspace and request it to be added into the
* namespace's tree
*/
static int do_new_mount(struct path *path, char *type, int flags,
static int do_new_mount(struct path *path, const char *fstype, int flags,
int mnt_flags, char *name, void *data)
{
struct file_system_type *type;
struct user_namespace *user_ns;
struct vfsmount *mnt;
int err;
if (!type)
if (!fstype)
return -EINVAL;
/* we need capabilities... */
if (!capable(CAP_SYS_ADMIN))
user_ns = real_mount(path->mnt)->mnt_ns->user_ns;
if (!ns_capable(user_ns, CAP_SYS_ADMIN))
return -EPERM;
mnt = do_kern_mount(type, flags, name, data);
type = get_fs_type(fstype);
if (!type)
return -ENODEV;
if (user_ns != &init_user_ns) {
if (!(type->fs_flags & FS_USERNS_MOUNT)) {
put_filesystem(type);
return -EPERM;
}
/* Only in special cases allow devices from mounts
* created outside the initial user namespace.
*/
if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) {
flags |= MS_NODEV;
mnt_flags |= MNT_NODEV;
}
}
mnt = vfs_kern_mount(type, flags, name, data);
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
mnt = fs_set_subtype(mnt, fstype);
put_filesystem(type);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
@@ -2192,18 +2230,43 @@ dput_out:
return retval;
}
static struct mnt_namespace *alloc_mnt_ns(void)
static void free_mnt_ns(struct mnt_namespace *ns)
{
proc_free_inum(ns->proc_inum);
put_user_ns(ns->user_ns);
kfree(ns);
}
/*
* 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(struct user_namespace *user_ns)
{
struct mnt_namespace *new_ns;
int ret;
new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
if (!new_ns)
return ERR_PTR(-ENOMEM);
ret = proc_alloc_inum(&new_ns->proc_inum);
if (ret) {
kfree(new_ns);
return ERR_PTR(ret);
}
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);
init_waitqueue_head(&new_ns->poll);
new_ns->event = 0;
new_ns->user_ns = get_user_ns(user_ns);
return new_ns;
}
@@ -2229,24 +2292,28 @@ void mnt_make_shortterm(struct vfsmount *m)
* copied from the namespace of the passed in task structure.
*/
static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
struct fs_struct *fs)
struct user_namespace *user_ns, struct fs_struct *fs)
{
struct mnt_namespace *new_ns;
struct vfsmount *rootmnt = NULL, *pwdmnt = NULL;
struct mount *p, *q;
struct mount *old = mnt_ns->root;
struct mount *new;
int copy_flags;
new_ns = alloc_mnt_ns();
new_ns = alloc_mnt_ns(user_ns);
if (IS_ERR(new_ns))
return new_ns;
down_write(&namespace_sem);
/* First pass: copy the tree topology */
new = copy_tree(old, old->mnt.mnt_root, CL_COPY_ALL | CL_EXPIRE);
copy_flags = CL_COPY_ALL | CL_EXPIRE;
if (user_ns != mnt_ns->user_ns)
copy_flags |= CL_SHARED_TO_SLAVE;
new = copy_tree(old, old->mnt.mnt_root, copy_flags);
if (!new) {
up_write(&namespace_sem);
kfree(new_ns);
free_mnt_ns(new_ns);
return ERR_PTR(-ENOMEM);
}
new_ns->root = new;
@@ -2292,7 +2359,7 @@ static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
}
struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
struct fs_struct *new_fs)
struct user_namespace *user_ns, struct fs_struct *new_fs)
{
struct mnt_namespace *new_ns;
@@ -2302,7 +2369,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
if (!(flags & CLONE_NEWNS))
return ns;
new_ns = dup_mnt_ns(ns, new_fs);
new_ns = dup_mnt_ns(ns, user_ns, new_fs);
put_mnt_ns(ns);
return new_ns;
@@ -2314,7 +2381,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
*/
static struct mnt_namespace *create_mnt_ns(struct vfsmount *m)
{
struct mnt_namespace *new_ns = alloc_mnt_ns();
struct mnt_namespace *new_ns = alloc_mnt_ns(&init_user_ns);
if (!IS_ERR(new_ns)) {
struct mount *mnt = real_mount(m);
mnt->mnt_ns = new_ns;
@@ -2455,7 +2522,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
struct mount *new_mnt, *root_mnt;
int error;
if (!capable(CAP_SYS_ADMIN))
if (!ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
error = user_path_dir(new_root, &new);
@@ -2537,8 +2604,13 @@ static void __init init_mount_tree(void)
struct vfsmount *mnt;
struct mnt_namespace *ns;
struct path root;
struct file_system_type *type;
mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
type = get_fs_type("rootfs");
if (!type)
panic("Can't find rootfs type");
mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
put_filesystem(type);
if (IS_ERR(mnt))
panic("Can't create rootfs");
@@ -2601,7 +2673,7 @@ void put_mnt_ns(struct mnt_namespace *ns)
br_write_unlock(vfsmount_lock);
up_write(&namespace_sem);
release_mounts(&umount_list);
kfree(ns);
free_mnt_ns(ns);
}
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
@@ -2633,3 +2705,71 @@ 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 (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
!nsown_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;
}
static unsigned int mntns_inum(void *ns)
{
struct mnt_namespace *mnt_ns = ns;
return mnt_ns->proc_inum;
}
const struct proc_ns_operations mntns_operations = {
.name = "mnt",
.type = CLONE_NEWNS,
.get = mntns_get,
.put = mntns_put,
.install = mntns_install,
.inum = mntns_inum,
};
+1
View File
@@ -22,6 +22,7 @@
#define CL_COPY_ALL 0x04
#define CL_MAKE_SHARED 0x08
#define CL_PRIVATE 0x10
#define CL_SHARED_TO_SLAVE 0x20
static inline void set_mnt_shared(struct mount *mnt)
{
+20 -19
View File
@@ -350,37 +350,39 @@ static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */
* Return an inode number between PROC_DYNAMIC_FIRST and
* 0xffffffff, or zero on failure.
*/
static unsigned int get_inode_number(void)
int proc_alloc_inum(unsigned int *inum)
{
unsigned int i;
int error;
retry:
if (ida_pre_get(&proc_inum_ida, GFP_KERNEL) == 0)
return 0;
if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL))
return -ENOMEM;
spin_lock(&proc_inum_lock);
spin_lock_irq(&proc_inum_lock);
error = ida_get_new(&proc_inum_ida, &i);
spin_unlock(&proc_inum_lock);
spin_unlock_irq(&proc_inum_lock);
if (error == -EAGAIN)
goto retry;
else if (error)
return 0;
return error;
if (i > UINT_MAX - PROC_DYNAMIC_FIRST) {
spin_lock(&proc_inum_lock);
spin_lock_irq(&proc_inum_lock);
ida_remove(&proc_inum_ida, i);
spin_unlock(&proc_inum_lock);
return 0;
spin_unlock_irq(&proc_inum_lock);
return -ENOSPC;
}
return PROC_DYNAMIC_FIRST + i;
*inum = PROC_DYNAMIC_FIRST + i;
return 0;
}
static void release_inode_number(unsigned int inum)
void proc_free_inum(unsigned int inum)
{
spin_lock(&proc_inum_lock);
unsigned long flags;
spin_lock_irqsave(&proc_inum_lock, flags);
ida_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
spin_unlock(&proc_inum_lock);
spin_unlock_irqrestore(&proc_inum_lock, flags);
}
static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
@@ -554,13 +556,12 @@ static const struct inode_operations proc_dir_inode_operations = {
static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
{
unsigned int i;
struct proc_dir_entry *tmp;
int ret;
i = get_inode_number();
if (i == 0)
return -EAGAIN;
dp->low_ino = i;
ret = proc_alloc_inum(&dp->low_ino);
if (ret)
return ret;
if (S_ISDIR(dp->mode)) {
if (dp->proc_iops == NULL) {
@@ -765,7 +766,7 @@ EXPORT_SYMBOL(proc_create_data);
static void free_proc_entry(struct proc_dir_entry *de)
{
release_inode_number(de->low_ino);
proc_free_inum(de->low_ino);
if (S_ISLNK(de->mode))
kfree(de->data);
+4 -2
View File
@@ -31,6 +31,7 @@ static void proc_evict_inode(struct inode *inode)
struct proc_dir_entry *de;
struct ctl_table_header *head;
const struct proc_ns_operations *ns_ops;
void *ns;
truncate_inode_pages(&inode->i_data, 0);
end_writeback(inode);
@@ -49,8 +50,9 @@ static void proc_evict_inode(struct inode *inode)
}
/* Release any associated namespace */
ns_ops = PROC_I(inode)->ns_ops;
if (ns_ops && ns_ops->put)
ns_ops->put(PROC_I(inode)->ns);
ns = PROC_I(inode)->ns;
if (ns_ops && ns)
ns_ops->put(ns);
}
static struct kmem_cache * proc_inode_cachep;
+160 -21
View File
@@ -24,12 +24,165 @@ static const struct proc_ns_operations *ns_entries[] = {
#ifdef CONFIG_IPC_NS
&ipcns_operations,
#endif
#ifdef CONFIG_PID_NS
&pidns_operations,
#endif
&mntns_operations,
};
static const struct file_operations ns_file_operations = {
.llseek = no_llseek,
};
static const struct inode_operations ns_inode_operations = {
.setattr = proc_setattr,
};
static int ns_delete_dentry(const struct dentry *dentry)
{
/* Don't cache namespace inodes when not in use */
return 1;
}
static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
{
struct inode *inode = dentry->d_inode;
const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]",
ns_ops->name, inode->i_ino);
}
const struct dentry_operations ns_dentry_operations =
{
.d_delete = ns_delete_dentry,
.d_dname = ns_dname,
};
static struct dentry *proc_ns_get_dentry(struct super_block *sb,
struct task_struct *task, const struct proc_ns_operations *ns_ops)
{
struct dentry *dentry, *result;
struct inode *inode;
struct proc_inode *ei;
struct qstr qname = { .name = "", };
void *ns;
ns = ns_ops->get(task);
if (!ns)
return ERR_PTR(-ENOENT);
dentry = d_alloc_pseudo(sb, &qname);
if (!dentry) {
ns_ops->put(ns);
return ERR_PTR(-ENOMEM);
}
inode = iget_locked(sb, ns_ops->inum(ns));
if (!inode) {
dput(dentry);
ns_ops->put(ns);
return ERR_PTR(-ENOMEM);
}
ei = PROC_I(inode);
if (inode->i_state & I_NEW) {
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_op = &ns_inode_operations;
inode->i_mode = S_IFREG | S_IRUGO;
inode->i_fop = &ns_file_operations;
ei->ns_ops = ns_ops;
ei->ns = ns;
unlock_new_inode(inode);
} else {
ns_ops->put(ns);
}
d_set_d_op(dentry, &ns_dentry_operations);
result = d_instantiate_unique(dentry, inode);
if (result) {
dput(dentry);
dentry = result;
}
return dentry;
}
static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct inode *inode = dentry->d_inode;
struct super_block *sb = inode->i_sb;
struct proc_inode *ei = PROC_I(inode);
struct task_struct *task;
struct dentry *ns_dentry;
void *error = ERR_PTR(-EACCES);
task = get_proc_task(inode);
if (!task)
goto out;
if (!ptrace_may_access(task, PTRACE_MODE_READ))
goto out_put_task;
ns_dentry = proc_ns_get_dentry(sb, task, ei->ns_ops);
if (IS_ERR(ns_dentry)) {
error = ERR_CAST(ns_dentry);
goto out_put_task;
}
dput(nd->path.dentry);
nd->path.dentry = ns_dentry;
error = NULL;
out_put_task:
put_task_struct(task);
out:
return error;
}
static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
struct inode *inode = dentry->d_inode;
struct proc_inode *ei = PROC_I(inode);
const struct proc_ns_operations *ns_ops = ei->ns_ops;
struct task_struct *task;
void *ns;
char name[50];
int len = -EACCES;
task = get_proc_task(inode);
if (!task)
goto out;
if (!ptrace_may_access(task, PTRACE_MODE_READ))
goto out_put_task;
len = -ENOENT;
ns = ns_ops->get(task);
if (!ns)
goto out_put_task;
snprintf(name, sizeof(name), "%s:[%u]", ns_ops->name, ns_ops->inum(ns));
len = strlen(name);
if (len > buflen)
len = buflen;
if (copy_to_user(buffer, name, len))
len = -EFAULT;
ns_ops->put(ns);
out_put_task:
put_task_struct(task);
out:
return len;
}
static const struct inode_operations proc_ns_link_inode_operations = {
.readlink = proc_ns_readlink,
.follow_link = proc_ns_follow_link,
.setattr = proc_setattr,
};
static struct dentry *proc_ns_instantiate(struct inode *dir,
struct dentry *dentry, struct task_struct *task, const void *ptr)
{
@@ -37,21 +190,15 @@ static struct dentry *proc_ns_instantiate(struct inode *dir,
struct inode *inode;
struct proc_inode *ei;
struct dentry *error = ERR_PTR(-ENOENT);
void *ns;
inode = proc_pid_make_inode(dir->i_sb, task);
if (!inode)
goto out;
ns = ns_ops->get(task);
if (!ns)
goto out_iput;
ei = PROC_I(inode);
inode->i_mode = S_IFREG|S_IRUSR;
inode->i_fop = &ns_file_operations;
ei->ns_ops = ns_ops;
ei->ns = ns;
inode->i_mode = S_IFLNK|S_IRWXUGO;
inode->i_op = &proc_ns_link_inode_operations;
ei->ns_ops = ns_ops;
d_set_d_op(dentry, &pid_dentry_operations);
d_add(dentry, inode);
@@ -60,9 +207,6 @@ static struct dentry *proc_ns_instantiate(struct inode *dir,
error = NULL;
out:
return error;
out_iput:
iput(inode);
goto out;
}
static int proc_ns_fill_cache(struct file *filp, void *dirent,
@@ -89,10 +233,6 @@ static int proc_ns_dir_readdir(struct file *filp, void *dirent,
if (!task)
goto out_no_task;
ret = -EPERM;
if (!ptrace_may_access(task, PTRACE_MODE_READ))
goto out;
ret = 0;
i = filp->f_pos;
switch (i) {
@@ -152,10 +292,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
if (!task)
goto out_no_task;
error = ERR_PTR(-EPERM);
if (!ptrace_may_access(task, PTRACE_MODE_READ))
goto out;
last = &ns_entries[ARRAY_SIZE(ns_entries)];
for (entry = ns_entries; entry < last; entry++) {
if (strlen((*entry)->name) != len)
@@ -163,7 +299,6 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
if (!memcmp(dentry->d_name.name, (*entry)->name, len))
break;
}
error = ERR_PTR(-ENOENT);
if (entry == last)
goto out;
@@ -198,3 +333,7 @@ out_invalid:
return ERR_PTR(-EINVAL);
}
bool proc_ns_inode(struct inode *inode)
{
return inode->i_fop == &ns_file_operations;
}
+7
View File
@@ -1836,6 +1836,13 @@ int sync_inode_metadata(struct inode *inode, int wait);
struct file_system_type {
const char *name;
int fs_flags;
#define FS_REQUIRES_DEV 1
#define FS_BINARY_MOUNTDATA 2
#define FS_HAS_SUBTYPE 4
#define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */
#define FS_USERNS_DEV_MOUNT 16 /* A userns mount does not imply MNT_NODEV */
#define FS_REVAL_DOT 16384 /* Check the paths ".", ".." for staleness */
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
void (*kill_sb) (struct super_block *);
+2
View File
@@ -65,6 +65,8 @@ struct ipc_namespace {
/* user_ns which owns the ipc ns */
struct user_namespace *user_ns;
unsigned int proc_inum;
};
extern struct ipc_namespace init_ipc_ns;
+2 -1
View File
@@ -4,9 +4,10 @@
struct mnt_namespace;
struct fs_struct;
struct user_namespace;
extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
struct fs_struct *);
struct user_namespace *, struct fs_struct *);
extern void put_mnt_ns(struct mnt_namespace *ns);
extern const struct file_operations proc_mounts_operations;
+1
View File
@@ -34,6 +34,7 @@ struct pid_namespace {
gid_t pid_gid;
int hide_pid;
int reboot; /* group exit code if this pidns was rebooted */
unsigned int proc_inum;
};
extern struct pid_namespace init_pid_ns;
+24 -1
View File
@@ -28,7 +28,11 @@ struct mm_struct;
*/
enum {
PROC_ROOT_INO = 1,
PROC_ROOT_INO = 1,
PROC_IPC_INIT_INO = 0xEFFFFFFFU,
PROC_UTS_INIT_INO = 0xEFFFFFFEU,
PROC_USER_INIT_INO = 0xEFFFFFFDU,
PROC_PID_INIT_INO = 0xEFFFFFFCU,
};
/*
@@ -174,7 +178,10 @@ 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);
extern int proc_alloc_inum(unsigned int *pino);
extern void proc_free_inum(unsigned int inum);
#else
#define proc_net_fops_create(net, name, mode, fops) ({ (void)(mode), NULL; })
@@ -229,6 +236,19 @@ static inline struct file *proc_ns_fget(int fd)
return ERR_PTR(-EINVAL);
}
static inline bool proc_ns_inode(struct inode *inode)
{
return false;
}
static inline int proc_alloc_inum(unsigned int *inum)
{
*inum = 1;
return 0;
}
static inline void proc_free_inum(unsigned int inum)
{
}
#endif /* CONFIG_PROC_FS */
#if !defined(CONFIG_PROC_KCORE)
@@ -247,10 +267,13 @@ struct proc_ns_operations {
void *(*get)(struct task_struct *task);
void (*put)(void *ns);
int (*install)(struct nsproxy *nsproxy, void *ns);
unsigned int (*inum)(void *ns);
};
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 *);
+1
View File
@@ -14,6 +14,7 @@ struct user_namespace {
struct hlist_head uidhash_table[UIDHASH_SZ];
struct user_struct *creator;
struct work_struct destroyer;
unsigned int proc_inum;
};
extern struct user_namespace init_user_ns;
+1
View File
@@ -52,6 +52,7 @@ struct uts_namespace {
struct kref kref;
struct new_utsname name;
struct user_namespace *user_ns;
unsigned int proc_inum;
};
extern struct uts_namespace init_uts_ns;
+2
View File
@@ -52,6 +52,8 @@ struct net {
struct list_head cleanup_list; /* namespaces on death row */
struct list_head exit_list; /* Use only net_mutex */
unsigned int proc_inum;
struct proc_dir_entry *proc_net;
struct proc_dir_entry *proc_net_stat;
+2
View File
@@ -12,6 +12,7 @@
#include <linux/utsname.h>
#include <generated/utsrelease.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#ifndef CONFIG_KALLSYMS
#define version(a) Version_ ## a
@@ -34,6 +35,7 @@ struct uts_namespace init_uts_ns = {
.domainname = UTS_DOMAINNAME,
},
.user_ns = &init_user_ns,
.proc_inum = PROC_UTS_INIT_INO,
};
EXPORT_SYMBOL_GPL(init_uts_ns);
+2
View File
@@ -16,6 +16,7 @@
#include <linux/msg.h>
#include <linux/ipc_namespace.h>
#include <linux/utsname.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include "util.h"
@@ -30,6 +31,7 @@ DEFINE_SPINLOCK(mq_lock);
struct ipc_namespace init_ipc_ns = {
.count = ATOMIC_INIT(1),
.user_ns = &init_user_ns,
.proc_inum = PROC_IPC_INIT_INO,
};
atomic_t nr_ipc_ns = ATOMIC_INIT(1);
+16
View File
@@ -26,9 +26,16 @@ static struct ipc_namespace *create_ipc_ns(struct task_struct *tsk,
if (ns == NULL)
return ERR_PTR(-ENOMEM);
err = proc_alloc_inum(&ns->proc_inum);
if (err) {
kfree(ns);
return ERR_PTR(err);
}
atomic_set(&ns->count, 1);
err = mq_init_ns(ns);
if (err) {
proc_free_inum(ns->proc_inum);
kfree(ns);
return ERR_PTR(err);
}
@@ -113,6 +120,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)
*/
ipcns_notify(IPCNS_REMOVED);
put_user_ns(ns->user_ns);
proc_free_inum(ns->proc_inum);
kfree(ns);
}
@@ -170,10 +178,18 @@ static int ipcns_install(struct nsproxy *nsproxy, void *ns)
return 0;
}
static unsigned int ipcns_inum(void *vp)
{
struct ipc_namespace *ns = vp;
return ns->proc_inum;
}
const struct proc_ns_operations ipcns_operations = {
.name = "ipc",
.type = CLONE_NEWIPC,
.get = ipcns_get,
.put = ipcns_put,
.install = ipcns_install,
.inum = ipcns_inum,
};
+1 -1
View File
@@ -66,7 +66,7 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
if (!new_nsp)
return ERR_PTR(-ENOMEM);
new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, new_fs);
new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, task_cred_xxx(tsk, user_ns), new_fs);
if (IS_ERR(new_nsp->mnt_ns)) {
err = PTR_ERR(new_nsp->mnt_ns);
goto out_ns;
+1
View File
@@ -78,6 +78,7 @@ struct pid_namespace init_pid_ns = {
.last_pid = 0,
.level = 0,
.child_reaper = &init_task,
.proc_inum = PROC_PID_INIT_INO,
};
EXPORT_SYMBOL_GPL(init_pid_ns);
+5
View File
@@ -88,6 +88,10 @@ static struct pid_namespace *create_pid_namespace(struct pid_namespace *parent_p
if (ns->pid_cachep == NULL)
goto out_free_map;
err = proc_alloc_inum(&ns->proc_inum);
if (err)
goto out_free_map;
kref_init(&ns->kref);
ns->level = level;
ns->parent = get_pid_ns(parent_pid_ns);
@@ -118,6 +122,7 @@ static void destroy_pid_namespace(struct pid_namespace *ns)
{
int i;
proc_free_inum(ns->proc_inum);
for (i = 0; i < PIDMAP_ENTRIES; i++)
kfree(ns->pidmap[i].page);
kmem_cache_free(pid_ns_cachep, ns);
+2
View File
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/export.h>
#include <linux/user_namespace.h>
#include <linux/proc_fs.h>
/*
* userns count is 1 for root user, 1 for init_uts_ns,
@@ -26,6 +27,7 @@ struct user_namespace init_user_ns = {
.refcount = ATOMIC_INIT(3),
},
.creator = &root_user,
.proc_inum = PROC_USER_INIT_INO,
};
EXPORT_SYMBOL_GPL(init_user_ns);
+8
View File
@@ -27,11 +27,18 @@ int create_user_ns(struct cred *new)
struct user_namespace *ns;
struct user_struct *root_user;
int n;
int ret;
ns = kmem_cache_alloc(user_ns_cachep, GFP_KERNEL);
if (!ns)
return -ENOMEM;
ret = proc_alloc_inum(&ns->proc_inum);
if (ret) {
kmem_cache_free(user_ns_cachep, ns);
return ret;
}
kref_init(&ns->kref);
for (n = 0; n < UIDHASH_SZ; ++n)
@@ -73,6 +80,7 @@ static void free_user_ns_work(struct work_struct *work)
struct user_namespace *ns =
container_of(work, struct user_namespace, destroyer);
free_uid(ns->creator);
proc_free_inum(ns->proc_inum);
kmem_cache_free(user_ns_cachep, ns);
}
+16 -1
View File
@@ -36,11 +36,18 @@ static struct uts_namespace *clone_uts_ns(struct task_struct *tsk,
struct uts_namespace *old_ns)
{
struct uts_namespace *ns;
int err;
ns = create_uts_ns();
if (!ns)
return ERR_PTR(-ENOMEM);
err = proc_alloc_inum(&ns->proc_inum);
if (err) {
kfree(ns);
return ERR_PTR(err);
}
down_read(&uts_sem);
memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
ns->user_ns = get_user_ns(task_cred_xxx(tsk, user)->user_ns);
@@ -78,6 +85,7 @@ void free_uts_ns(struct kref *kref)
ns = container_of(kref, struct uts_namespace, kref);
put_user_ns(ns->user_ns);
proc_free_inum(ns->proc_inum);
kfree(ns);
}
@@ -110,11 +118,18 @@ static int utsns_install(struct nsproxy *nsproxy, void *ns)
return 0;
}
static unsigned int utsns_inum(void *vp)
{
struct uts_namespace *ns = vp;
return ns->proc_inum;
}
const struct proc_ns_operations utsns_operations = {
.name = "uts",
.type = CLONE_NEWUTS,
.get = utsns_get,
.put = utsns_put,
.install = utsns_install,
.inum = utsns_inum,
};
+24
View File
@@ -376,6 +376,21 @@ struct net *get_net_ns_by_pid(pid_t pid)
}
EXPORT_SYMBOL_GPL(get_net_ns_by_pid);
static __net_init int net_ns_net_init(struct net *net)
{
return proc_alloc_inum(&net->proc_inum);
}
static __net_exit void net_ns_net_exit(struct net *net)
{
proc_free_inum(net->proc_inum);
}
static struct pernet_operations __net_initdata net_ns_ops = {
.init = net_ns_net_init,
.exit = net_ns_net_exit,
};
static int __init net_ns_init(void)
{
struct net_generic *ng;
@@ -407,6 +422,8 @@ static int __init net_ns_init(void)
mutex_unlock(&net_mutex);
register_pernet_subsys(&net_ns_ops);
return 0;
}
@@ -630,11 +647,18 @@ static int netns_install(struct nsproxy *nsproxy, void *ns)
return 0;
}
static unsigned int netns_inum(void *ns)
{
struct net *net = ns;
return net->proc_inum;
}
const struct proc_ns_operations netns_operations = {
.name = "net",
.type = CLONE_NEWNET,
.get = netns_get,
.put = netns_put,
.install = netns_install,
.inum = netns_inum,
};
#endif