mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	proc: Implement /proc/thread-self to point at the directory of the current thread
/proc/thread-self is derived from /proc/self. /proc/thread-self points to the directory in proc containing information about the current thread. This funtionality has been missing for a long time, and is tricky to implement in userspace as gettid() is not exported by glibc. More importantly this allows fixing defects in /proc/mounts and /proc/net where in a threaded application today they wind up being empty files when only the initial pthread has exited, causing problems for other threads. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
This commit is contained in:
		
							parent
							
								
									6ba8ed79a3
								
							
						
					
					
						commit
						0097875bd4
					
				
					 7 changed files with 112 additions and 6 deletions
				
			
		|  | @ -23,6 +23,7 @@ proc-y	+= version.o | ||||||
| proc-y	+= softirqs.o | proc-y	+= softirqs.o | ||||||
| proc-y	+= namespaces.o | proc-y	+= namespaces.o | ||||||
| proc-y	+= self.o | proc-y	+= self.o | ||||||
|  | proc-y	+= thread_self.o | ||||||
| proc-$(CONFIG_PROC_SYSCTL)	+= proc_sysctl.o | proc-$(CONFIG_PROC_SYSCTL)	+= proc_sysctl.o | ||||||
| proc-$(CONFIG_NET)		+= proc_net.o | proc-$(CONFIG_NET)		+= proc_net.o | ||||||
| proc-$(CONFIG_PROC_KCORE)	+= kcore.o | proc-$(CONFIG_PROC_KCORE)	+= kcore.o | ||||||
|  |  | ||||||
|  | @ -2847,7 +2847,7 @@ static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter ite | ||||||
| 	return iter; | 	return iter; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 1) | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2) | ||||||
| 
 | 
 | ||||||
| /* for the /proc/ directory itself, after non-process stuff has been done */ | /* for the /proc/ directory itself, after non-process stuff has been done */ | ||||||
| int proc_pid_readdir(struct file *file, struct dir_context *ctx) | int proc_pid_readdir(struct file *file, struct dir_context *ctx) | ||||||
|  | @ -2859,14 +2859,19 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) | ||||||
| 	if (pos >= PID_MAX_LIMIT + TGID_OFFSET) | 	if (pos >= PID_MAX_LIMIT + TGID_OFFSET) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	if (pos == TGID_OFFSET - 1) { | 	if (pos == TGID_OFFSET - 2) { | ||||||
| 		struct inode *inode = ns->proc_self->d_inode; | 		struct inode *inode = ns->proc_self->d_inode; | ||||||
| 		if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) | 		if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) | ||||||
| 			return 0; | 			return 0; | ||||||
| 		iter.tgid = 0; | 		ctx->pos = pos = pos + 1; | ||||||
| 	} else { |  | ||||||
| 		iter.tgid = pos - TGID_OFFSET; |  | ||||||
| 	} | 	} | ||||||
|  | 	if (pos == TGID_OFFSET - 1) { | ||||||
|  | 		struct inode *inode = ns->proc_thread_self->d_inode; | ||||||
|  | 		if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK)) | ||||||
|  | 			return 0; | ||||||
|  | 		ctx->pos = pos = pos + 1; | ||||||
|  | 	} | ||||||
|  | 	iter.tgid = pos - TGID_OFFSET; | ||||||
| 	iter.task = NULL; | 	iter.task = NULL; | ||||||
| 	for (iter = next_tgid(ns, iter); | 	for (iter = next_tgid(ns, iter); | ||||||
| 	     iter.task; | 	     iter.task; | ||||||
|  |  | ||||||
|  | @ -442,6 +442,7 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) | ||||||
| int proc_fill_super(struct super_block *s) | int proc_fill_super(struct super_block *s) | ||||||
| { | { | ||||||
| 	struct inode *root_inode; | 	struct inode *root_inode; | ||||||
|  | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; | 	s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; | ||||||
| 	s->s_blocksize = 1024; | 	s->s_blocksize = 1024; | ||||||
|  | @ -463,5 +464,9 @@ int proc_fill_super(struct super_block *s) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return proc_setup_self(s); | 	ret = proc_setup_self(s); | ||||||
|  | 	if (ret) { | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 	return proc_setup_thread_self(s); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -233,6 +233,12 @@ static inline int proc_net_init(void) { return 0; } | ||||||
|  */ |  */ | ||||||
| extern int proc_setup_self(struct super_block *); | extern int proc_setup_self(struct super_block *); | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * proc_thread_self.c | ||||||
|  |  */ | ||||||
|  | extern int proc_setup_thread_self(struct super_block *); | ||||||
|  | extern void proc_thread_self_init(void); | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * proc_sysctl.c |  * proc_sysctl.c | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
|  | @ -149,6 +149,8 @@ static void proc_kill_sb(struct super_block *sb) | ||||||
| 	ns = (struct pid_namespace *)sb->s_fs_info; | 	ns = (struct pid_namespace *)sb->s_fs_info; | ||||||
| 	if (ns->proc_self) | 	if (ns->proc_self) | ||||||
| 		dput(ns->proc_self); | 		dput(ns->proc_self); | ||||||
|  | 	if (ns->proc_thread_self) | ||||||
|  | 		dput(ns->proc_thread_self); | ||||||
| 	kill_anon_super(sb); | 	kill_anon_super(sb); | ||||||
| 	put_pid_ns(ns); | 	put_pid_ns(ns); | ||||||
| } | } | ||||||
|  | @ -170,6 +172,7 @@ void __init proc_root_init(void) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	proc_self_init(); | 	proc_self_init(); | ||||||
|  | 	proc_thread_self_init(); | ||||||
| 	proc_symlink("mounts", NULL, "self/mounts"); | 	proc_symlink("mounts", NULL, "self/mounts"); | ||||||
| 
 | 
 | ||||||
| 	proc_net_init(); | 	proc_net_init(); | ||||||
|  |  | ||||||
							
								
								
									
										85
									
								
								fs/proc/thread_self.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								fs/proc/thread_self.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | ||||||
|  | #include <linux/sched.h> | ||||||
|  | #include <linux/namei.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/pid_namespace.h> | ||||||
|  | #include "internal.h" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * /proc/thread_self: | ||||||
|  |  */ | ||||||
|  | static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, | ||||||
|  | 			      int buflen) | ||||||
|  | { | ||||||
|  | 	struct pid_namespace *ns = dentry->d_sb->s_fs_info; | ||||||
|  | 	pid_t tgid = task_tgid_nr_ns(current, ns); | ||||||
|  | 	pid_t pid = task_pid_nr_ns(current, ns); | ||||||
|  | 	char tmp[PROC_NUMBUF + 6 + PROC_NUMBUF]; | ||||||
|  | 	if (!pid) | ||||||
|  | 		return -ENOENT; | ||||||
|  | 	sprintf(tmp, "%d/task/%d", tgid, pid); | ||||||
|  | 	return readlink_copy(buffer, buflen, tmp); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void *proc_thread_self_follow_link(struct dentry *dentry, struct nameidata *nd) | ||||||
|  | { | ||||||
|  | 	struct pid_namespace *ns = dentry->d_sb->s_fs_info; | ||||||
|  | 	pid_t tgid = task_tgid_nr_ns(current, ns); | ||||||
|  | 	pid_t pid = task_pid_nr_ns(current, ns); | ||||||
|  | 	char *name = ERR_PTR(-ENOENT); | ||||||
|  | 	if (pid) { | ||||||
|  | 		name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); | ||||||
|  | 		if (!name) | ||||||
|  | 			name = ERR_PTR(-ENOMEM); | ||||||
|  | 		else | ||||||
|  | 			sprintf(name, "%d/task/%d", tgid, pid); | ||||||
|  | 	} | ||||||
|  | 	nd_set_link(nd, name); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct inode_operations proc_thread_self_inode_operations = { | ||||||
|  | 	.readlink	= proc_thread_self_readlink, | ||||||
|  | 	.follow_link	= proc_thread_self_follow_link, | ||||||
|  | 	.put_link	= kfree_put_link, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static unsigned thread_self_inum; | ||||||
|  | 
 | ||||||
|  | int proc_setup_thread_self(struct super_block *s) | ||||||
|  | { | ||||||
|  | 	struct inode *root_inode = s->s_root->d_inode; | ||||||
|  | 	struct pid_namespace *ns = s->s_fs_info; | ||||||
|  | 	struct dentry *thread_self; | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&root_inode->i_mutex); | ||||||
|  | 	thread_self = d_alloc_name(s->s_root, "thread-self"); | ||||||
|  | 	if (thread_self) { | ||||||
|  | 		struct inode *inode = new_inode_pseudo(s); | ||||||
|  | 		if (inode) { | ||||||
|  | 			inode->i_ino = thread_self_inum; | ||||||
|  | 			inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | ||||||
|  | 			inode->i_mode = S_IFLNK | S_IRWXUGO; | ||||||
|  | 			inode->i_uid = GLOBAL_ROOT_UID; | ||||||
|  | 			inode->i_gid = GLOBAL_ROOT_GID; | ||||||
|  | 			inode->i_op = &proc_thread_self_inode_operations; | ||||||
|  | 			d_add(thread_self, inode); | ||||||
|  | 		} else { | ||||||
|  | 			dput(thread_self); | ||||||
|  | 			thread_self = ERR_PTR(-ENOMEM); | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		thread_self = ERR_PTR(-ENOMEM); | ||||||
|  | 	} | ||||||
|  | 	mutex_unlock(&root_inode->i_mutex); | ||||||
|  | 	if (IS_ERR(thread_self)) { | ||||||
|  | 		pr_err("proc_fill_super: can't allocate /proc/thread_self\n"); | ||||||
|  | 		return PTR_ERR(thread_self); | ||||||
|  | 	} | ||||||
|  | 	ns->proc_thread_self = thread_self; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __init proc_thread_self_init(void) | ||||||
|  | { | ||||||
|  | 	proc_alloc_inum(&thread_self_inum); | ||||||
|  | } | ||||||
|  | @ -33,6 +33,7 @@ struct pid_namespace { | ||||||
| #ifdef CONFIG_PROC_FS | #ifdef CONFIG_PROC_FS | ||||||
| 	struct vfsmount *proc_mnt; | 	struct vfsmount *proc_mnt; | ||||||
| 	struct dentry *proc_self; | 	struct dentry *proc_self; | ||||||
|  | 	struct dentry *proc_thread_self; | ||||||
| #endif | #endif | ||||||
| #ifdef CONFIG_BSD_PROCESS_ACCT | #ifdef CONFIG_BSD_PROCESS_ACCT | ||||||
| 	struct bsd_acct_struct *bacct; | 	struct bsd_acct_struct *bacct; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric W. Biederman
						Eric W. Biederman