Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+112 -6
+1
fs/proc/Makefile
··· 23 23 proc-y += softirqs.o 24 24 proc-y += namespaces.o 25 25 proc-y += self.o 26 + proc-y += thread_self.o 26 27 proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o 27 28 proc-$(CONFIG_NET) += proc_net.o 28 29 proc-$(CONFIG_PROC_KCORE) += kcore.o
+10 -5
fs/proc/base.c
··· 2847 2847 return iter; 2848 2848 } 2849 2849 2850 - #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 1) 2850 + #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2) 2851 2851 2852 2852 /* for the /proc/ directory itself, after non-process stuff has been done */ 2853 2853 int proc_pid_readdir(struct file *file, struct dir_context *ctx) ··· 2859 2859 if (pos >= PID_MAX_LIMIT + TGID_OFFSET) 2860 2860 return 0; 2861 2861 2862 - if (pos == TGID_OFFSET - 1) { 2862 + if (pos == TGID_OFFSET - 2) { 2863 2863 struct inode *inode = ns->proc_self->d_inode; 2864 2864 if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) 2865 2865 return 0; 2866 - iter.tgid = 0; 2867 - } else { 2868 - iter.tgid = pos - TGID_OFFSET; 2866 + ctx->pos = pos = pos + 1; 2869 2867 } 2868 + if (pos == TGID_OFFSET - 1) { 2869 + struct inode *inode = ns->proc_thread_self->d_inode; 2870 + if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK)) 2871 + return 0; 2872 + ctx->pos = pos = pos + 1; 2873 + } 2874 + iter.tgid = pos - TGID_OFFSET; 2870 2875 iter.task = NULL; 2871 2876 for (iter = next_tgid(ns, iter); 2872 2877 iter.task;
+6 -1
fs/proc/inode.c
··· 442 442 int proc_fill_super(struct super_block *s) 443 443 { 444 444 struct inode *root_inode; 445 + int ret; 445 446 446 447 s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; 447 448 s->s_blocksize = 1024; ··· 464 463 return -ENOMEM; 465 464 } 466 465 467 - return proc_setup_self(s); 466 + ret = proc_setup_self(s); 467 + if (ret) { 468 + return ret; 469 + } 470 + return proc_setup_thread_self(s); 468 471 }
+6
fs/proc/internal.h
··· 234 234 extern int proc_setup_self(struct super_block *); 235 235 236 236 /* 237 + * proc_thread_self.c 238 + */ 239 + extern int proc_setup_thread_self(struct super_block *); 240 + extern void proc_thread_self_init(void); 241 + 242 + /* 237 243 * proc_sysctl.c 238 244 */ 239 245 #ifdef CONFIG_PROC_SYSCTL
+3
fs/proc/root.c
··· 149 149 ns = (struct pid_namespace *)sb->s_fs_info; 150 150 if (ns->proc_self) 151 151 dput(ns->proc_self); 152 + if (ns->proc_thread_self) 153 + dput(ns->proc_thread_self); 152 154 kill_anon_super(sb); 153 155 put_pid_ns(ns); 154 156 } ··· 172 170 return; 173 171 174 172 proc_self_init(); 173 + proc_thread_self_init(); 175 174 proc_symlink("mounts", NULL, "self/mounts"); 176 175 177 176 proc_net_init();
+85
fs/proc/thread_self.c
··· 1 + #include <linux/sched.h> 2 + #include <linux/namei.h> 3 + #include <linux/slab.h> 4 + #include <linux/pid_namespace.h> 5 + #include "internal.h" 6 + 7 + /* 8 + * /proc/thread_self: 9 + */ 10 + static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, 11 + int buflen) 12 + { 13 + struct pid_namespace *ns = dentry->d_sb->s_fs_info; 14 + pid_t tgid = task_tgid_nr_ns(current, ns); 15 + pid_t pid = task_pid_nr_ns(current, ns); 16 + char tmp[PROC_NUMBUF + 6 + PROC_NUMBUF]; 17 + if (!pid) 18 + return -ENOENT; 19 + sprintf(tmp, "%d/task/%d", tgid, pid); 20 + return readlink_copy(buffer, buflen, tmp); 21 + } 22 + 23 + static void *proc_thread_self_follow_link(struct dentry *dentry, struct nameidata *nd) 24 + { 25 + struct pid_namespace *ns = dentry->d_sb->s_fs_info; 26 + pid_t tgid = task_tgid_nr_ns(current, ns); 27 + pid_t pid = task_pid_nr_ns(current, ns); 28 + char *name = ERR_PTR(-ENOENT); 29 + if (pid) { 30 + name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); 31 + if (!name) 32 + name = ERR_PTR(-ENOMEM); 33 + else 34 + sprintf(name, "%d/task/%d", tgid, pid); 35 + } 36 + nd_set_link(nd, name); 37 + return NULL; 38 + } 39 + 40 + static const struct inode_operations proc_thread_self_inode_operations = { 41 + .readlink = proc_thread_self_readlink, 42 + .follow_link = proc_thread_self_follow_link, 43 + .put_link = kfree_put_link, 44 + }; 45 + 46 + static unsigned thread_self_inum; 47 + 48 + int proc_setup_thread_self(struct super_block *s) 49 + { 50 + struct inode *root_inode = s->s_root->d_inode; 51 + struct pid_namespace *ns = s->s_fs_info; 52 + struct dentry *thread_self; 53 + 54 + mutex_lock(&root_inode->i_mutex); 55 + thread_self = d_alloc_name(s->s_root, "thread-self"); 56 + if (thread_self) { 57 + struct inode *inode = new_inode_pseudo(s); 58 + if (inode) { 59 + inode->i_ino = thread_self_inum; 60 + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; 61 + inode->i_mode = S_IFLNK | S_IRWXUGO; 62 + inode->i_uid = GLOBAL_ROOT_UID; 63 + inode->i_gid = GLOBAL_ROOT_GID; 64 + inode->i_op = &proc_thread_self_inode_operations; 65 + d_add(thread_self, inode); 66 + } else { 67 + dput(thread_self); 68 + thread_self = ERR_PTR(-ENOMEM); 69 + } 70 + } else { 71 + thread_self = ERR_PTR(-ENOMEM); 72 + } 73 + mutex_unlock(&root_inode->i_mutex); 74 + if (IS_ERR(thread_self)) { 75 + pr_err("proc_fill_super: can't allocate /proc/thread_self\n"); 76 + return PTR_ERR(thread_self); 77 + } 78 + ns->proc_thread_self = thread_self; 79 + return 0; 80 + } 81 + 82 + void __init proc_thread_self_init(void) 83 + { 84 + proc_alloc_inum(&thread_self_inum); 85 + }
+1
include/linux/pid_namespace.h
··· 33 33 #ifdef CONFIG_PROC_FS 34 34 struct vfsmount *proc_mnt; 35 35 struct dentry *proc_self; 36 + struct dentry *proc_thread_self; 36 37 #endif 37 38 #ifdef CONFIG_BSD_PROCESS_ACCT 38 39 struct bsd_acct_struct *bacct;