xref: /openbmc/linux/fs/proc/root.c (revision 0d72b928)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/proc/root.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1991, 1992 Linus Torvalds
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  proc root directory handling functions
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds #include <linux/errno.h>
101da177e4SLinus Torvalds #include <linux/time.h>
111da177e4SLinus Torvalds #include <linux/proc_fs.h>
121da177e4SLinus Torvalds #include <linux/stat.h>
131da177e4SLinus Torvalds #include <linux/init.h>
14914e2637SAl Viro #include <linux/sched.h>
1503441a34SIngo Molnar #include <linux/sched/stat.h>
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/bitops.h>
1887a8ebd6SEric W. Biederman #include <linux/user_namespace.h>
1966f592e2SDavid Howells #include <linux/fs_context.h>
20f6c7a1f3SEric W. Biederman #include <linux/mount.h>
2107543f5cSPavel Emelyanov #include <linux/pid_namespace.h>
2266f592e2SDavid Howells #include <linux/fs_parser.h>
235b825c3aSIngo Molnar #include <linux/cred.h>
2460a3c3a5SDavid Howells #include <linux/magic.h>
2566f592e2SDavid Howells #include <linux/slab.h>
261da177e4SLinus Torvalds 
27fee781e6SAdrian Bunk #include "internal.h"
28fee781e6SAdrian Bunk 
2966f592e2SDavid Howells struct proc_fs_context {
3066f592e2SDavid Howells 	struct pid_namespace	*pid_ns;
3166f592e2SDavid Howells 	unsigned int		mask;
32e61bb8b3SAlexey Gladkov 	enum proc_hidepid	hidepid;
3366f592e2SDavid Howells 	int			gid;
34e61bb8b3SAlexey Gladkov 	enum proc_pidonly	pidonly;
3597412950SVasiliy Kulikov };
3697412950SVasiliy Kulikov 
3766f592e2SDavid Howells enum proc_param {
3866f592e2SDavid Howells 	Opt_gid,
3966f592e2SDavid Howells 	Opt_hidepid,
406814ef2dSAlexey Gladkov 	Opt_subset,
4197412950SVasiliy Kulikov };
4297412950SVasiliy Kulikov 
43d7167b14SAl Viro static const struct fs_parameter_spec proc_fs_parameters[] = {
4466f592e2SDavid Howells 	fsparam_u32("gid",	Opt_gid),
451c6c4d11SAlexey Gladkov 	fsparam_string("hidepid",	Opt_hidepid),
466814ef2dSAlexey Gladkov 	fsparam_string("subset",	Opt_subset),
4766f592e2SDavid Howells 	{}
4866f592e2SDavid Howells };
4966f592e2SDavid Howells 
valid_hidepid(unsigned int value)5024a71ce5SAlexey Gladkov static inline int valid_hidepid(unsigned int value)
5124a71ce5SAlexey Gladkov {
5224a71ce5SAlexey Gladkov 	return (value == HIDEPID_OFF ||
5324a71ce5SAlexey Gladkov 		value == HIDEPID_NO_ACCESS ||
5424a71ce5SAlexey Gladkov 		value == HIDEPID_INVISIBLE ||
5524a71ce5SAlexey Gladkov 		value == HIDEPID_NOT_PTRACEABLE);
5624a71ce5SAlexey Gladkov }
5724a71ce5SAlexey Gladkov 
proc_parse_hidepid_param(struct fs_context * fc,struct fs_parameter * param)581c6c4d11SAlexey Gladkov static int proc_parse_hidepid_param(struct fs_context *fc, struct fs_parameter *param)
591c6c4d11SAlexey Gladkov {
601c6c4d11SAlexey Gladkov 	struct proc_fs_context *ctx = fc->fs_private;
611c6c4d11SAlexey Gladkov 	struct fs_parameter_spec hidepid_u32_spec = fsparam_u32("hidepid", Opt_hidepid);
621c6c4d11SAlexey Gladkov 	struct fs_parse_result result;
631c6c4d11SAlexey Gladkov 	int base = (unsigned long)hidepid_u32_spec.data;
641c6c4d11SAlexey Gladkov 
651c6c4d11SAlexey Gladkov 	if (param->type != fs_value_is_string)
661c6c4d11SAlexey Gladkov 		return invalf(fc, "proc: unexpected type of hidepid value\n");
671c6c4d11SAlexey Gladkov 
681c6c4d11SAlexey Gladkov 	if (!kstrtouint(param->string, base, &result.uint_32)) {
691c6c4d11SAlexey Gladkov 		if (!valid_hidepid(result.uint_32))
701c6c4d11SAlexey Gladkov 			return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
711c6c4d11SAlexey Gladkov 		ctx->hidepid = result.uint_32;
721c6c4d11SAlexey Gladkov 		return 0;
731c6c4d11SAlexey Gladkov 	}
741c6c4d11SAlexey Gladkov 
751c6c4d11SAlexey Gladkov 	if (!strcmp(param->string, "off"))
761c6c4d11SAlexey Gladkov 		ctx->hidepid = HIDEPID_OFF;
771c6c4d11SAlexey Gladkov 	else if (!strcmp(param->string, "noaccess"))
781c6c4d11SAlexey Gladkov 		ctx->hidepid = HIDEPID_NO_ACCESS;
791c6c4d11SAlexey Gladkov 	else if (!strcmp(param->string, "invisible"))
801c6c4d11SAlexey Gladkov 		ctx->hidepid = HIDEPID_INVISIBLE;
811c6c4d11SAlexey Gladkov 	else if (!strcmp(param->string, "ptraceable"))
821c6c4d11SAlexey Gladkov 		ctx->hidepid = HIDEPID_NOT_PTRACEABLE;
831c6c4d11SAlexey Gladkov 	else
841c6c4d11SAlexey Gladkov 		return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string);
851c6c4d11SAlexey Gladkov 
861c6c4d11SAlexey Gladkov 	return 0;
871c6c4d11SAlexey Gladkov }
881c6c4d11SAlexey Gladkov 
proc_parse_subset_param(struct fs_context * fc,char * value)896814ef2dSAlexey Gladkov static int proc_parse_subset_param(struct fs_context *fc, char *value)
906814ef2dSAlexey Gladkov {
916814ef2dSAlexey Gladkov 	struct proc_fs_context *ctx = fc->fs_private;
926814ef2dSAlexey Gladkov 
936814ef2dSAlexey Gladkov 	while (value) {
946814ef2dSAlexey Gladkov 		char *ptr = strchr(value, ',');
956814ef2dSAlexey Gladkov 
966814ef2dSAlexey Gladkov 		if (ptr != NULL)
976814ef2dSAlexey Gladkov 			*ptr++ = '\0';
986814ef2dSAlexey Gladkov 
996814ef2dSAlexey Gladkov 		if (*value != '\0') {
1006814ef2dSAlexey Gladkov 			if (!strcmp(value, "pid")) {
1016814ef2dSAlexey Gladkov 				ctx->pidonly = PROC_PIDONLY_ON;
1026814ef2dSAlexey Gladkov 			} else {
1036814ef2dSAlexey Gladkov 				return invalf(fc, "proc: unsupported subset option - %s\n", value);
1046814ef2dSAlexey Gladkov 			}
1056814ef2dSAlexey Gladkov 		}
1066814ef2dSAlexey Gladkov 		value = ptr;
1076814ef2dSAlexey Gladkov 	}
1086814ef2dSAlexey Gladkov 
1096814ef2dSAlexey Gladkov 	return 0;
1106814ef2dSAlexey Gladkov }
1116814ef2dSAlexey Gladkov 
proc_parse_param(struct fs_context * fc,struct fs_parameter * param)11266f592e2SDavid Howells static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
11397412950SVasiliy Kulikov {
11466f592e2SDavid Howells 	struct proc_fs_context *ctx = fc->fs_private;
11566f592e2SDavid Howells 	struct fs_parse_result result;
11666f592e2SDavid Howells 	int opt;
11797412950SVasiliy Kulikov 
118d7167b14SAl Viro 	opt = fs_parse(fc, proc_fs_parameters, param, &result);
11966f592e2SDavid Howells 	if (opt < 0)
12066f592e2SDavid Howells 		return opt;
12197412950SVasiliy Kulikov 
12266f592e2SDavid Howells 	switch (opt) {
1230499680aSVasiliy Kulikov 	case Opt_gid:
12466f592e2SDavid Howells 		ctx->gid = result.uint_32;
1250499680aSVasiliy Kulikov 		break;
12666f592e2SDavid Howells 
1270499680aSVasiliy Kulikov 	case Opt_hidepid:
1281c6c4d11SAlexey Gladkov 		if (proc_parse_hidepid_param(fc, param))
1291c6c4d11SAlexey Gladkov 			return -EINVAL;
1300499680aSVasiliy Kulikov 		break;
13166f592e2SDavid Howells 
1326814ef2dSAlexey Gladkov 	case Opt_subset:
1336814ef2dSAlexey Gladkov 		if (proc_parse_subset_param(fc, param->string) < 0)
1346814ef2dSAlexey Gladkov 			return -EINVAL;
1356814ef2dSAlexey Gladkov 		break;
1366814ef2dSAlexey Gladkov 
13797412950SVasiliy Kulikov 	default:
13866f592e2SDavid Howells 		return -EINVAL;
13966f592e2SDavid Howells 	}
14066f592e2SDavid Howells 
14166f592e2SDavid Howells 	ctx->mask |= 1 << opt;
14297412950SVasiliy Kulikov 	return 0;
14397412950SVasiliy Kulikov }
14497412950SVasiliy Kulikov 
proc_apply_options(struct proc_fs_info * fs_info,struct fs_context * fc,struct user_namespace * user_ns)145fa10fed3SAlexey Gladkov static void proc_apply_options(struct proc_fs_info *fs_info,
14666f592e2SDavid Howells 			       struct fs_context *fc,
14766f592e2SDavid Howells 			       struct user_namespace *user_ns)
14897412950SVasiliy Kulikov {
14966f592e2SDavid Howells 	struct proc_fs_context *ctx = fc->fs_private;
15066f592e2SDavid Howells 
15166f592e2SDavid Howells 	if (ctx->mask & (1 << Opt_gid))
152fa10fed3SAlexey Gladkov 		fs_info->pid_gid = make_kgid(user_ns, ctx->gid);
15366f592e2SDavid Howells 	if (ctx->mask & (1 << Opt_hidepid))
154fa10fed3SAlexey Gladkov 		fs_info->hide_pid = ctx->hidepid;
1556814ef2dSAlexey Gladkov 	if (ctx->mask & (1 << Opt_subset))
1566814ef2dSAlexey Gladkov 		fs_info->pidonly = ctx->pidonly;
15766f592e2SDavid Howells }
15866f592e2SDavid Howells 
proc_fill_super(struct super_block * s,struct fs_context * fc)15966f592e2SDavid Howells static int proc_fill_super(struct super_block *s, struct fs_context *fc)
16066f592e2SDavid Howells {
161fa10fed3SAlexey Gladkov 	struct proc_fs_context *ctx = fc->fs_private;
16260a3c3a5SDavid Howells 	struct inode *root_inode;
163fa10fed3SAlexey Gladkov 	struct proc_fs_info *fs_info;
16460a3c3a5SDavid Howells 	int ret;
16560a3c3a5SDavid Howells 
166fa10fed3SAlexey Gladkov 	fs_info = kzalloc(sizeof(*fs_info), GFP_KERNEL);
167fa10fed3SAlexey Gladkov 	if (!fs_info)
168fa10fed3SAlexey Gladkov 		return -ENOMEM;
169fa10fed3SAlexey Gladkov 
170fa10fed3SAlexey Gladkov 	fs_info->pid_ns = get_pid_ns(ctx->pid_ns);
171fa10fed3SAlexey Gladkov 	proc_apply_options(fs_info, fc, current_user_ns());
17260a3c3a5SDavid Howells 
17360a3c3a5SDavid Howells 	/* User space would break if executables or devices appear on proc */
17460a3c3a5SDavid Howells 	s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV;
17560a3c3a5SDavid Howells 	s->s_flags |= SB_NODIRATIME | SB_NOSUID | SB_NOEXEC;
17660a3c3a5SDavid Howells 	s->s_blocksize = 1024;
17760a3c3a5SDavid Howells 	s->s_blocksize_bits = 10;
17860a3c3a5SDavid Howells 	s->s_magic = PROC_SUPER_MAGIC;
17960a3c3a5SDavid Howells 	s->s_op = &proc_sops;
18060a3c3a5SDavid Howells 	s->s_time_gran = 1;
181fa10fed3SAlexey Gladkov 	s->s_fs_info = fs_info;
18260a3c3a5SDavid Howells 
18360a3c3a5SDavid Howells 	/*
18460a3c3a5SDavid Howells 	 * procfs isn't actually a stacking filesystem; however, there is
18560a3c3a5SDavid Howells 	 * too much magic going on inside it to permit stacking things on
18660a3c3a5SDavid Howells 	 * top of it
18760a3c3a5SDavid Howells 	 */
18860a3c3a5SDavid Howells 	s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
18960a3c3a5SDavid Howells 
19060a3c3a5SDavid Howells 	/* procfs dentries and inodes don't require IO to create */
19160a3c3a5SDavid Howells 	s->s_shrink.seeks = 0;
19260a3c3a5SDavid Howells 
19360a3c3a5SDavid Howells 	pde_get(&proc_root);
19460a3c3a5SDavid Howells 	root_inode = proc_get_inode(s, &proc_root);
19560a3c3a5SDavid Howells 	if (!root_inode) {
19660a3c3a5SDavid Howells 		pr_err("proc_fill_super: get root inode failed\n");
19760a3c3a5SDavid Howells 		return -ENOMEM;
19860a3c3a5SDavid Howells 	}
19960a3c3a5SDavid Howells 
20060a3c3a5SDavid Howells 	s->s_root = d_make_root(root_inode);
20160a3c3a5SDavid Howells 	if (!s->s_root) {
20260a3c3a5SDavid Howells 		pr_err("proc_fill_super: allocate dentry failed\n");
20360a3c3a5SDavid Howells 		return -ENOMEM;
20460a3c3a5SDavid Howells 	}
20560a3c3a5SDavid Howells 
20660a3c3a5SDavid Howells 	ret = proc_setup_self(s);
20760a3c3a5SDavid Howells 	if (ret) {
20860a3c3a5SDavid Howells 		return ret;
20960a3c3a5SDavid Howells 	}
21060a3c3a5SDavid Howells 	return proc_setup_thread_self(s);
21160a3c3a5SDavid Howells }
21260a3c3a5SDavid Howells 
proc_reconfigure(struct fs_context * fc)21366f592e2SDavid Howells static int proc_reconfigure(struct fs_context *fc)
21497412950SVasiliy Kulikov {
21566f592e2SDavid Howells 	struct super_block *sb = fc->root->d_sb;
216fa10fed3SAlexey Gladkov 	struct proc_fs_info *fs_info = proc_sb_info(sb);
21702b9984dSTheodore Ts'o 
21802b9984dSTheodore Ts'o 	sync_filesystem(sb);
21966f592e2SDavid Howells 
220fa10fed3SAlexey Gladkov 	proc_apply_options(fs_info, fc, current_user_ns());
22166f592e2SDavid Howells 	return 0;
22297412950SVasiliy Kulikov }
22397412950SVasiliy Kulikov 
proc_get_tree(struct fs_context * fc)22466f592e2SDavid Howells static int proc_get_tree(struct fs_context *fc)
2251da177e4SLinus Torvalds {
226fa10fed3SAlexey Gladkov 	return get_tree_nodev(fc, proc_fill_super);
22797412950SVasiliy Kulikov }
22807543f5cSPavel Emelyanov 
proc_fs_context_free(struct fs_context * fc)22966f592e2SDavid Howells static void proc_fs_context_free(struct fs_context *fc)
23066f592e2SDavid Howells {
23166f592e2SDavid Howells 	struct proc_fs_context *ctx = fc->fs_private;
23266f592e2SDavid Howells 
23366f592e2SDavid Howells 	put_pid_ns(ctx->pid_ns);
23466f592e2SDavid Howells 	kfree(ctx);
23566f592e2SDavid Howells }
23666f592e2SDavid Howells 
23766f592e2SDavid Howells static const struct fs_context_operations proc_fs_context_ops = {
23866f592e2SDavid Howells 	.free		= proc_fs_context_free,
23966f592e2SDavid Howells 	.parse_param	= proc_parse_param,
24066f592e2SDavid Howells 	.get_tree	= proc_get_tree,
24166f592e2SDavid Howells 	.reconfigure	= proc_reconfigure,
24266f592e2SDavid Howells };
24366f592e2SDavid Howells 
proc_init_fs_context(struct fs_context * fc)24466f592e2SDavid Howells static int proc_init_fs_context(struct fs_context *fc)
24566f592e2SDavid Howells {
24666f592e2SDavid Howells 	struct proc_fs_context *ctx;
24766f592e2SDavid Howells 
24866f592e2SDavid Howells 	ctx = kzalloc(sizeof(struct proc_fs_context), GFP_KERNEL);
24966f592e2SDavid Howells 	if (!ctx)
25066f592e2SDavid Howells 		return -ENOMEM;
25166f592e2SDavid Howells 
25266f592e2SDavid Howells 	ctx->pid_ns = get_pid_ns(task_active_pid_ns(current));
25346cf047aSAl Viro 	put_user_ns(fc->user_ns);
25446cf047aSAl Viro 	fc->user_ns = get_user_ns(ctx->pid_ns->user_ns);
25566f592e2SDavid Howells 	fc->fs_private = ctx;
25666f592e2SDavid Howells 	fc->ops = &proc_fs_context_ops;
25766f592e2SDavid Howells 	return 0;
25807543f5cSPavel Emelyanov }
25907543f5cSPavel Emelyanov 
proc_kill_sb(struct super_block * sb)26007543f5cSPavel Emelyanov static void proc_kill_sb(struct super_block *sb)
26107543f5cSPavel Emelyanov {
262fa10fed3SAlexey Gladkov 	struct proc_fs_info *fs_info = proc_sb_info(sb);
26307543f5cSPavel Emelyanov 
264058f2e4dSAlexey Gladkov 	if (!fs_info) {
265058f2e4dSAlexey Gladkov 		kill_anon_super(sb);
266058f2e4dSAlexey Gladkov 		return;
267058f2e4dSAlexey Gladkov 	}
268fa10fed3SAlexey Gladkov 
269058f2e4dSAlexey Gladkov 	dput(fs_info->proc_self);
270fa10fed3SAlexey Gladkov 	dput(fs_info->proc_thread_self);
271fa10fed3SAlexey Gladkov 
27207543f5cSPavel Emelyanov 	kill_anon_super(sb);
273fa10fed3SAlexey Gladkov 	put_pid_ns(fs_info->pid_ns);
274fa10fed3SAlexey Gladkov 	kfree(fs_info);
2751da177e4SLinus Torvalds }
2761da177e4SLinus Torvalds 
277c2319540SAlexey Dobriyan static struct file_system_type proc_fs_type = {
2781da177e4SLinus Torvalds 	.name			= "proc",
27966f592e2SDavid Howells 	.init_fs_context	= proc_init_fs_context,
280d7167b14SAl Viro 	.parameters		= proc_fs_parameters,
28107543f5cSPavel Emelyanov 	.kill_sb		= proc_kill_sb,
2820b3b094aSJan Kara 	.fs_flags		= FS_USERNS_MOUNT | FS_DISALLOW_NOTIFY_PERM,
2831da177e4SLinus Torvalds };
2841da177e4SLinus Torvalds 
proc_root_init(void)2851da177e4SLinus Torvalds void __init proc_root_init(void)
2861da177e4SLinus Torvalds {
287195b8cf0SAlexey Dobriyan 	proc_init_kmemcache();
2881270dd8dSAlexey Dobriyan 	set_proc_pid_nlink();
289e656d8a6SEric W. Biederman 	proc_self_init();
2900097875bSEric W. Biederman 	proc_thread_self_init();
291155134feSLinus Torvalds 	proc_symlink("mounts", NULL, "self/mounts");
292457c4cbcSEric W. Biederman 
293457c4cbcSEric W. Biederman 	proc_net_init();
29436a5aeb8SAlexey Dobriyan 	proc_mkdir("fs", NULL);
295928b4d8cSAlexey Dobriyan 	proc_mkdir("driver", NULL);
296eb6d38d5SEric W. Biederman 	proc_create_mount_point("fs/nfsd"); /* somewhere for the nfsd filesystem to be mounted */
2971da177e4SLinus Torvalds #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
2981da177e4SLinus Torvalds 	/* just give it a mountpoint */
299eb6d38d5SEric W. Biederman 	proc_create_mount_point("openprom");
3001da177e4SLinus Torvalds #endif
3011da177e4SLinus Torvalds 	proc_tty_init();
3029c37066dSAlexey Dobriyan 	proc_mkdir("bus", NULL);
30377b14db5SEric W. Biederman 	proc_sys_init();
3041539d584SAlexey Dobriyan 
305ed8fb78dSAlexey Dobriyan 	/*
306ed8fb78dSAlexey Dobriyan 	 * Last things last. It is not like userspace processes eager
307ed8fb78dSAlexey Dobriyan 	 * to open /proc files exist at this point but register last
308ed8fb78dSAlexey Dobriyan 	 * anyway.
309ed8fb78dSAlexey Dobriyan 	 */
3101539d584SAlexey Dobriyan 	register_filesystem(&proc_fs_type);
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds 
proc_root_getattr(struct mnt_idmap * idmap,const struct path * path,struct kstat * stat,u32 request_mask,unsigned int query_flags)313b74d24f7SChristian Brauner static int proc_root_getattr(struct mnt_idmap *idmap,
314549c7297SChristian Brauner 			     const struct path *path, struct kstat *stat,
315a528d35eSDavid Howells 			     u32 request_mask, unsigned int query_flags)
31676b6159bSAl Viro {
317*0d72b928SJeff Layton 	generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(path->dentry),
318*0d72b928SJeff Layton 			 stat);
31976b6159bSAl Viro 	stat->nlink = proc_root.nlink + nr_processes();
32076b6159bSAl Viro 	return 0;
32176b6159bSAl Viro }
32276b6159bSAl Viro 
proc_root_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)32300cd8dd3SAl Viro static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry, unsigned int flags)
3241da177e4SLinus Torvalds {
325867aaccfSZhikang Zhang 	if (!proc_pid_lookup(dentry, flags))
3261da177e4SLinus Torvalds 		return NULL;
3271da177e4SLinus Torvalds 
328335eb531SAlexey Dobriyan 	return proc_lookup(dir, dentry, flags);
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds 
proc_root_readdir(struct file * file,struct dir_context * ctx)331f0c3b509SAl Viro static int proc_root_readdir(struct file *file, struct dir_context *ctx)
3321da177e4SLinus Torvalds {
333f0c3b509SAl Viro 	if (ctx->pos < FIRST_PROCESS_ENTRY) {
33494fc5d9dSRichard Genoud 		int error = proc_readdir(file, ctx);
33594fc5d9dSRichard Genoud 		if (unlikely(error <= 0))
33694fc5d9dSRichard Genoud 			return error;
337f0c3b509SAl Viro 		ctx->pos = FIRST_PROCESS_ENTRY;
3381da177e4SLinus Torvalds 	}
3391da177e4SLinus Torvalds 
340f0c3b509SAl Viro 	return proc_pid_readdir(file, ctx);
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds /*
3441da177e4SLinus Torvalds  * The root /proc directory is special, as it has the
3451da177e4SLinus Torvalds  * <pid> directories. Thus we don't use the generic
3461da177e4SLinus Torvalds  * directory handling functions for that..
3471da177e4SLinus Torvalds  */
34800977a59SArjan van de Ven static const struct file_operations proc_root_operations = {
3491da177e4SLinus Torvalds 	.read		 = generic_read_dir,
350f50752eaSAl Viro 	.iterate_shared	 = proc_root_readdir,
351f50752eaSAl Viro 	.llseek		= generic_file_llseek,
3521da177e4SLinus Torvalds };
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds /*
3551da177e4SLinus Torvalds  * proc root can do almost nothing..
3561da177e4SLinus Torvalds  */
357c5ef1c42SArjan van de Ven static const struct inode_operations proc_root_inode_operations = {
3581da177e4SLinus Torvalds 	.lookup		= proc_root_lookup,
35976b6159bSAl Viro 	.getattr	= proc_root_getattr,
3601da177e4SLinus Torvalds };
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds /*
3631da177e4SLinus Torvalds  * This is the root "inode" in the /proc tree..
3641da177e4SLinus Torvalds  */
3651da177e4SLinus Torvalds struct proc_dir_entry proc_root = {
3661da177e4SLinus Torvalds 	.low_ino	= PROC_ROOT_INO,
3671da177e4SLinus Torvalds 	.namelen	= 5,
3681da177e4SLinus Torvalds 	.mode		= S_IFDIR | S_IRUGO | S_IXUGO,
3691da177e4SLinus Torvalds 	.nlink		= 2,
3709cdd83e3SAlexey Dobriyan 	.refcnt		= REFCOUNT_INIT(1),
3711da177e4SLinus Torvalds 	.proc_iops	= &proc_root_inode_operations,
372d56c0d45SAlexey Dobriyan 	.proc_dir_ops	= &proc_root_operations,
3731da177e4SLinus Torvalds 	.parent		= &proc_root,
3744f113437SAlexey Dobriyan 	.subdir		= RB_ROOT,
37524074a35SDavid Howells 	.name		= "/proc",
3761da177e4SLinus Torvalds };
377