| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  linux/fs/proc/root.c | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 1991, 1992 Linus Torvalds | 
|  | 5 | * | 
|  | 6 | *  proc root directory handling functions | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include <asm/uaccess.h> | 
|  | 10 |  | 
|  | 11 | #include <linux/errno.h> | 
|  | 12 | #include <linux/time.h> | 
|  | 13 | #include <linux/proc_fs.h> | 
|  | 14 | #include <linux/stat.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 15 | #include <linux/init.h> | 
| Al Viro | 914e263 | 2006-10-18 13:55:46 -0400 | [diff] [blame] | 16 | #include <linux/sched.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 | #include <linux/module.h> | 
|  | 18 | #include <linux/bitops.h> | 
| Eric W. Biederman | f6c7a1f | 2006-10-02 02:17:07 -0700 | [diff] [blame] | 19 | #include <linux/mount.h> | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 20 | #include <linux/pid_namespace.h> | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 21 | #include <linux/parser.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 22 |  | 
| Adrian Bunk | fee781e | 2006-01-08 01:04:16 -0800 | [diff] [blame] | 23 | #include "internal.h" | 
|  | 24 |  | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 25 | static int proc_test_super(struct super_block *sb, void *data) | 
|  | 26 | { | 
|  | 27 | return sb->s_fs_info == data; | 
|  | 28 | } | 
|  | 29 |  | 
|  | 30 | static int proc_set_super(struct super_block *sb, void *data) | 
|  | 31 | { | 
| Al Viro | ff78fca | 2011-06-12 09:42:17 -0400 | [diff] [blame] | 32 | int err = set_anon_super(sb, NULL); | 
|  | 33 | if (!err) { | 
|  | 34 | struct pid_namespace *ns = (struct pid_namespace *)data; | 
|  | 35 | sb->s_fs_info = get_pid_ns(ns); | 
|  | 36 | } | 
|  | 37 | return err; | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 38 | } | 
|  | 39 |  | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 40 | enum { | 
| Vasiliy Kulikov | 0499680 | 2012-01-10 15:11:31 -0800 | [diff] [blame] | 41 | Opt_gid, Opt_hidepid, Opt_err, | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 42 | }; | 
|  | 43 |  | 
|  | 44 | static const match_table_t tokens = { | 
| Vasiliy Kulikov | 0499680 | 2012-01-10 15:11:31 -0800 | [diff] [blame] | 45 | {Opt_hidepid, "hidepid=%u"}, | 
|  | 46 | {Opt_gid, "gid=%u"}, | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 47 | {Opt_err, NULL}, | 
|  | 48 | }; | 
|  | 49 |  | 
|  | 50 | static int proc_parse_options(char *options, struct pid_namespace *pid) | 
|  | 51 | { | 
|  | 52 | char *p; | 
|  | 53 | substring_t args[MAX_OPT_ARGS]; | 
| Vasiliy Kulikov | 0499680 | 2012-01-10 15:11:31 -0800 | [diff] [blame] | 54 | int option; | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 55 |  | 
|  | 56 | if (!options) | 
|  | 57 | return 1; | 
|  | 58 |  | 
|  | 59 | while ((p = strsep(&options, ",")) != NULL) { | 
|  | 60 | int token; | 
|  | 61 | if (!*p) | 
|  | 62 | continue; | 
|  | 63 |  | 
|  | 64 | args[0].to = args[0].from = 0; | 
|  | 65 | token = match_token(p, tokens, args); | 
|  | 66 | switch (token) { | 
| Vasiliy Kulikov | 0499680 | 2012-01-10 15:11:31 -0800 | [diff] [blame] | 67 | case Opt_gid: | 
|  | 68 | if (match_int(&args[0], &option)) | 
|  | 69 | return 0; | 
| Eric W. Biederman | dcb0f22 | 2012-02-09 08:48:21 -0800 | [diff] [blame] | 70 | pid->pid_gid = make_kgid(current_user_ns(), option); | 
| Vasiliy Kulikov | 0499680 | 2012-01-10 15:11:31 -0800 | [diff] [blame] | 71 | break; | 
|  | 72 | case Opt_hidepid: | 
|  | 73 | if (match_int(&args[0], &option)) | 
|  | 74 | return 0; | 
|  | 75 | if (option < 0 || option > 2) { | 
|  | 76 | pr_err("proc: hidepid value must be between 0 and 2.\n"); | 
|  | 77 | return 0; | 
|  | 78 | } | 
|  | 79 | pid->hide_pid = option; | 
|  | 80 | break; | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 81 | default: | 
|  | 82 | pr_err("proc: unrecognized mount option \"%s\" " | 
|  | 83 | "or missing value\n", p); | 
|  | 84 | return 0; | 
|  | 85 | } | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | return 1; | 
|  | 89 | } | 
|  | 90 |  | 
|  | 91 | int proc_remount(struct super_block *sb, int *flags, char *data) | 
|  | 92 | { | 
|  | 93 | struct pid_namespace *pid = sb->s_fs_info; | 
|  | 94 | return !proc_parse_options(data, pid); | 
|  | 95 | } | 
|  | 96 |  | 
| Al Viro | aed1d84 | 2010-07-26 13:12:54 +0400 | [diff] [blame] | 97 | static struct dentry *proc_mount(struct file_system_type *fs_type, | 
|  | 98 | int flags, const char *dev_name, void *data) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | { | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 100 | int err; | 
|  | 101 | struct super_block *sb; | 
|  | 102 | struct pid_namespace *ns; | 
|  | 103 | struct proc_inode *ei; | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 104 | char *options; | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 105 |  | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 106 | if (flags & MS_KERNMOUNT) { | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 107 | ns = (struct pid_namespace *)data; | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 108 | options = NULL; | 
|  | 109 | } else { | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 110 | ns = current->nsproxy->pid_ns; | 
| Vasiliy Kulikov | 9741295 | 2012-01-10 15:11:27 -0800 | [diff] [blame] | 111 | options = data; | 
|  | 112 | } | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 113 |  | 
| David Howells | 9249e17 | 2012-06-25 12:55:37 +0100 | [diff] [blame] | 114 | sb = sget(fs_type, proc_test_super, proc_set_super, flags, ns); | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 115 | if (IS_ERR(sb)) | 
| Al Viro | aed1d84 | 2010-07-26 13:12:54 +0400 | [diff] [blame] | 116 | return ERR_CAST(sb); | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 117 |  | 
| Vasiliy Kulikov | 99663be | 2012-04-05 14:25:04 -0700 | [diff] [blame] | 118 | if (!proc_parse_options(options, ns)) { | 
|  | 119 | deactivate_locked_super(sb); | 
|  | 120 | return ERR_PTR(-EINVAL); | 
|  | 121 | } | 
|  | 122 |  | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 123 | if (!sb->s_root) { | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 124 | err = proc_fill_super(sb); | 
|  | 125 | if (err) { | 
| Al Viro | 6f5bbff | 2009-05-06 01:34:22 -0400 | [diff] [blame] | 126 | deactivate_locked_super(sb); | 
| Al Viro | aed1d84 | 2010-07-26 13:12:54 +0400 | [diff] [blame] | 127 | return ERR_PTR(err); | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 128 | } | 
|  | 129 |  | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 130 | sb->s_flags |= MS_ACTIVE; | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 131 | } | 
|  | 132 |  | 
| Eric W. Biederman | 4308eeb | 2011-03-23 16:43:13 -0700 | [diff] [blame] | 133 | ei = PROC_I(sb->s_root->d_inode); | 
|  | 134 | if (!ei->pid) { | 
|  | 135 | rcu_read_lock(); | 
|  | 136 | ei->pid = get_pid(find_pid_ns(1, ns)); | 
|  | 137 | rcu_read_unlock(); | 
|  | 138 | } | 
|  | 139 |  | 
| Al Viro | aed1d84 | 2010-07-26 13:12:54 +0400 | [diff] [blame] | 140 | return dget(sb->s_root); | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 141 | } | 
|  | 142 |  | 
|  | 143 | static void proc_kill_sb(struct super_block *sb) | 
|  | 144 | { | 
|  | 145 | struct pid_namespace *ns; | 
|  | 146 |  | 
|  | 147 | ns = (struct pid_namespace *)sb->s_fs_info; | 
|  | 148 | kill_anon_super(sb); | 
|  | 149 | put_pid_ns(ns); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 150 | } | 
|  | 151 |  | 
| Alexey Dobriyan | c231954 | 2007-11-28 16:21:23 -0800 | [diff] [blame] | 152 | static struct file_system_type proc_fs_type = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 153 | .name		= "proc", | 
| Al Viro | aed1d84 | 2010-07-26 13:12:54 +0400 | [diff] [blame] | 154 | .mount		= proc_mount, | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 155 | .kill_sb	= proc_kill_sb, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 156 | }; | 
|  | 157 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 | void __init proc_root_init(void) | 
|  | 159 | { | 
| Alexey Dobriyan | 5bcd7ff | 2008-10-17 03:43:55 +0400 | [diff] [blame] | 160 | int err; | 
|  | 161 |  | 
|  | 162 | proc_init_inodecache(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 | err = register_filesystem(&proc_fs_type); | 
|  | 164 | if (err) | 
|  | 165 | return; | 
| Al Viro | 905ad26 | 2011-12-08 23:20:45 -0500 | [diff] [blame] | 166 | err = pid_ns_prepare_proc(&init_pid_ns); | 
|  | 167 | if (err) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 168 | unregister_filesystem(&proc_fs_type); | 
|  | 169 | return; | 
|  | 170 | } | 
| Pavel Emelyanov | 07543f5 | 2007-10-18 23:40:08 -0700 | [diff] [blame] | 171 |  | 
| Alexey Dobriyan | 59c7572 | 2008-10-06 14:49:39 +0400 | [diff] [blame] | 172 | proc_symlink("mounts", NULL, "self/mounts"); | 
| Eric W. Biederman | 457c4cb | 2007-09-12 12:01:34 +0200 | [diff] [blame] | 173 |  | 
|  | 174 | proc_net_init(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 |  | 
|  | 176 | #ifdef CONFIG_SYSVIPC | 
|  | 177 | proc_mkdir("sysvipc", NULL); | 
|  | 178 | #endif | 
| Alexey Dobriyan | 36a5aeb | 2008-04-29 01:01:42 -0700 | [diff] [blame] | 179 | proc_mkdir("fs", NULL); | 
| Alexey Dobriyan | 928b4d8 | 2008-04-29 01:01:44 -0700 | [diff] [blame] | 180 | proc_mkdir("driver", NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 181 | proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */ | 
|  | 182 | #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) | 
|  | 183 | /* just give it a mountpoint */ | 
|  | 184 | proc_mkdir("openprom", NULL); | 
|  | 185 | #endif | 
|  | 186 | proc_tty_init(); | 
|  | 187 | #ifdef CONFIG_PROC_DEVICETREE | 
|  | 188 | proc_device_tree_init(); | 
|  | 189 | #endif | 
| Alexey Dobriyan | 9c37066 | 2008-04-29 01:01:41 -0700 | [diff] [blame] | 190 | proc_mkdir("bus", NULL); | 
| Eric W. Biederman | 77b14db | 2007-02-14 00:34:12 -0800 | [diff] [blame] | 191 | proc_sys_init(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 192 | } | 
|  | 193 |  | 
| Al Viro | 76b6159 | 2006-02-08 14:37:40 -0500 | [diff] [blame] | 194 | static int proc_root_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat | 
|  | 195 | ) | 
|  | 196 | { | 
|  | 197 | generic_fillattr(dentry->d_inode, stat); | 
|  | 198 | stat->nlink = proc_root.nlink + nr_processes(); | 
|  | 199 | return 0; | 
|  | 200 | } | 
|  | 201 |  | 
| Al Viro | 00cd8dd | 2012-06-10 17:13:09 -0400 | [diff] [blame] | 202 | static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry, unsigned int flags) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 203 | { | 
| Al Viro | 00cd8dd | 2012-06-10 17:13:09 -0400 | [diff] [blame] | 204 | if (!proc_lookup(dir, dentry, flags)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 205 | return NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 206 |  | 
| Al Viro | 00cd8dd | 2012-06-10 17:13:09 -0400 | [diff] [blame] | 207 | return proc_pid_lookup(dir, dentry, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 208 | } | 
|  | 209 |  | 
|  | 210 | static int proc_root_readdir(struct file * filp, | 
|  | 211 | void * dirent, filldir_t filldir) | 
|  | 212 | { | 
|  | 213 | unsigned int nr = filp->f_pos; | 
|  | 214 | int ret; | 
|  | 215 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 | if (nr < FIRST_PROCESS_ENTRY) { | 
|  | 217 | int error = proc_readdir(filp, dirent, filldir); | 
| Alexey Dobriyan | b4df2b9 | 2008-10-27 22:48:36 +0300 | [diff] [blame] | 218 | if (error <= 0) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 219 | return error; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | filp->f_pos = FIRST_PROCESS_ENTRY; | 
|  | 221 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 222 |  | 
|  | 223 | ret = proc_pid_readdir(filp, dirent, filldir); | 
|  | 224 | return ret; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | /* | 
|  | 228 | * The root /proc directory is special, as it has the | 
|  | 229 | * <pid> directories. Thus we don't use the generic | 
|  | 230 | * directory handling functions for that.. | 
|  | 231 | */ | 
| Arjan van de Ven | 00977a5 | 2007-02-12 00:55:34 -0800 | [diff] [blame] | 232 | static const struct file_operations proc_root_operations = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 233 | .read		 = generic_read_dir, | 
|  | 234 | .readdir	 = proc_root_readdir, | 
| Arnd Bergmann | 6038f37 | 2010-08-15 18:52:59 +0200 | [diff] [blame] | 235 | .llseek		= default_llseek, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 | }; | 
|  | 237 |  | 
|  | 238 | /* | 
|  | 239 | * proc root can do almost nothing.. | 
|  | 240 | */ | 
| Arjan van de Ven | c5ef1c4 | 2007-02-12 00:55:40 -0800 | [diff] [blame] | 241 | static const struct inode_operations proc_root_inode_operations = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 242 | .lookup		= proc_root_lookup, | 
| Al Viro | 76b6159 | 2006-02-08 14:37:40 -0500 | [diff] [blame] | 243 | .getattr	= proc_root_getattr, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 244 | }; | 
|  | 245 |  | 
|  | 246 | /* | 
|  | 247 | * This is the root "inode" in the /proc tree.. | 
|  | 248 | */ | 
|  | 249 | struct proc_dir_entry proc_root = { | 
|  | 250 | .low_ino	= PROC_ROOT_INO, | 
|  | 251 | .namelen	= 5, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 252 | .mode		= S_IFDIR | S_IRUGO | S_IXUGO, | 
|  | 253 | .nlink		= 2, | 
| Alexey Dobriyan | 5a622f2 | 2007-12-04 23:45:28 -0800 | [diff] [blame] | 254 | .count		= ATOMIC_INIT(1), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 255 | .proc_iops	= &proc_root_inode_operations, | 
|  | 256 | .proc_fops	= &proc_root_operations, | 
|  | 257 | .parent		= &proc_root, | 
| David Howells | 09570f9 | 2011-07-27 21:47:03 +0300 | [diff] [blame] | 258 | .name		= "/proc", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 259 | }; | 
|  | 260 |  | 
| Pavel Emelyanov | 6f4e643 | 2007-10-18 23:40:11 -0700 | [diff] [blame] | 261 | int pid_ns_prepare_proc(struct pid_namespace *ns) | 
|  | 262 | { | 
|  | 263 | struct vfsmount *mnt; | 
|  | 264 |  | 
|  | 265 | mnt = kern_mount_data(&proc_fs_type, ns); | 
|  | 266 | if (IS_ERR(mnt)) | 
|  | 267 | return PTR_ERR(mnt); | 
|  | 268 |  | 
| Al Viro | 579441a | 2010-07-26 13:09:36 +0400 | [diff] [blame] | 269 | ns->proc_mnt = mnt; | 
| Pavel Emelyanov | 6f4e643 | 2007-10-18 23:40:11 -0700 | [diff] [blame] | 270 | return 0; | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | void pid_ns_release_proc(struct pid_namespace *ns) | 
|  | 274 | { | 
| Al Viro | 905ad26 | 2011-12-08 23:20:45 -0500 | [diff] [blame] | 275 | kern_unmount(ns->proc_mnt); | 
| Pavel Emelyanov | 6f4e643 | 2007-10-18 23:40:11 -0700 | [diff] [blame] | 276 | } |