forked from rubenslte/android_kernel_samsung_msm8226
Merge "proc: Allow proc_free_inum to be called from any context"
This commit is contained in:
committed by
Gerrit - the friendly Code Review server
commit
2d02fca362
@@ -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
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 *);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user