@@ -2948,7 +2948,6 @@ static const struct file_operations proc_task_operations;
static const struct inode_operations proc_task_inode_operations;
static const struct pid_entry tgid_base_stuff[] = {
- DIR("task", S_IRUGO|S_IXUGO, proc_task_inode_operations, proc_task_operations),
DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations),
DIR("map_files", S_IRUSR|S_IXUSR, proc_map_files_inode_operations, proc_map_files_operations),
DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations),
@@ -3047,10 +3046,96 @@ static const struct pid_entry tgid_base_stuff[] = {
#endif
};
+/*
+ * Don't instantiate a full duplicate of the thread leader's task
+ * directory for every member of the task group. Just symlink to the
+ * thread leader's copy.
+ */
+static const char *proc_tgid_task_symlink_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
+{
+ struct task_struct *task;
+ char *link = ERR_PTR(-ENOENT);
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
+ task = get_proc_task(inode);
+ if (task) {
+ struct pid_namespace *ns = inode->i_sb->s_fs_info;
+
+ link = kasprintf(GFP_KERNEL, "../%u/task",
+ pid_nr_ns(task_tgid(task), ns));
+ if (link)
+ set_delayed_call(done, kfree_link, link);
+ else
+ link = ERR_PTR(-ENOMEM);
+ put_task_struct(task);
+ }
+ return link;
+}
+
+static const struct inode_operations proc_task_symlink_inode_operations = {
+ .get_link = proc_tgid_task_symlink_get_link,
+ .setattr = proc_setattr,
+};
+
+static const struct pid_entry proc_tgid_task_symlink_entry = {
+ .name = "task",
+ .len = sizeof("task") - 1,
+ .mode = S_IFLNK|S_IRWXUGO,
+ .iop = &proc_task_symlink_inode_operations,
+};
+
+static const struct pid_entry proc_tgid_task_dir_entry = {
+ .name = "task",
+ .len = sizeof("task") - 1,
+ .mode = S_IFDIR|S_IRUGO|S_IXUGO,
+ .iop = &proc_task_inode_operations,
+ .fop = &proc_task_operations,
+};
+
+static const struct pid_entry *proc_tgid_task_entry(struct task_struct *task)
+{
+ if (thread_group_leader(task))
+ return &proc_tgid_task_dir_entry;
+ else
+ return &proc_tgid_task_symlink_entry;
+}
+
static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
{
- return proc_pident_readdir(file, ctx,
- tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff));
+ const struct pid_entry *entry;
+ struct task_struct *task;
+ int i;
+
+ task = get_proc_task(file_inode(file));
+ if (!task)
+ return -ENOENT;
+
+ if (!dir_emit_dots(file, ctx))
+ goto out;
+
+ /* Add /proc/pid/task entry */
+ if (ctx->pos == 2) {
+ entry = proc_tgid_task_entry(task);
+
+ if (!proc_fill_cache_entry(file, ctx, entry, task))
+ goto out;
+ ctx->pos++;
+ }
+
+ for (i = ctx->pos - 3; i < ARRAY_SIZE(tgid_base_stuff); i++) {
+ entry = &tgid_base_stuff[i];
+
+ if (!proc_fill_cache_entry(file, ctx, entry, task))
+ goto out;
+ ctx->pos++;
+ }
+out:
+ put_task_struct(task);
+ return 0;
}
static const struct file_operations proc_tgid_base_operations = {
@@ -3059,10 +3144,29 @@ static const struct file_operations proc_tgid_base_operations = {
.llseek = generic_file_llseek,
};
-static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
+
+static struct dentry *proc_tgid_base_lookup(struct inode *dir,
+ struct dentry *dentry,
+ unsigned int flags)
{
- return proc_pident_lookup(dir, dentry,
- tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff));
+ struct task_struct *task;
+ int error = -ENOENT;
+
+ task = get_proc_task(dir);
+ if (!task)
+ goto out;
+
+ /* Handle /proc/pid/task separately */
+ if (pid_entry_match_dentry(&proc_tgid_task_dir_entry, dentry))
+ error = proc_pident_instantiate(dir, dentry, task,
+ proc_tgid_task_entry(task));
+ else
+ error = proc_pident_lookup_task(dir, dentry, tgid_base_stuff,
+ ARRAY_SIZE(tgid_base_stuff),
+ task);
+ put_task_struct(task);
+out:
+ return ERR_PTR(error);
}
static const struct inode_operations proc_tgid_base_inode_operations = {
@@ -3163,6 +3267,7 @@ static int proc_pid_instantiate(struct inode *dir,
struct task_struct *task, const void *ptr)
{
struct inode *inode;
+ int nlinks = nlink_tgid;
inode = proc_pid_make_inode(dir->i_sb, task, S_IFDIR | S_IRUGO | S_IXUGO);
if (!inode)
@@ -3172,7 +3277,11 @@ static int proc_pid_instantiate(struct inode *dir,
inode->i_fop = &proc_tgid_base_operations;
inode->i_flags|=S_IMMUTABLE;
- set_nlink(inode, nlink_tgid);
+ /* The group leader has a directory */
+ if (thread_group_leader(task))
+ nlinks++;
+
+ set_nlink(inode, nlinks);
d_set_d_op(dentry, &pid_dentry_operations);