162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/proc/root.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * proc root directory handling functions 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/time.h> 1162306a36Sopenharmony_ci#include <linux/proc_fs.h> 1262306a36Sopenharmony_ci#include <linux/stat.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/sched.h> 1562306a36Sopenharmony_ci#include <linux/sched/stat.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/bitops.h> 1862306a36Sopenharmony_ci#include <linux/user_namespace.h> 1962306a36Sopenharmony_ci#include <linux/fs_context.h> 2062306a36Sopenharmony_ci#include <linux/mount.h> 2162306a36Sopenharmony_ci#include <linux/pid_namespace.h> 2262306a36Sopenharmony_ci#include <linux/fs_parser.h> 2362306a36Sopenharmony_ci#include <linux/cred.h> 2462306a36Sopenharmony_ci#include <linux/magic.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "internal.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct proc_fs_context { 3062306a36Sopenharmony_ci struct pid_namespace *pid_ns; 3162306a36Sopenharmony_ci unsigned int mask; 3262306a36Sopenharmony_ci enum proc_hidepid hidepid; 3362306a36Sopenharmony_ci int gid; 3462306a36Sopenharmony_ci enum proc_pidonly pidonly; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cienum proc_param { 3862306a36Sopenharmony_ci Opt_gid, 3962306a36Sopenharmony_ci Opt_hidepid, 4062306a36Sopenharmony_ci Opt_subset, 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct fs_parameter_spec proc_fs_parameters[] = { 4462306a36Sopenharmony_ci fsparam_u32("gid", Opt_gid), 4562306a36Sopenharmony_ci fsparam_string("hidepid", Opt_hidepid), 4662306a36Sopenharmony_ci fsparam_string("subset", Opt_subset), 4762306a36Sopenharmony_ci {} 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic inline int valid_hidepid(unsigned int value) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return (value == HIDEPID_OFF || 5362306a36Sopenharmony_ci value == HIDEPID_NO_ACCESS || 5462306a36Sopenharmony_ci value == HIDEPID_INVISIBLE || 5562306a36Sopenharmony_ci value == HIDEPID_NOT_PTRACEABLE); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int proc_parse_hidepid_param(struct fs_context *fc, struct fs_parameter *param) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct proc_fs_context *ctx = fc->fs_private; 6162306a36Sopenharmony_ci struct fs_parameter_spec hidepid_u32_spec = fsparam_u32("hidepid", Opt_hidepid); 6262306a36Sopenharmony_ci struct fs_parse_result result; 6362306a36Sopenharmony_ci int base = (unsigned long)hidepid_u32_spec.data; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (param->type != fs_value_is_string) 6662306a36Sopenharmony_ci return invalf(fc, "proc: unexpected type of hidepid value\n"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!kstrtouint(param->string, base, &result.uint_32)) { 6962306a36Sopenharmony_ci if (!valid_hidepid(result.uint_32)) 7062306a36Sopenharmony_ci return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string); 7162306a36Sopenharmony_ci ctx->hidepid = result.uint_32; 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!strcmp(param->string, "off")) 7662306a36Sopenharmony_ci ctx->hidepid = HIDEPID_OFF; 7762306a36Sopenharmony_ci else if (!strcmp(param->string, "noaccess")) 7862306a36Sopenharmony_ci ctx->hidepid = HIDEPID_NO_ACCESS; 7962306a36Sopenharmony_ci else if (!strcmp(param->string, "invisible")) 8062306a36Sopenharmony_ci ctx->hidepid = HIDEPID_INVISIBLE; 8162306a36Sopenharmony_ci else if (!strcmp(param->string, "ptraceable")) 8262306a36Sopenharmony_ci ctx->hidepid = HIDEPID_NOT_PTRACEABLE; 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci return invalf(fc, "proc: unknown value of hidepid - %s\n", param->string); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int proc_parse_subset_param(struct fs_context *fc, char *value) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct proc_fs_context *ctx = fc->fs_private; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci while (value) { 9462306a36Sopenharmony_ci char *ptr = strchr(value, ','); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ptr != NULL) 9762306a36Sopenharmony_ci *ptr++ = '\0'; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (*value != '\0') { 10062306a36Sopenharmony_ci if (!strcmp(value, "pid")) { 10162306a36Sopenharmony_ci ctx->pidonly = PROC_PIDONLY_ON; 10262306a36Sopenharmony_ci } else { 10362306a36Sopenharmony_ci return invalf(fc, "proc: unsupported subset option - %s\n", value); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci value = ptr; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int proc_parse_param(struct fs_context *fc, struct fs_parameter *param) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct proc_fs_context *ctx = fc->fs_private; 11562306a36Sopenharmony_ci struct fs_parse_result result; 11662306a36Sopenharmony_ci int opt; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci opt = fs_parse(fc, proc_fs_parameters, param, &result); 11962306a36Sopenharmony_ci if (opt < 0) 12062306a36Sopenharmony_ci return opt; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci switch (opt) { 12362306a36Sopenharmony_ci case Opt_gid: 12462306a36Sopenharmony_ci ctx->gid = result.uint_32; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci case Opt_hidepid: 12862306a36Sopenharmony_ci if (proc_parse_hidepid_param(fc, param)) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci case Opt_subset: 13362306a36Sopenharmony_ci if (proc_parse_subset_param(fc, param->string) < 0) 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci default: 13862306a36Sopenharmony_ci return -EINVAL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ctx->mask |= 1 << opt; 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void proc_apply_options(struct proc_fs_info *fs_info, 14662306a36Sopenharmony_ci struct fs_context *fc, 14762306a36Sopenharmony_ci struct user_namespace *user_ns) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct proc_fs_context *ctx = fc->fs_private; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (ctx->mask & (1 << Opt_gid)) 15262306a36Sopenharmony_ci fs_info->pid_gid = make_kgid(user_ns, ctx->gid); 15362306a36Sopenharmony_ci if (ctx->mask & (1 << Opt_hidepid)) 15462306a36Sopenharmony_ci fs_info->hide_pid = ctx->hidepid; 15562306a36Sopenharmony_ci if (ctx->mask & (1 << Opt_subset)) 15662306a36Sopenharmony_ci fs_info->pidonly = ctx->pidonly; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int proc_fill_super(struct super_block *s, struct fs_context *fc) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct proc_fs_context *ctx = fc->fs_private; 16262306a36Sopenharmony_ci struct inode *root_inode; 16362306a36Sopenharmony_ci struct proc_fs_info *fs_info; 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci fs_info = kzalloc(sizeof(*fs_info), GFP_KERNEL); 16762306a36Sopenharmony_ci if (!fs_info) 16862306a36Sopenharmony_ci return -ENOMEM; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci fs_info->pid_ns = get_pid_ns(ctx->pid_ns); 17162306a36Sopenharmony_ci proc_apply_options(fs_info, fc, current_user_ns()); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* User space would break if executables or devices appear on proc */ 17462306a36Sopenharmony_ci s->s_iflags |= SB_I_USERNS_VISIBLE | SB_I_NOEXEC | SB_I_NODEV; 17562306a36Sopenharmony_ci s->s_flags |= SB_NODIRATIME | SB_NOSUID | SB_NOEXEC; 17662306a36Sopenharmony_ci s->s_blocksize = 1024; 17762306a36Sopenharmony_ci s->s_blocksize_bits = 10; 17862306a36Sopenharmony_ci s->s_magic = PROC_SUPER_MAGIC; 17962306a36Sopenharmony_ci s->s_op = &proc_sops; 18062306a36Sopenharmony_ci s->s_time_gran = 1; 18162306a36Sopenharmony_ci s->s_fs_info = fs_info; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * procfs isn't actually a stacking filesystem; however, there is 18562306a36Sopenharmony_ci * too much magic going on inside it to permit stacking things on 18662306a36Sopenharmony_ci * top of it 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci s->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* procfs dentries and inodes don't require IO to create */ 19162306a36Sopenharmony_ci s->s_shrink.seeks = 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci pde_get(&proc_root); 19462306a36Sopenharmony_ci root_inode = proc_get_inode(s, &proc_root); 19562306a36Sopenharmony_ci if (!root_inode) { 19662306a36Sopenharmony_ci pr_err("proc_fill_super: get root inode failed\n"); 19762306a36Sopenharmony_ci return -ENOMEM; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci s->s_root = d_make_root(root_inode); 20162306a36Sopenharmony_ci if (!s->s_root) { 20262306a36Sopenharmony_ci pr_err("proc_fill_super: allocate dentry failed\n"); 20362306a36Sopenharmony_ci return -ENOMEM; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ret = proc_setup_self(s); 20762306a36Sopenharmony_ci if (ret) { 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci return proc_setup_thread_self(s); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int proc_reconfigure(struct fs_context *fc) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct super_block *sb = fc->root->d_sb; 21662306a36Sopenharmony_ci struct proc_fs_info *fs_info = proc_sb_info(sb); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci sync_filesystem(sb); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci proc_apply_options(fs_info, fc, current_user_ns()); 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int proc_get_tree(struct fs_context *fc) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci return get_tree_nodev(fc, proc_fill_super); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void proc_fs_context_free(struct fs_context *fc) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct proc_fs_context *ctx = fc->fs_private; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci put_pid_ns(ctx->pid_ns); 23462306a36Sopenharmony_ci kfree(ctx); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic const struct fs_context_operations proc_fs_context_ops = { 23862306a36Sopenharmony_ci .free = proc_fs_context_free, 23962306a36Sopenharmony_ci .parse_param = proc_parse_param, 24062306a36Sopenharmony_ci .get_tree = proc_get_tree, 24162306a36Sopenharmony_ci .reconfigure = proc_reconfigure, 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int proc_init_fs_context(struct fs_context *fc) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct proc_fs_context *ctx; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ctx = kzalloc(sizeof(struct proc_fs_context), GFP_KERNEL); 24962306a36Sopenharmony_ci if (!ctx) 25062306a36Sopenharmony_ci return -ENOMEM; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ctx->pid_ns = get_pid_ns(task_active_pid_ns(current)); 25362306a36Sopenharmony_ci put_user_ns(fc->user_ns); 25462306a36Sopenharmony_ci fc->user_ns = get_user_ns(ctx->pid_ns->user_ns); 25562306a36Sopenharmony_ci fc->fs_private = ctx; 25662306a36Sopenharmony_ci fc->ops = &proc_fs_context_ops; 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void proc_kill_sb(struct super_block *sb) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct proc_fs_info *fs_info = proc_sb_info(sb); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!fs_info) { 26562306a36Sopenharmony_ci kill_anon_super(sb); 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci dput(fs_info->proc_self); 27062306a36Sopenharmony_ci dput(fs_info->proc_thread_self); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci kill_anon_super(sb); 27362306a36Sopenharmony_ci put_pid_ns(fs_info->pid_ns); 27462306a36Sopenharmony_ci kfree(fs_info); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic struct file_system_type proc_fs_type = { 27862306a36Sopenharmony_ci .name = "proc", 27962306a36Sopenharmony_ci .init_fs_context = proc_init_fs_context, 28062306a36Sopenharmony_ci .parameters = proc_fs_parameters, 28162306a36Sopenharmony_ci .kill_sb = proc_kill_sb, 28262306a36Sopenharmony_ci .fs_flags = FS_USERNS_MOUNT | FS_DISALLOW_NOTIFY_PERM, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_civoid __init proc_root_init(void) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci proc_init_kmemcache(); 28862306a36Sopenharmony_ci set_proc_pid_nlink(); 28962306a36Sopenharmony_ci proc_self_init(); 29062306a36Sopenharmony_ci proc_thread_self_init(); 29162306a36Sopenharmony_ci proc_symlink("mounts", NULL, "self/mounts"); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci proc_net_init(); 29462306a36Sopenharmony_ci proc_mkdir("fs", NULL); 29562306a36Sopenharmony_ci proc_mkdir("driver", NULL); 29662306a36Sopenharmony_ci proc_create_mount_point("fs/nfsd"); /* somewhere for the nfsd filesystem to be mounted */ 29762306a36Sopenharmony_ci#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) 29862306a36Sopenharmony_ci /* just give it a mountpoint */ 29962306a36Sopenharmony_ci proc_create_mount_point("openprom"); 30062306a36Sopenharmony_ci#endif 30162306a36Sopenharmony_ci proc_tty_init(); 30262306a36Sopenharmony_ci proc_mkdir("bus", NULL); 30362306a36Sopenharmony_ci proc_sys_init(); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * Last things last. It is not like userspace processes eager 30762306a36Sopenharmony_ci * to open /proc files exist at this point but register last 30862306a36Sopenharmony_ci * anyway. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci register_filesystem(&proc_fs_type); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int proc_root_getattr(struct mnt_idmap *idmap, 31462306a36Sopenharmony_ci const struct path *path, struct kstat *stat, 31562306a36Sopenharmony_ci u32 request_mask, unsigned int query_flags) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci generic_fillattr(&nop_mnt_idmap, request_mask, d_inode(path->dentry), 31862306a36Sopenharmony_ci stat); 31962306a36Sopenharmony_ci stat->nlink = proc_root.nlink + nr_processes(); 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry, unsigned int flags) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci if (!proc_pid_lookup(dentry, flags)) 32662306a36Sopenharmony_ci return NULL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return proc_lookup(dir, dentry, flags); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int proc_root_readdir(struct file *file, struct dir_context *ctx) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci if (ctx->pos < FIRST_PROCESS_ENTRY) { 33462306a36Sopenharmony_ci int error = proc_readdir(file, ctx); 33562306a36Sopenharmony_ci if (unlikely(error <= 0)) 33662306a36Sopenharmony_ci return error; 33762306a36Sopenharmony_ci ctx->pos = FIRST_PROCESS_ENTRY; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return proc_pid_readdir(file, ctx); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* 34462306a36Sopenharmony_ci * The root /proc directory is special, as it has the 34562306a36Sopenharmony_ci * <pid> directories. Thus we don't use the generic 34662306a36Sopenharmony_ci * directory handling functions for that.. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_cistatic const struct file_operations proc_root_operations = { 34962306a36Sopenharmony_ci .read = generic_read_dir, 35062306a36Sopenharmony_ci .iterate_shared = proc_root_readdir, 35162306a36Sopenharmony_ci .llseek = generic_file_llseek, 35262306a36Sopenharmony_ci}; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/* 35562306a36Sopenharmony_ci * proc root can do almost nothing.. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistatic const struct inode_operations proc_root_inode_operations = { 35862306a36Sopenharmony_ci .lookup = proc_root_lookup, 35962306a36Sopenharmony_ci .getattr = proc_root_getattr, 36062306a36Sopenharmony_ci}; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * This is the root "inode" in the /proc tree.. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_cistruct proc_dir_entry proc_root = { 36662306a36Sopenharmony_ci .low_ino = PROC_ROOT_INO, 36762306a36Sopenharmony_ci .namelen = 5, 36862306a36Sopenharmony_ci .mode = S_IFDIR | S_IRUGO | S_IXUGO, 36962306a36Sopenharmony_ci .nlink = 2, 37062306a36Sopenharmony_ci .refcnt = REFCOUNT_INIT(1), 37162306a36Sopenharmony_ci .proc_iops = &proc_root_inode_operations, 37262306a36Sopenharmony_ci .proc_dir_ops = &proc_root_operations, 37362306a36Sopenharmony_ci .parent = &proc_root, 37462306a36Sopenharmony_ci .subdir = RB_ROOT, 37562306a36Sopenharmony_ci .name = "/proc", 37662306a36Sopenharmony_ci}; 377