mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +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	+= namespaces.o | ||||
| proc-y	+= self.o | ||||
| proc-y	+= thread_self.o | ||||
| proc-$(CONFIG_PROC_SYSCTL)	+= proc_sysctl.o | ||||
| proc-$(CONFIG_NET)		+= proc_net.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; | ||||
| } | ||||
| 
 | ||||
| #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 */ | ||||
| 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) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (pos == TGID_OFFSET - 1) { | ||||
| 	if (pos == TGID_OFFSET - 2) { | ||||
| 		struct inode *inode = ns->proc_self->d_inode; | ||||
| 		if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) | ||||
| 			return 0; | ||||
| 		iter.tgid = 0; | ||||
| 	} else { | ||||
| 		iter.tgid = pos - TGID_OFFSET; | ||||
| 		ctx->pos = pos = pos + 1; | ||||
| 	} | ||||
| 	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; | ||||
| 	for (iter = next_tgid(ns, iter); | ||||
| 	     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) | ||||
| { | ||||
| 	struct inode *root_inode; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; | ||||
| 	s->s_blocksize = 1024; | ||||
|  | @ -463,5 +464,9 @@ int proc_fill_super(struct super_block *s) | |||
| 		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 *); | ||||
| 
 | ||||
| /*
 | ||||
|  * proc_thread_self.c | ||||
|  */ | ||||
| extern int proc_setup_thread_self(struct super_block *); | ||||
| extern void proc_thread_self_init(void); | ||||
| 
 | ||||
| /*
 | ||||
|  * proc_sysctl.c | ||||
|  */ | ||||
|  |  | |||
|  | @ -149,6 +149,8 @@ static void proc_kill_sb(struct super_block *sb) | |||
| 	ns = (struct pid_namespace *)sb->s_fs_info; | ||||
| 	if (ns->proc_self) | ||||
| 		dput(ns->proc_self); | ||||
| 	if (ns->proc_thread_self) | ||||
| 		dput(ns->proc_thread_self); | ||||
| 	kill_anon_super(sb); | ||||
| 	put_pid_ns(ns); | ||||
| } | ||||
|  | @ -170,6 +172,7 @@ void __init proc_root_init(void) | |||
| 		return; | ||||
| 
 | ||||
| 	proc_self_init(); | ||||
| 	proc_thread_self_init(); | ||||
| 	proc_symlink("mounts", NULL, "self/mounts"); | ||||
| 
 | ||||
| 	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 | ||||
| 	struct vfsmount *proc_mnt; | ||||
| 	struct dentry *proc_self; | ||||
| 	struct dentry *proc_thread_self; | ||||
| #endif | ||||
| #ifdef CONFIG_BSD_PROCESS_ACCT | ||||
| 	struct bsd_acct_struct *bacct; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Eric W. Biederman
						Eric W. Biederman