mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	take the targets of /proc/*/ns/* symlinks to separate fs
New pseudo-filesystem: nsfs. Targets of /proc/*/ns/* live there now. It's not mountable (not even registered, so it's not in /proc/filesystems, etc.). Files on it *are* bindable - we explicitly permit that in do_loopback(). This stuff lives in fs/nsfs.c now; proc_ns_fget() moved there as well. get_proc_ns() is a macro now (it's simply returning ->i_private; would have been an inline, if not for header ordering headache). proc_ns_inode() is an ex-parrot. The interface used in procfs is ns_get_path(path, task, ops) and ns_get_name(buf, size, task, ops). Dentries and inodes are never hashed; a non-counting reference to dentry is stashed in ns_common (removed by ->d_prune()) and reused by ns_get_path() if present. See ns_get_path()/ns_prune_dentry/nsfs_evict() for details of that mechanism. As the result, proc_ns_follow_link() has stopped poking in nd->path.mnt; it does nd_jump_link() on a consistent <vfsmount,dentry> pair it gets from ns_get_path(). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									f77c80142e
								
							
						
					
					
						commit
						e149ed2b80
					
				
					 10 changed files with 208 additions and 161 deletions
				
			
		|  | @ -11,7 +11,7 @@ obj-y :=	open.o read_write.o file_table.o super.o \ | ||||||
| 		attr.o bad_inode.o file.o filesystems.o namespace.o \
 | 		attr.o bad_inode.o file.o filesystems.o namespace.o \
 | ||||||
| 		seq_file.o xattr.o libfs.o fs-writeback.o \
 | 		seq_file.o xattr.o libfs.o fs-writeback.o \
 | ||||||
| 		pnode.o splice.o sync.o utimes.o \
 | 		pnode.o splice.o sync.o utimes.o \
 | ||||||
| 		stack.o fs_struct.o statfs.o fs_pin.o | 		stack.o fs_struct.o statfs.o fs_pin.o nsfs.o | ||||||
| 
 | 
 | ||||||
| ifeq ($(CONFIG_BLOCK),y) | ifeq ($(CONFIG_BLOCK),y) | ||||||
| obj-y +=	buffer.o block_dev.o direct-io.o mpage.o | obj-y +=	buffer.o block_dev.o direct-io.o mpage.o | ||||||
|  |  | ||||||
|  | @ -147,3 +147,8 @@ extern const struct file_operations pipefifo_fops; | ||||||
|  */ |  */ | ||||||
| extern void sb_pin_kill(struct super_block *sb); | extern void sb_pin_kill(struct super_block *sb); | ||||||
| extern void mnt_pin_kill(struct mount *m); | extern void mnt_pin_kill(struct mount *m); | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * fs/nsfs.c | ||||||
|  |  */ | ||||||
|  | extern struct dentry_operations ns_dentry_operations; | ||||||
|  |  | ||||||
|  | @ -1569,8 +1569,8 @@ SYSCALL_DEFINE1(oldumount, char __user *, name) | ||||||
| static bool is_mnt_ns_file(struct dentry *dentry) | static bool is_mnt_ns_file(struct dentry *dentry) | ||||||
| { | { | ||||||
| 	/* Is this a proxy for a mount namespace? */ | 	/* Is this a proxy for a mount namespace? */ | ||||||
| 	struct inode *inode = dentry->d_inode; | 	return dentry->d_op == &ns_dentry_operations && | ||||||
| 	return proc_ns_inode(inode) && dentry->d_fsdata == &mntns_operations; | 	       dentry->d_fsdata == &mntns_operations; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct mnt_namespace *to_mnt_ns(struct ns_common *ns) | struct mnt_namespace *to_mnt_ns(struct ns_common *ns) | ||||||
|  | @ -2016,7 +2016,10 @@ static int do_loopback(struct path *path, const char *old_name, | ||||||
| 	if (IS_MNT_UNBINDABLE(old)) | 	if (IS_MNT_UNBINDABLE(old)) | ||||||
| 		goto out2; | 		goto out2; | ||||||
| 
 | 
 | ||||||
| 	if (!check_mnt(parent) || !check_mnt(old)) | 	if (!check_mnt(parent)) | ||||||
|  | 		goto out2; | ||||||
|  | 
 | ||||||
|  | 	if (!check_mnt(old) && old_path.dentry->d_op != &ns_dentry_operations) | ||||||
| 		goto out2; | 		goto out2; | ||||||
| 
 | 
 | ||||||
| 	if (!recurse && has_locked_children(old, old_path.dentry)) | 	if (!recurse && has_locked_children(old, old_path.dentry)) | ||||||
|  |  | ||||||
							
								
								
									
										161
									
								
								fs/nsfs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								fs/nsfs.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,161 @@ | ||||||
|  | #include <linux/mount.h> | ||||||
|  | #include <linux/file.h> | ||||||
|  | #include <linux/fs.h> | ||||||
|  | #include <linux/proc_ns.h> | ||||||
|  | #include <linux/magic.h> | ||||||
|  | #include <linux/ktime.h> | ||||||
|  | 
 | ||||||
|  | static struct vfsmount *nsfs_mnt; | ||||||
|  | 
 | ||||||
|  | static const struct file_operations ns_file_operations = { | ||||||
|  | 	.llseek		= no_llseek, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | ||||||
|  | { | ||||||
|  | 	struct inode *inode = dentry->d_inode; | ||||||
|  | 	const struct proc_ns_operations *ns_ops = dentry->d_fsdata; | ||||||
|  | 
 | ||||||
|  | 	return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]", | ||||||
|  | 		ns_ops->name, inode->i_ino); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ns_prune_dentry(struct dentry *dentry) | ||||||
|  | { | ||||||
|  | 	struct inode *inode = dentry->d_inode; | ||||||
|  | 	if (inode) { | ||||||
|  | 		struct ns_common *ns = inode->i_private; | ||||||
|  | 		atomic_long_set(&ns->stashed, 0); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct dentry_operations ns_dentry_operations = | ||||||
|  | { | ||||||
|  | 	.d_prune	= ns_prune_dentry, | ||||||
|  | 	.d_delete	= always_delete_dentry, | ||||||
|  | 	.d_dname	= ns_dname, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void nsfs_evict(struct inode *inode) | ||||||
|  | { | ||||||
|  | 	struct ns_common *ns = inode->i_private; | ||||||
|  | 	clear_inode(inode); | ||||||
|  | 	ns->ops->put(ns); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void *ns_get_path(struct path *path, struct task_struct *task, | ||||||
|  | 			const struct proc_ns_operations *ns_ops) | ||||||
|  | { | ||||||
|  | 	struct vfsmount *mnt = mntget(nsfs_mnt); | ||||||
|  | 	struct qstr qname = { .name = "", }; | ||||||
|  | 	struct dentry *dentry; | ||||||
|  | 	struct inode *inode; | ||||||
|  | 	struct ns_common *ns; | ||||||
|  | 	unsigned long d; | ||||||
|  | 
 | ||||||
|  | again: | ||||||
|  | 	ns = ns_ops->get(task); | ||||||
|  | 	if (!ns) { | ||||||
|  | 		mntput(mnt); | ||||||
|  | 		return ERR_PTR(-ENOENT); | ||||||
|  | 	} | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	d = atomic_long_read(&ns->stashed); | ||||||
|  | 	if (!d) | ||||||
|  | 		goto slow; | ||||||
|  | 	dentry = (struct dentry *)d; | ||||||
|  | 	if (!lockref_get_not_dead(&dentry->d_lockref)) | ||||||
|  | 		goto slow; | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 	ns_ops->put(ns); | ||||||
|  | got_it: | ||||||
|  | 	path->mnt = mnt; | ||||||
|  | 	path->dentry = dentry; | ||||||
|  | 	return NULL; | ||||||
|  | slow: | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 	inode = new_inode_pseudo(mnt->mnt_sb); | ||||||
|  | 	if (!inode) { | ||||||
|  | 		ns_ops->put(ns); | ||||||
|  | 		mntput(mnt); | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 	} | ||||||
|  | 	inode->i_ino = ns->inum; | ||||||
|  | 	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | ||||||
|  | 	inode->i_flags |= S_IMMUTABLE; | ||||||
|  | 	inode->i_mode = S_IFREG | S_IRUGO; | ||||||
|  | 	inode->i_fop = &ns_file_operations; | ||||||
|  | 	inode->i_private = ns; | ||||||
|  | 
 | ||||||
|  | 	dentry = d_alloc_pseudo(mnt->mnt_sb, &qname); | ||||||
|  | 	if (!dentry) { | ||||||
|  | 		iput(inode); | ||||||
|  | 		mntput(mnt); | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 	} | ||||||
|  | 	d_instantiate(dentry, inode); | ||||||
|  | 	dentry->d_fsdata = (void *)ns_ops; | ||||||
|  | 	d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry); | ||||||
|  | 	if (d) { | ||||||
|  | 		d_delete(dentry);	/* make sure ->d_prune() does nothing */ | ||||||
|  | 		dput(dentry); | ||||||
|  | 		cpu_relax(); | ||||||
|  | 		goto again; | ||||||
|  | 	} | ||||||
|  | 	goto got_it; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int ns_get_name(char *buf, size_t size, struct task_struct *task, | ||||||
|  | 			const struct proc_ns_operations *ns_ops) | ||||||
|  | { | ||||||
|  | 	struct ns_common *ns; | ||||||
|  | 	int res = -ENOENT; | ||||||
|  | 	ns = ns_ops->get(task); | ||||||
|  | 	if (ns) { | ||||||
|  | 		res = snprintf(buf, size, "%s:[%u]", ns_ops->name, ns->inum); | ||||||
|  | 		ns_ops->put(ns); | ||||||
|  | 	} | ||||||
|  | 	return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct file *proc_ns_fget(int fd) | ||||||
|  | { | ||||||
|  | 	struct file *file; | ||||||
|  | 
 | ||||||
|  | 	file = fget(fd); | ||||||
|  | 	if (!file) | ||||||
|  | 		return ERR_PTR(-EBADF); | ||||||
|  | 
 | ||||||
|  | 	if (file->f_op != &ns_file_operations) | ||||||
|  | 		goto out_invalid; | ||||||
|  | 
 | ||||||
|  | 	return file; | ||||||
|  | 
 | ||||||
|  | out_invalid: | ||||||
|  | 	fput(file); | ||||||
|  | 	return ERR_PTR(-EINVAL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct super_operations nsfs_ops = { | ||||||
|  | 	.statfs = simple_statfs, | ||||||
|  | 	.evict_inode = nsfs_evict, | ||||||
|  | }; | ||||||
|  | static struct dentry *nsfs_mount(struct file_system_type *fs_type, | ||||||
|  | 			int flags, const char *dev_name, void *data) | ||||||
|  | { | ||||||
|  | 	return mount_pseudo(fs_type, "nsfs:", &nsfs_ops, | ||||||
|  | 			&ns_dentry_operations, NSFS_MAGIC); | ||||||
|  | } | ||||||
|  | static struct file_system_type nsfs = { | ||||||
|  | 	.name = "nsfs", | ||||||
|  | 	.mount = nsfs_mount, | ||||||
|  | 	.kill_sb = kill_anon_super, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void __init nsfs_init(void) | ||||||
|  | { | ||||||
|  | 	nsfs_mnt = kern_mount(&nsfs); | ||||||
|  | 	if (IS_ERR(nsfs_mnt)) | ||||||
|  | 		panic("can't set nsfs up\n"); | ||||||
|  | 	nsfs_mnt->mnt_sb->s_flags &= ~MS_NOUSER; | ||||||
|  | } | ||||||
|  | @ -32,7 +32,6 @@ static void proc_evict_inode(struct inode *inode) | ||||||
| { | { | ||||||
| 	struct proc_dir_entry *de; | 	struct proc_dir_entry *de; | ||||||
| 	struct ctl_table_header *head; | 	struct ctl_table_header *head; | ||||||
| 	struct ns_common *ns; |  | ||||||
| 
 | 
 | ||||||
| 	truncate_inode_pages_final(&inode->i_data); | 	truncate_inode_pages_final(&inode->i_data); | ||||||
| 	clear_inode(inode); | 	clear_inode(inode); | ||||||
|  | @ -49,10 +48,6 @@ static void proc_evict_inode(struct inode *inode) | ||||||
| 		RCU_INIT_POINTER(PROC_I(inode)->sysctl, NULL); | 		RCU_INIT_POINTER(PROC_I(inode)->sysctl, NULL); | ||||||
| 		sysctl_head_put(head); | 		sysctl_head_put(head); | ||||||
| 	} | 	} | ||||||
| 	/* Release any associated namespace */ |  | ||||||
| 	ns = PROC_I(inode)->ns.ns; |  | ||||||
| 	if (ns && ns->ops) |  | ||||||
| 		ns->ops->put(ns); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct kmem_cache * proc_inode_cachep; | static struct kmem_cache * proc_inode_cachep; | ||||||
|  |  | ||||||
|  | @ -1,10 +1,6 @@ | ||||||
| #include <linux/proc_fs.h> | #include <linux/proc_fs.h> | ||||||
| #include <linux/nsproxy.h> | #include <linux/nsproxy.h> | ||||||
| #include <linux/sched.h> |  | ||||||
| #include <linux/ptrace.h> | #include <linux/ptrace.h> | ||||||
| #include <linux/fs_struct.h> |  | ||||||
| #include <linux/mount.h> |  | ||||||
| #include <linux/path.h> |  | ||||||
| #include <linux/namei.h> | #include <linux/namei.h> | ||||||
| #include <linux/file.h> | #include <linux/file.h> | ||||||
| #include <linux/utsname.h> | #include <linux/utsname.h> | ||||||
|  | @ -34,139 +30,45 @@ static const struct proc_ns_operations *ns_entries[] = { | ||||||
| 	&mntns_operations, | 	&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 char *ns_dname(struct dentry *dentry, char *buffer, int buflen) |  | ||||||
| { |  | ||||||
| 	struct inode *inode = dentry->d_inode; |  | ||||||
| 	const struct proc_ns_operations *ns_ops = dentry->d_fsdata; |  | ||||||
| 
 |  | ||||||
| 	return dynamic_dname(dentry, buffer, buflen, "%s:[%lu]", |  | ||||||
| 		ns_ops->name, inode->i_ino); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const struct dentry_operations ns_dentry_operations = |  | ||||||
| { |  | ||||||
| 	.d_delete	= always_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 = "", }; |  | ||||||
| 	struct ns_common *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); |  | ||||||
| 	} |  | ||||||
| 	dentry->d_fsdata = (void *)ns_ops; |  | ||||||
| 
 |  | ||||||
| 	inode = iget_locked(sb, ns->inum); |  | ||||||
| 	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.ns_ops = ns_ops; |  | ||||||
| 		ei->ns.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) | static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd) | ||||||
| { | { | ||||||
| 	struct inode *inode = dentry->d_inode; | 	struct inode *inode = dentry->d_inode; | ||||||
| 	struct super_block *sb = inode->i_sb; | 	const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns.ns_ops; | ||||||
| 	struct proc_inode *ei = PROC_I(inode); |  | ||||||
| 	struct task_struct *task; | 	struct task_struct *task; | ||||||
| 	struct path ns_path; | 	struct path ns_path; | ||||||
| 	void *error = ERR_PTR(-EACCES); | 	void *error = ERR_PTR(-EACCES); | ||||||
| 
 | 
 | ||||||
| 	task = get_proc_task(inode); | 	task = get_proc_task(inode); | ||||||
| 	if (!task) | 	if (!task) | ||||||
| 		goto out; | 		return error; | ||||||
| 
 | 
 | ||||||
| 	if (!ptrace_may_access(task, PTRACE_MODE_READ)) | 	if (ptrace_may_access(task, PTRACE_MODE_READ)) { | ||||||
| 		goto out_put_task; | 		error = ns_get_path(&ns_path, task, ns_ops); | ||||||
| 
 | 		if (!error) | ||||||
| 	ns_path.dentry = proc_ns_get_dentry(sb, task, ei->ns.ns_ops); | 			nd_jump_link(nd, &ns_path); | ||||||
| 	if (IS_ERR(ns_path.dentry)) { |  | ||||||
| 		error = ERR_CAST(ns_path.dentry); |  | ||||||
| 		goto out_put_task; |  | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	ns_path.mnt = mntget(nd->path.mnt); |  | ||||||
| 	nd_jump_link(nd, &ns_path); |  | ||||||
| 	error = NULL; |  | ||||||
| 
 |  | ||||||
| out_put_task: |  | ||||||
| 	put_task_struct(task); | 	put_task_struct(task); | ||||||
| out: |  | ||||||
| 	return error; | 	return error; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) | static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int buflen) | ||||||
| { | { | ||||||
| 	struct inode *inode = dentry->d_inode; | 	struct inode *inode = dentry->d_inode; | ||||||
| 	struct proc_inode *ei = PROC_I(inode); | 	const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns.ns_ops; | ||||||
| 	const struct proc_ns_operations *ns_ops = ei->ns.ns_ops; |  | ||||||
| 	struct task_struct *task; | 	struct task_struct *task; | ||||||
| 	struct ns_common *ns; |  | ||||||
| 	char name[50]; | 	char name[50]; | ||||||
| 	int res = -EACCES; | 	int res = -EACCES; | ||||||
| 
 | 
 | ||||||
| 	task = get_proc_task(inode); | 	task = get_proc_task(inode); | ||||||
| 	if (!task) | 	if (!task) | ||||||
| 		goto out; | 		return res; | ||||||
| 
 | 
 | ||||||
| 	if (!ptrace_may_access(task, PTRACE_MODE_READ)) | 	if (ptrace_may_access(task, PTRACE_MODE_READ)) { | ||||||
| 		goto out_put_task; | 		res = ns_get_name(name, sizeof(name), task, ns_ops); | ||||||
| 
 | 		if (res >= 0) | ||||||
| 	res = -ENOENT; | 			res = readlink_copy(buffer, buflen, name); | ||||||
| 	ns = ns_ops->get(task); | 	} | ||||||
| 	if (!ns) |  | ||||||
| 		goto out_put_task; |  | ||||||
| 
 |  | ||||||
| 	snprintf(name, sizeof(name), "%s:[%u]", ns_ops->name, ns->inum); |  | ||||||
| 	res = readlink_copy(buffer, buflen, name); |  | ||||||
| 	ns_ops->put(ns); |  | ||||||
| out_put_task: |  | ||||||
| 	put_task_struct(task); | 	put_task_struct(task); | ||||||
| out: |  | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -268,31 +170,3 @@ const struct inode_operations proc_ns_dir_inode_operations = { | ||||||
| 	.getattr	= pid_getattr, | 	.getattr	= pid_getattr, | ||||||
| 	.setattr	= proc_setattr, | 	.setattr	= proc_setattr, | ||||||
| }; | }; | ||||||
| 
 |  | ||||||
| struct file *proc_ns_fget(int fd) |  | ||||||
| { |  | ||||||
| 	struct file *file; |  | ||||||
| 
 |  | ||||||
| 	file = fget(fd); |  | ||||||
| 	if (!file) |  | ||||||
| 		return ERR_PTR(-EBADF); |  | ||||||
| 
 |  | ||||||
| 	if (file->f_op != &ns_file_operations) |  | ||||||
| 		goto out_invalid; |  | ||||||
| 
 |  | ||||||
| 	return file; |  | ||||||
| 
 |  | ||||||
| out_invalid: |  | ||||||
| 	fput(file); |  | ||||||
| 	return ERR_PTR(-EINVAL); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct ns_common *get_proc_ns(struct inode *inode) |  | ||||||
| { |  | ||||||
| 	return PROC_I(inode)->ns.ns; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool proc_ns_inode(struct inode *inode) |  | ||||||
| { |  | ||||||
| 	return inode->i_fop == &ns_file_operations; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| struct proc_ns_operations; | struct proc_ns_operations; | ||||||
| 
 | 
 | ||||||
| struct ns_common { | struct ns_common { | ||||||
|  | 	atomic_long_t stashed; | ||||||
| 	const struct proc_ns_operations *ops; | 	const struct proc_ns_operations *ops; | ||||||
| 	unsigned int inum; | 	unsigned int inum; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -4,9 +4,11 @@ | ||||||
| #ifndef _LINUX_PROC_NS_H | #ifndef _LINUX_PROC_NS_H | ||||||
| #define _LINUX_PROC_NS_H | #define _LINUX_PROC_NS_H | ||||||
| 
 | 
 | ||||||
|  | #include <linux/ns_common.h> | ||||||
|  | 
 | ||||||
| struct pid_namespace; | struct pid_namespace; | ||||||
| struct nsproxy; | struct nsproxy; | ||||||
| struct ns_common; | struct path; | ||||||
| 
 | 
 | ||||||
| struct proc_ns_operations { | struct proc_ns_operations { | ||||||
| 	const char *name; | 	const char *name; | ||||||
|  | @ -38,35 +40,38 @@ enum { | ||||||
| 
 | 
 | ||||||
| extern int pid_ns_prepare_proc(struct pid_namespace *ns); | extern int pid_ns_prepare_proc(struct pid_namespace *ns); | ||||||
| extern void pid_ns_release_proc(struct pid_namespace *ns); | extern void pid_ns_release_proc(struct pid_namespace *ns); | ||||||
| extern struct file *proc_ns_fget(int fd); |  | ||||||
| extern struct ns_common *get_proc_ns(struct inode *); |  | ||||||
| extern int proc_alloc_inum(unsigned int *pino); | extern int proc_alloc_inum(unsigned int *pino); | ||||||
| extern void proc_free_inum(unsigned int inum); | extern void proc_free_inum(unsigned int inum); | ||||||
| extern bool proc_ns_inode(struct inode *inode); |  | ||||||
| 
 | 
 | ||||||
| #else /* CONFIG_PROC_FS */ | #else /* CONFIG_PROC_FS */ | ||||||
| 
 | 
 | ||||||
| static inline int pid_ns_prepare_proc(struct pid_namespace *ns) { return 0; } | static inline int pid_ns_prepare_proc(struct pid_namespace *ns) { return 0; } | ||||||
| static inline void pid_ns_release_proc(struct pid_namespace *ns) {} | static inline void pid_ns_release_proc(struct pid_namespace *ns) {} | ||||||
| 
 | 
 | ||||||
| static inline struct file *proc_ns_fget(int fd) |  | ||||||
| { |  | ||||||
| 	return ERR_PTR(-EINVAL); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline struct ns_common *get_proc_ns(struct inode *inode) { return NULL; } |  | ||||||
| 
 |  | ||||||
| static inline int proc_alloc_inum(unsigned int *inum) | static inline int proc_alloc_inum(unsigned int *inum) | ||||||
| { | { | ||||||
| 	*inum = 1; | 	*inum = 1; | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| static inline void proc_free_inum(unsigned int inum) {} | static inline void proc_free_inum(unsigned int inum) {} | ||||||
| static inline bool proc_ns_inode(struct inode *inode) { return false; } |  | ||||||
| 
 | 
 | ||||||
| #endif /* CONFIG_PROC_FS */ | #endif /* CONFIG_PROC_FS */ | ||||||
| 
 | 
 | ||||||
| #define ns_alloc_inum(ns) proc_alloc_inum(&(ns)->inum) | static inline int ns_alloc_inum(struct ns_common *ns) | ||||||
|  | { | ||||||
|  | 	atomic_long_set(&ns->stashed, 0); | ||||||
|  | 	return proc_alloc_inum(&ns->inum); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #define ns_free_inum(ns) proc_free_inum((ns)->inum) | #define ns_free_inum(ns) proc_free_inum((ns)->inum) | ||||||
| 
 | 
 | ||||||
|  | extern struct file *proc_ns_fget(int fd); | ||||||
|  | #define get_proc_ns(inode) ((struct ns_common *)(inode)->i_private) | ||||||
|  | extern void *ns_get_path(struct path *path, struct task_struct *task, | ||||||
|  | 			const struct proc_ns_operations *ns_ops); | ||||||
|  | 
 | ||||||
|  | extern int ns_get_name(char *buf, size_t size, struct task_struct *task, | ||||||
|  | 			const struct proc_ns_operations *ns_ops); | ||||||
|  | extern void nsfs_init(void); | ||||||
|  | 
 | ||||||
| #endif /* _LINUX_PROC_NS_H */ | #endif /* _LINUX_PROC_NS_H */ | ||||||
|  |  | ||||||
|  | @ -72,5 +72,6 @@ | ||||||
| #define MTD_INODE_FS_MAGIC      0x11307854 | #define MTD_INODE_FS_MAGIC      0x11307854 | ||||||
| #define ANON_INODE_FS_MAGIC	0x09041934 | #define ANON_INODE_FS_MAGIC	0x09041934 | ||||||
| #define BTRFS_TEST_MAGIC	0x73727279 | #define BTRFS_TEST_MAGIC	0x73727279 | ||||||
|  | #define NSFS_MAGIC		0x6e736673 | ||||||
| 
 | 
 | ||||||
| #endif /* __LINUX_MAGIC_H__ */ | #endif /* __LINUX_MAGIC_H__ */ | ||||||
|  |  | ||||||
|  | @ -78,6 +78,7 @@ | ||||||
| #include <linux/context_tracking.h> | #include <linux/context_tracking.h> | ||||||
| #include <linux/random.h> | #include <linux/random.h> | ||||||
| #include <linux/list.h> | #include <linux/list.h> | ||||||
|  | #include <linux/proc_ns.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/io.h> | #include <asm/io.h> | ||||||
| #include <asm/bugs.h> | #include <asm/bugs.h> | ||||||
|  | @ -660,6 +661,7 @@ asmlinkage __visible void __init start_kernel(void) | ||||||
| 	/* rootfs populating might need page-writeback */ | 	/* rootfs populating might need page-writeback */ | ||||||
| 	page_writeback_init(); | 	page_writeback_init(); | ||||||
| 	proc_root_init(); | 	proc_root_init(); | ||||||
|  | 	nsfs_init(); | ||||||
| 	cgroup_init(); | 	cgroup_init(); | ||||||
| 	cpuset_init(); | 	cpuset_init(); | ||||||
| 	taskstats_init_early(); | 	taskstats_init_early(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Al Viro
						Al Viro