Blame view

fs/proc/proc_sysctl.c 45.4 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
2
3
4
  /*
   * /proc/sys support
   */
1e0edd3f6   Alexey Dobriyan   proc: spread __init
5
  #include <linux/init.h>
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
6
  #include <linux/sysctl.h>
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
7
  #include <linux/poll.h>
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
8
  #include <linux/proc_fs.h>
87ebdc00e   Andrew Morton   fs/proc: clean up...
9
  #include <linux/printk.h>
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
10
  #include <linux/security.h>
404015308   Al Viro   security: trim se...
11
  #include <linux/sched.h>
5b825c3af   Ingo Molnar   sched/headers: Pr...
12
  #include <linux/cred.h>
34286d666   Nick Piggin   fs: rcu-walk awar...
13
  #include <linux/namei.h>
404015308   Al Viro   security: trim se...
14
  #include <linux/mm.h>
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
15
  #include <linux/uio.h>
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
16
  #include <linux/module.h>
7b146cebe   Andrey Ignatov   bpf: Sysctl hook
17
  #include <linux/bpf-cgroup.h>
3db978d48   Vlastimil Babka   kernel/sysctl: su...
18
  #include <linux/mount.h>
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
19
  #include "internal.h"
d72f71eb0   Al Viro   constify dentry_o...
20
  static const struct dentry_operations proc_sys_dentry_operations;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
21
  static const struct file_operations proc_sys_file_operations;
03a44825b   Jan Engelhardt   procfs: constify ...
22
  static const struct inode_operations proc_sys_inode_operations;
9043476f7   Al Viro   [PATCH] sanitize ...
23
24
  static const struct file_operations proc_sys_dir_file_operations;
  static const struct inode_operations proc_sys_dir_operations;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
25

eec4844fa   Matteo Croce   proc/sysctl: add ...
26
27
28
  /* shared constants to be used in various sysctls */
  const int sysctl_vals[] = { 0, 1, INT_MAX };
  EXPORT_SYMBOL(sysctl_vals);
f9bd6733d   Eric W. Biederman   sysctl: Allow cre...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  /* Support for permanently empty directories */
  
  struct ctl_table sysctl_mount_point[] = {
  	{ }
  };
  
  static bool is_empty_dir(struct ctl_table_header *head)
  {
  	return head->ctl_table[0].child == sysctl_mount_point;
  }
  
  static void set_empty_dir(struct ctl_dir *dir)
  {
  	dir->header.ctl_table[0].child = sysctl_mount_point;
  }
  
  static void clear_empty_dir(struct ctl_dir *dir)
  
  {
  	dir->header.ctl_table[0].child = NULL;
  }
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
50
51
52
53
54
55
56
57
  void proc_sys_poll_notify(struct ctl_table_poll *poll)
  {
  	if (!poll)
  		return;
  
  	atomic_inc(&poll->event);
  	wake_up_interruptible(&poll->wait);
  }
a194558e8   Eric W. Biederman   sysctl: Normalize...
58
59
60
  static struct ctl_table root_table[] = {
  	{
  		.procname = "",
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
61
  		.mode = S_IFDIR|S_IRUGO|S_IXUGO,
a194558e8   Eric W. Biederman   sysctl: Normalize...
62
63
64
  	},
  	{ }
  };
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
65
  static struct ctl_table_root sysctl_table_root = {
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
66
  	.default_set.dir.header = {
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
67
68
  		{{.count = 1,
  		  .nreg = 1,
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
69
  		  .ctl_table = root_table }},
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
70
  		.ctl_table_arg = root_table,
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
71
72
73
  		.root = &sysctl_table_root,
  		.set = &sysctl_table_root.default_set,
  	},
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
74
  };
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
75
76
  
  static DEFINE_SPINLOCK(sysctl_lock);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
77
  static void drop_sysctl_table(struct ctl_table_header *header);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
78
  static int sysctl_follow_link(struct ctl_table_header **phead,
13bcc6a28   Eric W. Biederman   sysctl: Stop impl...
79
  	struct ctl_table **pentry);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
80
81
  static int insert_links(struct ctl_table_header *head);
  static void put_links(struct ctl_table_header *header);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
82

6980128fe   Eric W. Biederman   sysctl: Add sysct...
83
84
85
86
  static void sysctl_print_dir(struct ctl_dir *dir)
  {
  	if (dir->header.parent)
  		sysctl_print_dir(dir->header.parent);
87ebdc00e   Andrew Morton   fs/proc: clean up...
87
  	pr_cont("%s/", dir->header.ctl_table[0].procname);
6980128fe   Eric W. Biederman   sysctl: Add sysct...
88
  }
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  static int namecmp(const char *name1, int len1, const char *name2, int len2)
  {
  	int minlen;
  	int cmp;
  
  	minlen = len1;
  	if (minlen > len2)
  		minlen = len2;
  
  	cmp = memcmp(name1, name2, minlen);
  	if (cmp == 0)
  		cmp = len1 - len2;
  	return cmp;
  }
60f126d93   Eric W. Biederman   sysctl: Comments ...
103
  /* Called under sysctl_lock */
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
104
  static struct ctl_table *find_entry(struct ctl_table_header **phead,
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
105
  	struct ctl_dir *dir, const char *name, int namelen)
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
106
107
108
  {
  	struct ctl_table_header *head;
  	struct ctl_table *entry;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
109
  	struct rb_node *node = dir->root.rb_node;
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
110

ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  	while (node)
  	{
  		struct ctl_node *ctl_node;
  		const char *procname;
  		int cmp;
  
  		ctl_node = rb_entry(node, struct ctl_node, node);
  		head = ctl_node->header;
  		entry = &head->ctl_table[ctl_node - head->node];
  		procname = entry->procname;
  
  		cmp = namecmp(name, namelen, procname, strlen(procname));
  		if (cmp < 0)
  			node = node->rb_left;
  		else if (cmp > 0)
  			node = node->rb_right;
  		else {
  			*phead = head;
  			return entry;
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
130
131
132
133
  		}
  	}
  	return NULL;
  }
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  static int insert_entry(struct ctl_table_header *head, struct ctl_table *entry)
  {
  	struct rb_node *node = &head->node[entry - head->ctl_table].node;
  	struct rb_node **p = &head->parent->root.rb_node;
  	struct rb_node *parent = NULL;
  	const char *name = entry->procname;
  	int namelen = strlen(name);
  
  	while (*p) {
  		struct ctl_table_header *parent_head;
  		struct ctl_table *parent_entry;
  		struct ctl_node *parent_node;
  		const char *parent_name;
  		int cmp;
  
  		parent = *p;
  		parent_node = rb_entry(parent, struct ctl_node, node);
  		parent_head = parent_node->header;
  		parent_entry = &parent_head->ctl_table[parent_node - parent_head->node];
  		parent_name = parent_entry->procname;
  
  		cmp = namecmp(name, namelen, parent_name, strlen(parent_name));
  		if (cmp < 0)
  			p = &(*p)->rb_left;
  		else if (cmp > 0)
  			p = &(*p)->rb_right;
  		else {
87ebdc00e   Andrew Morton   fs/proc: clean up...
161
  			pr_err("sysctl duplicate entry: ");
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
162
  			sysctl_print_dir(head->parent);
87ebdc00e   Andrew Morton   fs/proc: clean up...
163
164
  			pr_cont("/%s
  ", entry->procname);
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
165
166
167
168
169
  			return -EEXIST;
  		}
  	}
  
  	rb_link_node(node, parent, p);
ea5272f5c   Michel Lespinasse   rbtree: fix incor...
170
  	rb_insert_color(node, &head->parent->root);
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
171
172
173
174
175
176
177
178
179
  	return 0;
  }
  
  static void erase_entry(struct ctl_table_header *head, struct ctl_table *entry)
  {
  	struct rb_node *node = &head->node[entry - head->ctl_table].node;
  
  	rb_erase(node, &head->parent->root);
  }
e0d045290   Eric W. Biederman   sysctl: Factor ou...
180
181
  static void init_header(struct ctl_table_header *head,
  	struct ctl_table_root *root, struct ctl_table_set *set,
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
182
  	struct ctl_node *node, struct ctl_table *table)
e0d045290   Eric W. Biederman   sysctl: Factor ou...
183
  {
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
184
  	head->ctl_table = table;
e0d045290   Eric W. Biederman   sysctl: Factor ou...
185
  	head->ctl_table_arg = table;
e0d045290   Eric W. Biederman   sysctl: Factor ou...
186
187
188
189
190
191
192
  	head->used = 0;
  	head->count = 1;
  	head->nreg = 1;
  	head->unregistering = NULL;
  	head->root = root;
  	head->set = set;
  	head->parent = NULL;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
193
  	head->node = node;
2fd1d2c4c   Eric W. Biederman   proc: Fix proc_sy...
194
  	INIT_HLIST_HEAD(&head->inodes);
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
195
196
  	if (node) {
  		struct ctl_table *entry;
4c199a93a   Michel Lespinasse   rbtree: empty nod...
197
  		for (entry = table; entry->procname; entry++, node++)
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
198
  			node->header = head;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
199
  	}
e0d045290   Eric W. Biederman   sysctl: Factor ou...
200
  }
8425d6aaf   Eric W. Biederman   sysctl: Factor ou...
201
202
  static void erase_header(struct ctl_table_header *head)
  {
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
203
204
205
  	struct ctl_table *entry;
  	for (entry = head->ctl_table; entry->procname; entry++)
  		erase_entry(head, entry);
8425d6aaf   Eric W. Biederman   sysctl: Factor ou...
206
  }
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
207
  static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
8425d6aaf   Eric W. Biederman   sysctl: Factor ou...
208
  {
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
209
  	struct ctl_table *entry;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
210
  	int err;
f9bd6733d   Eric W. Biederman   sysctl: Allow cre...
211
212
213
214
215
216
217
218
219
220
  	/* Is this a permanently empty directory? */
  	if (is_empty_dir(&dir->header))
  		return -EROFS;
  
  	/* Am I creating a permanently empty directory? */
  	if (header->ctl_table == sysctl_mount_point) {
  		if (!RB_EMPTY_ROOT(&dir->root))
  			return -EINVAL;
  		set_empty_dir(dir);
  	}
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
221
  	dir->header.nreg++;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
222
  	header->parent = dir;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
223
224
225
  	err = insert_links(header);
  	if (err)
  		goto fail_links;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
226
227
228
229
230
  	for (entry = header->ctl_table; entry->procname; entry++) {
  		err = insert_entry(header, entry);
  		if (err)
  			goto fail;
  	}
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
231
  	return 0;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
232
233
234
  fail:
  	erase_header(header);
  	put_links(header);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
235
  fail_links:
f9bd6733d   Eric W. Biederman   sysctl: Allow cre...
236
237
  	if (header->ctl_table == sysctl_mount_point)
  		clear_empty_dir(dir);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
238
239
240
  	header->parent = NULL;
  	drop_sysctl_table(&dir->header);
  	return err;
8425d6aaf   Eric W. Biederman   sysctl: Factor ou...
241
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  /* called under sysctl_lock */
  static int use_table(struct ctl_table_header *p)
  {
  	if (unlikely(p->unregistering))
  		return 0;
  	p->used++;
  	return 1;
  }
  
  /* called under sysctl_lock */
  static void unuse_table(struct ctl_table_header *p)
  {
  	if (!--p->used)
  		if (unlikely(p->unregistering))
  			complete(p->unregistering);
  }
f90f3cafe   Eric W. Biederman   proc: Use d_inval...
258
  static void proc_sys_invalidate_dcache(struct ctl_table_header *head)
d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
259
  {
f90f3cafe   Eric W. Biederman   proc: Use d_inval...
260
  	proc_invalidate_siblings_dcache(&head->inodes, &sysctl_lock);
d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
261
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
262
263
264
265
266
267
268
269
270
271
272
273
274
  /* called under sysctl_lock, will reacquire if has to wait */
  static void start_unregistering(struct ctl_table_header *p)
  {
  	/*
  	 * if p->used is 0, nobody will ever touch that entry again;
  	 * we'll eliminate all paths to it before dropping sysctl_lock
  	 */
  	if (unlikely(p->used)) {
  		struct completion wait;
  		init_completion(&wait);
  		p->unregistering = &wait;
  		spin_unlock(&sysctl_lock);
  		wait_for_completion(&wait);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
275
276
277
  	} else {
  		/* anything non-NULL; we'll never dereference it */
  		p->unregistering = ERR_PTR(-EINVAL);
ace0c791e   Eric W. Biederman   proc/sysctl: Don'...
278
  		spin_unlock(&sysctl_lock);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
279
280
  	}
  	/*
f90f3cafe   Eric W. Biederman   proc: Use d_inval...
281
  	 * Invalidate dentries for unregistered sysctls: namespaced sysctls
d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
282
283
  	 * can have duplicate names and contaminate dcache very badly.
  	 */
f90f3cafe   Eric W. Biederman   proc: Use d_inval...
284
  	proc_sys_invalidate_dcache(p);
d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
285
  	/*
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
286
287
288
  	 * do not remove from the list until nobody holds it; walking the
  	 * list in do_sysctl() relies on that.
  	 */
ace0c791e   Eric W. Biederman   proc/sysctl: Don'...
289
  	spin_lock(&sysctl_lock);
8425d6aaf   Eric W. Biederman   sysctl: Factor ou...
290
  	erase_header(p);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
291
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
292
293
  static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head)
  {
ab4a1f247   Prasad Joshi   proc_sysctl.c: us...
294
  	BUG_ON(!head);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  	spin_lock(&sysctl_lock);
  	if (!use_table(head))
  		head = ERR_PTR(-ENOENT);
  	spin_unlock(&sysctl_lock);
  	return head;
  }
  
  static void sysctl_head_finish(struct ctl_table_header *head)
  {
  	if (!head)
  		return;
  	spin_lock(&sysctl_lock);
  	unuse_table(head);
  	spin_unlock(&sysctl_lock);
  }
  
  static struct ctl_table_set *
13bcc6a28   Eric W. Biederman   sysctl: Stop impl...
312
  lookup_header_set(struct ctl_table_root *root)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
313
314
315
  {
  	struct ctl_table_set *set = &root->default_set;
  	if (root->lookup)
13bcc6a28   Eric W. Biederman   sysctl: Stop impl...
316
  		set = root->lookup(root);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
317
318
  	return set;
  }
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
319
  static struct ctl_table *lookup_entry(struct ctl_table_header **phead,
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
320
  				      struct ctl_dir *dir,
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
321
322
323
324
  				      const char *name, int namelen)
  {
  	struct ctl_table_header *head;
  	struct ctl_table *entry;
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
325
326
  
  	spin_lock(&sysctl_lock);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
327
328
329
330
331
  	entry = find_entry(&head, dir, name, namelen);
  	if (entry && use_table(head))
  		*phead = head;
  	else
  		entry = NULL;
076c3eed2   Eric W. Biederman   sysctl: Rewrite p...
332
333
334
  	spin_unlock(&sysctl_lock);
  	return entry;
  }
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
335
  static struct ctl_node *first_usable_entry(struct rb_node *node)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
336
  {
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
337
  	struct ctl_node *ctl_node;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
338

ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
339
340
341
342
  	for (;node; node = rb_next(node)) {
  		ctl_node = rb_entry(node, struct ctl_node, node);
  		if (use_table(ctl_node->header))
  			return ctl_node;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
343
  	}
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
344
345
  	return NULL;
  }
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
346
  static void first_entry(struct ctl_dir *dir,
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
347
348
  	struct ctl_table_header **phead, struct ctl_table **pentry)
  {
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
349
  	struct ctl_table_header *head = NULL;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
350
  	struct ctl_table *entry = NULL;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
351
  	struct ctl_node *ctl_node;
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
352
353
  
  	spin_lock(&sysctl_lock);
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
354
  	ctl_node = first_usable_entry(rb_first(&dir->root));
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
355
  	spin_unlock(&sysctl_lock);
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
356
357
358
359
  	if (ctl_node) {
  		head = ctl_node->header;
  		entry = &head->ctl_table[ctl_node - head->node];
  	}
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
360
361
362
  	*phead = head;
  	*pentry = entry;
  }
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
363
  static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentry)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
364
  {
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
365
366
  	struct ctl_table_header *head = *phead;
  	struct ctl_table *entry = *pentry;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
367
  	struct ctl_node *ctl_node = &head->node[entry - head->ctl_table];
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
368

ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
369
370
371
372
373
374
375
376
377
  	spin_lock(&sysctl_lock);
  	unuse_table(head);
  
  	ctl_node = first_usable_entry(rb_next(&ctl_node->node));
  	spin_unlock(&sysctl_lock);
  	head = NULL;
  	if (ctl_node) {
  		head = ctl_node->header;
  		entry = &head->ctl_table[ctl_node - head->node];
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
378
379
380
  	}
  	*phead = head;
  	*pentry = entry;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
381
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
382
383
384
385
386
387
388
  /*
   * sysctl_perm does NOT grant the superuser all rights automatically, because
   * some sysctl variables are readonly even to root.
   */
  
  static int test_perm(int mode, int op)
  {
091bd3ea4   Eric W. Biederman   userns: Convert s...
389
  	if (uid_eq(current_euid(), GLOBAL_ROOT_UID))
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
390
  		mode >>= 6;
091bd3ea4   Eric W. Biederman   userns: Convert s...
391
  	else if (in_egroup_p(GLOBAL_ROOT_GID))
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
392
393
394
395
396
  		mode >>= 3;
  	if ((op & ~mode & (MAY_READ|MAY_WRITE|MAY_EXEC)) == 0)
  		return 0;
  	return -EACCES;
  }
73f7ef435   Eric W. Biederman   sysctl: Pass usef...
397
  static int sysctl_perm(struct ctl_table_header *head, struct ctl_table *table, int op)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
398
  {
73f7ef435   Eric W. Biederman   sysctl: Pass usef...
399
  	struct ctl_table_root *root = head->root;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
400
401
402
  	int mode;
  
  	if (root->permissions)
73f7ef435   Eric W. Biederman   sysctl: Pass usef...
403
  		mode = root->permissions(head, table);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
404
405
406
407
408
  	else
  		mode = table->mode;
  
  	return test_perm(mode, op);
  }
9043476f7   Al Viro   [PATCH] sanitize ...
409
410
  static struct inode *proc_sys_make_inode(struct super_block *sb,
  		struct ctl_table_header *head, struct ctl_table *table)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
411
  {
e79c6a4fc   Dmitry Torokhov   net: make net nam...
412
  	struct ctl_table_root *root = head->root;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
413
  	struct inode *inode;
9043476f7   Al Viro   [PATCH] sanitize ...
414
  	struct proc_inode *ei;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
415

9043476f7   Al Viro   [PATCH] sanitize ...
416
  	inode = new_inode(sb);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
417
  	if (!inode)
ea5751ccd   Ivan Delalande   proc/sysctl: don'...
418
  		return ERR_PTR(-ENOMEM);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
419

85fe4025c   Christoph Hellwig   fs: do not assign...
420
  	inode->i_ino = get_next_ino();
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
421
  	ei = PROC_I(inode);
9043476f7   Al Viro   [PATCH] sanitize ...
422

d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
423
  	spin_lock(&sysctl_lock);
ace0c791e   Eric W. Biederman   proc/sysctl: Don'...
424
425
426
  	if (unlikely(head->unregistering)) {
  		spin_unlock(&sysctl_lock);
  		iput(inode);
ea5751ccd   Ivan Delalande   proc/sysctl: don'...
427
  		return ERR_PTR(-ENOENT);
ace0c791e   Eric W. Biederman   proc/sysctl: Don'...
428
429
430
  	}
  	ei->sysctl = head;
  	ei->sysctl_entry = table;
0afa5ca82   Eric W. Biederman   proc: Rename in p...
431
  	hlist_add_head_rcu(&ei->sibling_inodes, &head->inodes);
d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
432
433
  	head->count++;
  	spin_unlock(&sysctl_lock);
078cd8279   Deepa Dinamani   fs: Replace CURRE...
434
  	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
9043476f7   Al Viro   [PATCH] sanitize ...
435
  	inode->i_mode = table->mode;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
436
  	if (!S_ISDIR(table->mode)) {
9043476f7   Al Viro   [PATCH] sanitize ...
437
438
439
440
441
  		inode->i_mode |= S_IFREG;
  		inode->i_op = &proc_sys_inode_operations;
  		inode->i_fop = &proc_sys_file_operations;
  	} else {
  		inode->i_mode |= S_IFDIR;
9043476f7   Al Viro   [PATCH] sanitize ...
442
443
  		inode->i_op = &proc_sys_dir_operations;
  		inode->i_fop = &proc_sys_dir_file_operations;
f9bd6733d   Eric W. Biederman   sysctl: Allow cre...
444
445
  		if (is_empty_dir(head))
  			make_empty_dir_inode(inode);
9043476f7   Al Viro   [PATCH] sanitize ...
446
  	}
e79c6a4fc   Dmitry Torokhov   net: make net nam...
447
448
449
  
  	if (root->set_ownership)
  		root->set_ownership(head, table, &inode->i_uid, &inode->i_gid);
5ec27ec73   Radoslaw Burny   fs/proc/proc_sysc...
450
451
452
453
  	else {
  		inode->i_uid = GLOBAL_ROOT_UID;
  		inode->i_gid = GLOBAL_ROOT_GID;
  	}
e79c6a4fc   Dmitry Torokhov   net: make net nam...
454

77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
455
456
  	return inode;
  }
d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
457
458
459
  void proc_sys_evict_inode(struct inode *inode, struct ctl_table_header *head)
  {
  	spin_lock(&sysctl_lock);
0afa5ca82   Eric W. Biederman   proc: Rename in p...
460
  	hlist_del_init_rcu(&PROC_I(inode)->sibling_inodes);
d6cffbbe9   Konstantin Khlebnikov   proc/sysctl: prun...
461
462
463
464
  	if (!--head->count)
  		kfree_rcu(head, rcu);
  	spin_unlock(&sysctl_lock);
  }
81324364b   Adrian Bunk   proc: make grab_h...
465
  static struct ctl_table_header *grab_header(struct inode *inode)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
466
  {
3cc3e0463   Eric W. Biederman   sysctl: A more ob...
467
468
  	struct ctl_table_header *head = PROC_I(inode)->sysctl;
  	if (!head)
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
469
  		head = &sysctl_table_root.default_set.dir.header;
3cc3e0463   Eric W. Biederman   sysctl: A more ob...
470
  	return sysctl_head_grab(head);
9043476f7   Al Viro   [PATCH] sanitize ...
471
  }
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
472

9043476f7   Al Viro   [PATCH] sanitize ...
473
  static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
00cd8dd3b   Al Viro   stop passing name...
474
  					unsigned int flags)
9043476f7   Al Viro   [PATCH] sanitize ...
475
476
  {
  	struct ctl_table_header *head = grab_header(dir);
9043476f7   Al Viro   [PATCH] sanitize ...
477
  	struct ctl_table_header *h = NULL;
dc12e9094   Al Viro   qstr: constify in...
478
  	const struct qstr *name = &dentry->d_name;
9043476f7   Al Viro   [PATCH] sanitize ...
479
480
481
  	struct ctl_table *p;
  	struct inode *inode;
  	struct dentry *err = ERR_PTR(-ENOENT);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
482
  	struct ctl_dir *ctl_dir;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
483
  	int ret;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
484

9043476f7   Al Viro   [PATCH] sanitize ...
485
486
  	if (IS_ERR(head))
  		return ERR_CAST(head);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
487

7ec66d063   Eric W. Biederman   sysctl: Stop requ...
488
  	ctl_dir = container_of(head, struct ctl_dir, header);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
489

7ec66d063   Eric W. Biederman   sysctl: Stop requ...
490
  	p = lookup_entry(&h, ctl_dir, name->name, name->len);
9043476f7   Al Viro   [PATCH] sanitize ...
491
  	if (!p)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
492
  		goto out;
4e7573203   Eric W. Biederman   sysctl: Don't cal...
493
  	if (S_ISLNK(p->mode)) {
13bcc6a28   Eric W. Biederman   sysctl: Stop impl...
494
  		ret = sysctl_follow_link(&h, &p);
4e7573203   Eric W. Biederman   sysctl: Don't cal...
495
496
497
498
  		err = ERR_PTR(ret);
  		if (ret)
  			goto out;
  	}
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
499

9043476f7   Al Viro   [PATCH] sanitize ...
500
  	inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
ea5751ccd   Ivan Delalande   proc/sysctl: don'...
501
502
  	if (IS_ERR(inode)) {
  		err = ERR_CAST(inode);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
503
  		goto out;
ea5751ccd   Ivan Delalande   proc/sysctl: don'...
504
  	}
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
505

fb045adb9   Nick Piggin   fs: dcache reduce...
506
  	d_set_d_op(dentry, &proc_sys_dentry_operations);
888e2b03e   Al Viro   switch the rest o...
507
  	err = d_splice_alias(inode, dentry);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
508
509
  
  out:
6bf610457   Francesco Ruggeri   fs/proc: fix pote...
510
511
  	if (h)
  		sysctl_head_finish(h);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
512
513
514
  	sysctl_head_finish(head);
  	return err;
  }
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
515
516
  static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
  		int write)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
517
  {
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
518
  	struct inode *inode = file_inode(iocb->ki_filp);
9043476f7   Al Viro   [PATCH] sanitize ...
519
520
  	struct ctl_table_header *head = grab_header(inode);
  	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
521
522
  	size_t count = iov_iter_count(iter);
  	char *kbuf;
2a2da53b1   David Howells   Fix pointer misma...
523
  	ssize_t error;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
524

9043476f7   Al Viro   [PATCH] sanitize ...
525
526
  	if (IS_ERR(head))
  		return PTR_ERR(head);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
527
528
529
530
531
532
  
  	/*
  	 * At this point we know that the sysctl was not unregistered
  	 * and won't be until we finish.
  	 */
  	error = -EPERM;
73f7ef435   Eric W. Biederman   sysctl: Pass usef...
533
  	if (sysctl_perm(head, table, write ? MAY_WRITE : MAY_READ))
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
534
  		goto out;
9043476f7   Al Viro   [PATCH] sanitize ...
535
536
537
538
  	/* if that can happen at all, it should be -EINVAL, not -EISDIR */
  	error = -EINVAL;
  	if (!table->proc_handler)
  		goto out;
ef9d965bc   Christoph Hellwig   sysctl: reject gi...
539
  	/* don't even try if the size is too large */
d4d80e699   Matthew Wilcox (Oracle)   Call sysctl_head_...
540
541
542
  	error = -ENOMEM;
  	if (count >= KMALLOC_MAX_SIZE)
  		goto out;
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
543
544
545
  	kbuf = kzalloc(count + 1, GFP_KERNEL);
  	if (!kbuf)
  		goto out;
ef9d965bc   Christoph Hellwig   sysctl: reject gi...
546

32927393d   Christoph Hellwig   sysctl: pass kern...
547
  	if (write) {
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
548
549
550
551
  		error = -EFAULT;
  		if (!copy_from_iter_full(kbuf, count, iter))
  			goto out_free_buf;
  		kbuf[count] = '\0';
32927393d   Christoph Hellwig   sysctl: pass kern...
552
553
554
  	}
  
  	error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, &kbuf, &count,
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
555
  					   &iocb->ki_pos);
7b146cebe   Andrey Ignatov   bpf: Sysctl hook
556
  	if (error)
32927393d   Christoph Hellwig   sysctl: pass kern...
557
  		goto out_free_buf;
7b146cebe   Andrey Ignatov   bpf: Sysctl hook
558

77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
559
  	/* careful: calling conventions are nasty here */
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
560
  	error = table->proc_handler(table, write, kbuf, &count, &iocb->ki_pos);
32927393d   Christoph Hellwig   sysctl: pass kern...
561
562
563
564
565
  	if (error)
  		goto out_free_buf;
  
  	if (!write) {
  		error = -EFAULT;
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
566
  		if (copy_to_iter(kbuf, count, iter) < count)
32927393d   Christoph Hellwig   sysctl: pass kern...
567
  			goto out_free_buf;
4e63acdff   Andrey Ignatov   bpf: Introduce bp...
568
  	}
32927393d   Christoph Hellwig   sysctl: pass kern...
569
570
571
  	error = count;
  out_free_buf:
  	kfree(kbuf);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
572
573
574
575
576
  out:
  	sysctl_head_finish(head);
  
  	return error;
  }
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
577
  static ssize_t proc_sys_read(struct kiocb *iocb, struct iov_iter *iter)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
578
  {
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
579
  	return proc_sys_call_handler(iocb, iter, 0);
7708bfb1c   Pavel Emelyanov   sysctl: merge equ...
580
  }
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
581

4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
582
  static ssize_t proc_sys_write(struct kiocb *iocb, struct iov_iter *iter)
7708bfb1c   Pavel Emelyanov   sysctl: merge equ...
583
  {
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
584
  	return proc_sys_call_handler(iocb, iter, 1);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
585
  }
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
586
587
  static int proc_sys_open(struct inode *inode, struct file *filp)
  {
4e474a00d   Lucas De Marchi   sysctl: protect p...
588
  	struct ctl_table_header *head = grab_header(inode);
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
589
  	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
4e474a00d   Lucas De Marchi   sysctl: protect p...
590
591
592
  	/* sysctl was unregistered */
  	if (IS_ERR(head))
  		return PTR_ERR(head);
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
593
594
  	if (table->poll)
  		filp->private_data = proc_sys_poll_event(table->poll);
4e474a00d   Lucas De Marchi   sysctl: protect p...
595
  	sysctl_head_finish(head);
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
596
597
  	return 0;
  }
076ccb76e   Al Viro   fs: annotate ->po...
598
  static __poll_t proc_sys_poll(struct file *filp, poll_table *wait)
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
599
  {
496ad9aa8   Al Viro   new helper: file_...
600
  	struct inode *inode = file_inode(filp);
4e474a00d   Lucas De Marchi   sysctl: protect p...
601
  	struct ctl_table_header *head = grab_header(inode);
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
602
  	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
076ccb76e   Al Viro   fs: annotate ->po...
603
  	__poll_t ret = DEFAULT_POLLMASK;
4e474a00d   Lucas De Marchi   sysctl: protect p...
604
605
606
607
  	unsigned long event;
  
  	/* sysctl was unregistered */
  	if (IS_ERR(head))
a9a08845e   Linus Torvalds   vfs: do bulk POLL...
608
  		return EPOLLERR | EPOLLHUP;
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
609
610
611
612
613
614
  
  	if (!table->proc_handler)
  		goto out;
  
  	if (!table->poll)
  		goto out;
4e474a00d   Lucas De Marchi   sysctl: protect p...
615
  	event = (unsigned long)filp->private_data;
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
616
617
618
619
  	poll_wait(filp, &table->poll->wait, wait);
  
  	if (event != atomic_read(&table->poll->event)) {
  		filp->private_data = proc_sys_poll_event(table->poll);
a9a08845e   Linus Torvalds   vfs: do bulk POLL...
620
  		ret = EPOLLIN | EPOLLRDNORM | EPOLLERR | EPOLLPRI;
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
621
622
623
  	}
  
  out:
4e474a00d   Lucas De Marchi   sysctl: protect p...
624
  	sysctl_head_finish(head);
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
625
626
  	return ret;
  }
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
627

f0c3b5093   Al Viro   [readdir] convert...
628
629
  static bool proc_sys_fill_cache(struct file *file,
  				struct dir_context *ctx,
9043476f7   Al Viro   [PATCH] sanitize ...
630
631
  				struct ctl_table_header *head,
  				struct ctl_table *table)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
632
  {
f0c3b5093   Al Viro   [readdir] convert...
633
  	struct dentry *child, *dir = file->f_path.dentry;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
634
635
636
637
  	struct inode *inode;
  	struct qstr qname;
  	ino_t ino = 0;
  	unsigned type = DT_UNKNOWN;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
638
639
640
  
  	qname.name = table->procname;
  	qname.len  = strlen(table->procname);
8387ff257   Linus Torvalds   vfs: make the str...
641
  	qname.hash = full_name_hash(dir, qname.name, qname.len);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
642

77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
643
644
  	child = d_lookup(dir, &qname);
  	if (!child) {
76aab3ab6   Al Viro   proc_sys_fill_cac...
645
646
647
648
649
  		DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
  		child = d_alloc_parallel(dir, &qname, &wq);
  		if (IS_ERR(child))
  			return false;
  		if (d_in_lookup(child)) {
888e2b03e   Al Viro   switch the rest o...
650
  			struct dentry *res;
9043476f7   Al Viro   [PATCH] sanitize ...
651
  			inode = proc_sys_make_inode(dir->d_sb, head, table);
ea5751ccd   Ivan Delalande   proc/sysctl: don'...
652
  			if (IS_ERR(inode)) {
76aab3ab6   Al Viro   proc_sys_fill_cac...
653
  				d_lookup_done(child);
9043476f7   Al Viro   [PATCH] sanitize ...
654
  				dput(child);
f0c3b5093   Al Viro   [readdir] convert...
655
  				return false;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
656
  			}
76aab3ab6   Al Viro   proc_sys_fill_cac...
657
  			d_set_d_op(child, &proc_sys_dentry_operations);
888e2b03e   Al Viro   switch the rest o...
658
659
660
661
662
663
664
665
666
667
  			res = d_splice_alias(inode, child);
  			d_lookup_done(child);
  			if (unlikely(res)) {
  				if (IS_ERR(res)) {
  					dput(child);
  					return false;
  				}
  				dput(child);
  				child = res;
  			}
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
668
669
  		}
  	}
2b0143b5c   David Howells   VFS: normal files...
670
  	inode = d_inode(child);
9043476f7   Al Viro   [PATCH] sanitize ...
671
672
  	ino  = inode->i_ino;
  	type = inode->i_mode >> 12;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
673
  	dput(child);
f0c3b5093   Al Viro   [readdir] convert...
674
  	return dir_emit(ctx, qname.name, qname.len, ino, type);
9043476f7   Al Viro   [PATCH] sanitize ...
675
  }
f0c3b5093   Al Viro   [readdir] convert...
676
677
  static bool proc_sys_link_fill_cache(struct file *file,
  				    struct dir_context *ctx,
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
678
679
680
  				    struct ctl_table_header *head,
  				    struct ctl_table *table)
  {
f0c3b5093   Al Viro   [readdir] convert...
681
  	bool ret = true;
a0b0d1c34   Danilo Krummrich   fs/proc/proc_sysc...
682

0e47c99d7   Eric W. Biederman   sysctl: Replace r...
683
  	head = sysctl_head_grab(head);
a0b0d1c34   Danilo Krummrich   fs/proc/proc_sysc...
684
685
  	if (IS_ERR(head))
  		return false;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
686

835b94e05   Danilo Krummrich   fs/proc/proc_sysc...
687
688
689
  	/* It is not an error if we can not follow the link ignore it */
  	if (sysctl_follow_link(&head, &table))
  		goto out;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
690

f0c3b5093   Al Viro   [readdir] convert...
691
  	ret = proc_sys_fill_cache(file, ctx, head, table);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
692
693
694
695
  out:
  	sysctl_head_finish(head);
  	return ret;
  }
e5eea0981   Joe Perches   sysctl: remove ty...
696
  static int scan(struct ctl_table_header *head, struct ctl_table *table,
9043476f7   Al Viro   [PATCH] sanitize ...
697
  		unsigned long *pos, struct file *file,
f0c3b5093   Al Viro   [readdir] convert...
698
  		struct dir_context *ctx)
9043476f7   Al Viro   [PATCH] sanitize ...
699
  {
f0c3b5093   Al Viro   [readdir] convert...
700
  	bool res;
9043476f7   Al Viro   [PATCH] sanitize ...
701

f0c3b5093   Al Viro   [readdir] convert...
702
703
  	if ((*pos)++ < ctx->pos)
  		return true;
9043476f7   Al Viro   [PATCH] sanitize ...
704

0e47c99d7   Eric W. Biederman   sysctl: Replace r...
705
  	if (unlikely(S_ISLNK(table->mode)))
f0c3b5093   Al Viro   [readdir] convert...
706
  		res = proc_sys_link_fill_cache(file, ctx, head, table);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
707
  	else
f0c3b5093   Al Viro   [readdir] convert...
708
  		res = proc_sys_fill_cache(file, ctx, head, table);
9043476f7   Al Viro   [PATCH] sanitize ...
709

f0c3b5093   Al Viro   [readdir] convert...
710
711
  	if (res)
  		ctx->pos = *pos;
9043476f7   Al Viro   [PATCH] sanitize ...
712

6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
713
  	return res;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
714
  }
f0c3b5093   Al Viro   [readdir] convert...
715
  static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
716
  {
f0c3b5093   Al Viro   [readdir] convert...
717
  	struct ctl_table_header *head = grab_header(file_inode(file));
9043476f7   Al Viro   [PATCH] sanitize ...
718
  	struct ctl_table_header *h = NULL;
6a75ce167   Eric W. Biederman   sysctl: Rewrite p...
719
  	struct ctl_table *entry;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
720
  	struct ctl_dir *ctl_dir;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
721
  	unsigned long pos;
9043476f7   Al Viro   [PATCH] sanitize ...
722
723
724
  
  	if (IS_ERR(head))
  		return PTR_ERR(head);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
725

7ec66d063   Eric W. Biederman   sysctl: Stop requ...
726
  	ctl_dir = container_of(head, struct ctl_dir, header);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
727

f0c3b5093   Al Viro   [readdir] convert...
728
  	if (!dir_emit_dots(file, ctx))
93362fa47   Zhou Chengming   sysctl: Drop refe...
729
  		goto out;
f0c3b5093   Al Viro   [readdir] convert...
730

77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
731
  	pos = 2;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
732
  	for (first_entry(ctl_dir, &h, &entry); h; next_entry(&h, &entry)) {
f0c3b5093   Al Viro   [readdir] convert...
733
  		if (!scan(h, entry, &pos, file, ctx)) {
9043476f7   Al Viro   [PATCH] sanitize ...
734
735
  			sysctl_head_finish(h);
  			break;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
736
737
  		}
  	}
93362fa47   Zhou Chengming   sysctl: Drop refe...
738
  out:
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
739
  	sysctl_head_finish(head);
f0c3b5093   Al Viro   [readdir] convert...
740
  	return 0;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
741
  }
10556cb21   Al Viro   ->permission() sa...
742
  static int proc_sys_permission(struct inode *inode, int mask)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
743
744
745
746
747
  {
  	/*
  	 * sysctl entries that are not writeable,
  	 * are _NOT_ writeable, capabilities or not.
  	 */
f696a3659   Miklos Szeredi   [PATCH] move exec...
748
749
  	struct ctl_table_header *head;
  	struct ctl_table *table;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
750
  	int error;
f696a3659   Miklos Szeredi   [PATCH] move exec...
751
752
753
754
755
  	/* Executable files are not allowed under /proc/sys/ */
  	if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))
  		return -EACCES;
  
  	head = grab_header(inode);
9043476f7   Al Viro   [PATCH] sanitize ...
756
757
  	if (IS_ERR(head))
  		return PTR_ERR(head);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
758

f696a3659   Miklos Szeredi   [PATCH] move exec...
759
  	table = PROC_I(inode)->sysctl_entry;
9043476f7   Al Viro   [PATCH] sanitize ...
760
761
762
  	if (!table) /* global root - r-xr-xr-x */
  		error = mask & MAY_WRITE ? -EACCES : 0;
  	else /* Use the permissions on the sysctl table entry */
73f7ef435   Eric W. Biederman   sysctl: Pass usef...
763
  		error = sysctl_perm(head, table, mask & ~MAY_NOT_BLOCK);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
764

77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
765
766
767
768
769
770
  	sysctl_head_finish(head);
  	return error;
  }
  
  static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
  {
2b0143b5c   David Howells   VFS: normal files...
771
  	struct inode *inode = d_inode(dentry);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
772
773
774
775
  	int error;
  
  	if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
  		return -EPERM;
31051c85b   Jan Kara   fs: Give dentry t...
776
  	error = setattr_prepare(dentry, attr);
1025774ce   Christoph Hellwig   remove inode_setattr
777
778
  	if (error)
  		return error;
1025774ce   Christoph Hellwig   remove inode_setattr
779
780
781
  	setattr_copy(inode, attr);
  	mark_inode_dirty(inode);
  	return 0;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
782
  }
a528d35e8   David Howells   statx: Add a syst...
783
784
  static int proc_sys_getattr(const struct path *path, struct kstat *stat,
  			    u32 request_mask, unsigned int query_flags)
9043476f7   Al Viro   [PATCH] sanitize ...
785
  {
a528d35e8   David Howells   statx: Add a syst...
786
  	struct inode *inode = d_inode(path->dentry);
9043476f7   Al Viro   [PATCH] sanitize ...
787
788
789
790
791
792
793
794
795
796
797
798
799
  	struct ctl_table_header *head = grab_header(inode);
  	struct ctl_table *table = PROC_I(inode)->sysctl_entry;
  
  	if (IS_ERR(head))
  		return PTR_ERR(head);
  
  	generic_fillattr(inode, stat);
  	if (table)
  		stat->mode = (stat->mode & S_IFMT) | table->mode;
  
  	sysctl_head_finish(head);
  	return 0;
  }
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
800
  static const struct file_operations proc_sys_file_operations = {
f1ecf0685   Lucas De Marchi   sysctl: add suppo...
801
802
  	.open		= proc_sys_open,
  	.poll		= proc_sys_poll,
4bd6a7353   Matthew Wilcox (Oracle)   sysctl: Convert t...
803
804
805
806
  	.read_iter	= proc_sys_read,
  	.write_iter	= proc_sys_write,
  	.splice_read	= generic_file_splice_read,
  	.splice_write	= iter_file_splice_write,
6038f373a   Arnd Bergmann   llseek: automatic...
807
  	.llseek		= default_llseek,
9043476f7   Al Viro   [PATCH] sanitize ...
808
809
810
  };
  
  static const struct file_operations proc_sys_dir_file_operations = {
887df0789   Pavel Emelyanov   procfs: report EI...
811
  	.read		= generic_read_dir,
f50752eaa   Al Viro   switch all procfs...
812
  	.iterate_shared	= proc_sys_readdir,
3222a3e55   Christoph Hellwig   [PATCH] fix ->lls...
813
  	.llseek		= generic_file_llseek,
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
814
  };
03a44825b   Jan Engelhardt   procfs: constify ...
815
  static const struct inode_operations proc_sys_inode_operations = {
9043476f7   Al Viro   [PATCH] sanitize ...
816
817
818
819
820
821
  	.permission	= proc_sys_permission,
  	.setattr	= proc_sys_setattr,
  	.getattr	= proc_sys_getattr,
  };
  
  static const struct inode_operations proc_sys_dir_operations = {
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
822
823
824
  	.lookup		= proc_sys_lookup,
  	.permission	= proc_sys_permission,
  	.setattr	= proc_sys_setattr,
9043476f7   Al Viro   [PATCH] sanitize ...
825
  	.getattr	= proc_sys_getattr,
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
826
  };
0b728e191   Al Viro   stop passing name...
827
  static int proc_sys_revalidate(struct dentry *dentry, unsigned int flags)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
828
  {
0b728e191   Al Viro   stop passing name...
829
  	if (flags & LOOKUP_RCU)
34286d666   Nick Piggin   fs: rcu-walk awar...
830
  		return -ECHILD;
2b0143b5c   David Howells   VFS: normal files...
831
  	return !PROC_I(d_inode(dentry))->sysctl->unregistering;
9043476f7   Al Viro   [PATCH] sanitize ...
832
  }
fe15ce446   Nick Piggin   fs: change d_dele...
833
  static int proc_sys_delete(const struct dentry *dentry)
9043476f7   Al Viro   [PATCH] sanitize ...
834
  {
2b0143b5c   David Howells   VFS: normal files...
835
  	return !!PROC_I(d_inode(dentry))->sysctl->unregistering;
9043476f7   Al Viro   [PATCH] sanitize ...
836
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
837
838
839
840
841
842
843
844
845
846
847
848
849
850
  static int sysctl_is_seen(struct ctl_table_header *p)
  {
  	struct ctl_table_set *set = p->set;
  	int res;
  	spin_lock(&sysctl_lock);
  	if (p->unregistering)
  		res = 0;
  	else if (!set->is_seen)
  		res = 1;
  	else
  		res = set->is_seen(set);
  	spin_unlock(&sysctl_lock);
  	return res;
  }
6fa67e707   Al Viro   get rid of 'paren...
851
  static int proc_sys_compare(const struct dentry *dentry,
621e155a3   Nick Piggin   fs: change d_comp...
852
  		unsigned int len, const char *str, const struct qstr *name)
9043476f7   Al Viro   [PATCH] sanitize ...
853
  {
dfef6dcd3   Al Viro   unfuck proc_sysct...
854
  	struct ctl_table_header *head;
da53be12b   Linus Torvalds   Don't pass inode ...
855
  	struct inode *inode;
31e6b01f4   Nick Piggin   fs: rcu-walk for ...
856
857
  	/* Although proc doesn't have negative dentries, rcu-walk means
  	 * that inode here can be NULL */
dfef6dcd3   Al Viro   unfuck proc_sysct...
858
  	/* AV: can it, indeed? */
2b0143b5c   David Howells   VFS: normal files...
859
  	inode = d_inode_rcu(dentry);
31e6b01f4   Nick Piggin   fs: rcu-walk for ...
860
  	if (!inode)
dfef6dcd3   Al Viro   unfuck proc_sysct...
861
  		return 1;
621e155a3   Nick Piggin   fs: change d_comp...
862
  	if (name->len != len)
9043476f7   Al Viro   [PATCH] sanitize ...
863
  		return 1;
621e155a3   Nick Piggin   fs: change d_comp...
864
  	if (memcmp(name->name, str, len))
9043476f7   Al Viro   [PATCH] sanitize ...
865
  		return 1;
dfef6dcd3   Al Viro   unfuck proc_sysct...
866
867
  	head = rcu_dereference(PROC_I(inode)->sysctl);
  	return !head || !sysctl_is_seen(head);
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
868
  }
d72f71eb0   Al Viro   constify dentry_o...
869
  static const struct dentry_operations proc_sys_dentry_operations = {
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
870
  	.d_revalidate	= proc_sys_revalidate,
9043476f7   Al Viro   [PATCH] sanitize ...
871
872
  	.d_delete	= proc_sys_delete,
  	.d_compare	= proc_sys_compare,
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
873
  };
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
874
875
  static struct ctl_dir *find_subdir(struct ctl_dir *dir,
  				   const char *name, int namelen)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
876
  {
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
877
878
  	struct ctl_table_header *head;
  	struct ctl_table *entry;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
879

0e47c99d7   Eric W. Biederman   sysctl: Replace r...
880
  	entry = find_entry(&head, dir, name, namelen);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
881
882
  	if (!entry)
  		return ERR_PTR(-ENOENT);
51f72f4a0   Eric W. Biederman   sysctl: An easier...
883
884
885
  	if (!S_ISDIR(entry->mode))
  		return ERR_PTR(-ENOTDIR);
  	return container_of(head, struct ctl_dir, header);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
886
887
888
  }
  
  static struct ctl_dir *new_dir(struct ctl_table_set *set,
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
889
  			       const char *name, int namelen)
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
890
891
892
  {
  	struct ctl_table *table;
  	struct ctl_dir *new;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
893
  	struct ctl_node *node;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
894
  	char *new_name;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
895

ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
896
897
898
  	new = kzalloc(sizeof(*new) + sizeof(struct ctl_node) +
  		      sizeof(struct ctl_table)*2 +  namelen + 1,
  		      GFP_KERNEL);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
899
  	if (!new)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
900
  		return NULL;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
901
902
  	node = (struct ctl_node *)(new + 1);
  	table = (struct ctl_table *)(node + 1);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
903
904
905
906
907
  	new_name = (char *)(table + 2);
  	memcpy(new_name, name, namelen);
  	new_name[namelen] = '\0';
  	table[0].procname = new_name;
  	table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
908
  	init_header(&new->header, set->dir.header.root, set, node, table);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
909
910
  
  	return new;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
911
  }
60f126d93   Eric W. Biederman   sysctl: Comments ...
912
913
914
915
916
917
918
919
920
921
922
923
  /**
   * get_subdir - find or create a subdir with the specified name.
   * @dir:  Directory to create the subdirectory in
   * @name: The name of the subdirectory to find or create
   * @namelen: The length of name
   *
   * Takes a directory with an elevated reference count so we know that
   * if we drop the lock the directory will not go away.  Upon success
   * the reference is moved from @dir to the returned subdirectory.
   * Upon error an error code is returned and the reference on @dir is
   * simply dropped.
   */
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
924
925
  static struct ctl_dir *get_subdir(struct ctl_dir *dir,
  				  const char *name, int namelen)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
926
  {
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
927
  	struct ctl_table_set *set = dir->header.set;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
928
  	struct ctl_dir *subdir, *new = NULL;
0eb97f38d   Eric W. Biederman   sysctl: Correct e...
929
  	int err;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
930

7ec66d063   Eric W. Biederman   sysctl: Stop requ...
931
  	spin_lock(&sysctl_lock);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
932
  	subdir = find_subdir(dir, name, namelen);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
933
934
935
936
937
938
939
940
941
942
943
  	if (!IS_ERR(subdir))
  		goto found;
  	if (PTR_ERR(subdir) != -ENOENT)
  		goto failed;
  
  	spin_unlock(&sysctl_lock);
  	new = new_dir(set, name, namelen);
  	spin_lock(&sysctl_lock);
  	subdir = ERR_PTR(-ENOMEM);
  	if (!new)
  		goto failed;
60f126d93   Eric W. Biederman   sysctl: Comments ...
944
  	/* Was the subdir added while we dropped the lock? */
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
945
  	subdir = find_subdir(dir, name, namelen);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
946
947
948
949
  	if (!IS_ERR(subdir))
  		goto found;
  	if (PTR_ERR(subdir) != -ENOENT)
  		goto failed;
60f126d93   Eric W. Biederman   sysctl: Comments ...
950
  	/* Nope.  Use the our freshly made directory entry. */
0eb97f38d   Eric W. Biederman   sysctl: Correct e...
951
952
953
  	err = insert_header(dir, &new->header);
  	subdir = ERR_PTR(err);
  	if (err)
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
954
  		goto failed;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
955
956
957
958
  	subdir = new;
  found:
  	subdir->header.nreg++;
  failed:
a1c83681d   Viresh Kumar   fs: Drop unlikely...
959
  	if (IS_ERR(subdir)) {
87ebdc00e   Andrew Morton   fs/proc: clean up...
960
  		pr_err("sysctl could not get directory: ");
6980128fe   Eric W. Biederman   sysctl: Add sysct...
961
  		sysctl_print_dir(dir);
87ebdc00e   Andrew Morton   fs/proc: clean up...
962
963
  		pr_cont("/%*.*s %ld
  ",
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
964
  			namelen, namelen, name, PTR_ERR(subdir));
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
965
  	}
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
966
967
968
969
970
  	drop_sysctl_table(&dir->header);
  	if (new)
  		drop_sysctl_table(&new->header);
  	spin_unlock(&sysctl_lock);
  	return subdir;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
971
  }
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
972
973
974
975
976
977
978
979
980
981
982
983
984
985
  static struct ctl_dir *xlate_dir(struct ctl_table_set *set, struct ctl_dir *dir)
  {
  	struct ctl_dir *parent;
  	const char *procname;
  	if (!dir->header.parent)
  		return &set->dir;
  	parent = xlate_dir(set, dir->header.parent);
  	if (IS_ERR(parent))
  		return parent;
  	procname = dir->header.ctl_table[0].procname;
  	return find_subdir(parent, procname, strlen(procname));
  }
  
  static int sysctl_follow_link(struct ctl_table_header **phead,
13bcc6a28   Eric W. Biederman   sysctl: Stop impl...
986
  	struct ctl_table **pentry)
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
987
988
989
990
991
992
993
  {
  	struct ctl_table_header *head;
  	struct ctl_table_root *root;
  	struct ctl_table_set *set;
  	struct ctl_table *entry;
  	struct ctl_dir *dir;
  	int ret;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
994
995
996
  	ret = 0;
  	spin_lock(&sysctl_lock);
  	root = (*pentry)->data;
13bcc6a28   Eric W. Biederman   sysctl: Stop impl...
997
  	set = lookup_header_set(root);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
  	dir = xlate_dir(set, (*phead)->parent);
  	if (IS_ERR(dir))
  		ret = PTR_ERR(dir);
  	else {
  		const char *procname = (*pentry)->procname;
  		head = NULL;
  		entry = find_entry(&head, dir, procname, strlen(procname));
  		ret = -ENOENT;
  		if (entry && use_table(head)) {
  			unuse_table(*phead);
  			*phead = head;
  			*pentry = entry;
  			ret = 0;
  		}
  	}
  
  	spin_unlock(&sysctl_lock);
  	return ret;
  }
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1017
  static int sysctl_err(const char *path, struct ctl_table *table, char *fmt, ...)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1018
  {
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1019
1020
  	struct va_format vaf;
  	va_list args;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1021

7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1022
1023
1024
  	va_start(args, fmt);
  	vaf.fmt = fmt;
  	vaf.va = &args;
87ebdc00e   Andrew Morton   fs/proc: clean up...
1025
1026
1027
  	pr_err("sysctl table check failed: %s/%s %pV
  ",
  	       path, table->procname, &vaf);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1028

7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1029
1030
  	va_end(args);
  	return -EINVAL;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1031
  }
4f2fec00a   Luis R. Rodriguez   sysctl: simplify ...
1032
1033
1034
  static int sysctl_check_table_array(const char *path, struct ctl_table *table)
  {
  	int err = 0;
61d9b56a8   Luis R. Rodriguez   sysctl: add unsig...
1035
1036
  	if ((table->proc_handler == proc_douintvec) ||
  	    (table->proc_handler == proc_douintvec_minmax)) {
4f2fec00a   Luis R. Rodriguez   sysctl: simplify ...
1037
  		if (table->maxlen != sizeof(unsigned int))
64a11f3dc   Waiman Long   fs/proc/proc_sysc...
1038
  			err |= sysctl_err(path, table, "array not allowed");
4f2fec00a   Luis R. Rodriguez   sysctl: simplify ...
1039
1040
1041
1042
  	}
  
  	return err;
  }
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1043
  static int sysctl_check_table(const char *path, struct ctl_table *table)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1044
  {
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1045
  	int err = 0;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1046
  	for (; table->procname; table++) {
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1047
  		if (table->child)
89c5b53b1   Luis R. Rodriguez   sysctl: fix lax s...
1048
  			err |= sysctl_err(path, table, "Not a file");
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1049
1050
1051
  
  		if ((table->proc_handler == proc_dostring) ||
  		    (table->proc_handler == proc_dointvec) ||
1680a3868   Liping Zhang   sysctl: add sanit...
1052
  		    (table->proc_handler == proc_douintvec) ||
61d9b56a8   Luis R. Rodriguez   sysctl: add unsig...
1053
  		    (table->proc_handler == proc_douintvec_minmax) ||
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1054
1055
1056
1057
1058
1059
1060
  		    (table->proc_handler == proc_dointvec_minmax) ||
  		    (table->proc_handler == proc_dointvec_jiffies) ||
  		    (table->proc_handler == proc_dointvec_userhz_jiffies) ||
  		    (table->proc_handler == proc_dointvec_ms_jiffies) ||
  		    (table->proc_handler == proc_doulongvec_minmax) ||
  		    (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) {
  			if (!table->data)
89c5b53b1   Luis R. Rodriguez   sysctl: fix lax s...
1061
  				err |= sysctl_err(path, table, "No data");
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1062
  			if (!table->maxlen)
89c5b53b1   Luis R. Rodriguez   sysctl: fix lax s...
1063
  				err |= sysctl_err(path, table, "No maxlen");
4f2fec00a   Luis R. Rodriguez   sysctl: simplify ...
1064
1065
  			else
  				err |= sysctl_check_table_array(path, table);
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1066
1067
  		}
  		if (!table->proc_handler)
89c5b53b1   Luis R. Rodriguez   sysctl: fix lax s...
1068
  			err |= sysctl_err(path, table, "No proc_handler");
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1069
1070
  
  		if ((table->mode & (S_IRUGO|S_IWUGO)) != table->mode)
89c5b53b1   Luis R. Rodriguez   sysctl: fix lax s...
1071
  			err |= sysctl_err(path, table, "bogus .mode 0%o",
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1072
  				table->mode);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1073
  	}
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1074
  	return err;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1075
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1076

0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1077
1078
1079
1080
1081
  static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table *table,
  	struct ctl_table_root *link_root)
  {
  	struct ctl_table *link_table, *entry, *link;
  	struct ctl_table_header *links;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1082
  	struct ctl_node *node;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
  	char *link_name;
  	int nr_entries, name_bytes;
  
  	name_bytes = 0;
  	nr_entries = 0;
  	for (entry = table; entry->procname; entry++) {
  		nr_entries++;
  		name_bytes += strlen(entry->procname) + 1;
  	}
  
  	links = kzalloc(sizeof(struct ctl_table_header) +
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1094
  			sizeof(struct ctl_node)*nr_entries +
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1095
1096
1097
1098
1099
1100
  			sizeof(struct ctl_table)*(nr_entries + 1) +
  			name_bytes,
  			GFP_KERNEL);
  
  	if (!links)
  		return NULL;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1101
1102
  	node = (struct ctl_node *)(links + 1);
  	link_table = (struct ctl_table *)(node + nr_entries);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
  	link_name = (char *)&link_table[nr_entries + 1];
  
  	for (link = link_table, entry = table; entry->procname; link++, entry++) {
  		int len = strlen(entry->procname) + 1;
  		memcpy(link_name, entry->procname, len);
  		link->procname = link_name;
  		link->mode = S_IFLNK|S_IRWXUGO;
  		link->data = link_root;
  		link_name += len;
  	}
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1113
  	init_header(links, dir->header.root, dir->header.set, node, link_table);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
  	links->nreg = nr_entries;
  
  	return links;
  }
  
  static bool get_links(struct ctl_dir *dir,
  	struct ctl_table *table, struct ctl_table_root *link_root)
  {
  	struct ctl_table_header *head;
  	struct ctl_table *entry, *link;
  
  	/* Are there links available for every entry in table? */
  	for (entry = table; entry->procname; entry++) {
  		const char *procname = entry->procname;
  		link = find_entry(&head, dir, procname, strlen(procname));
  		if (!link)
  			return false;
  		if (S_ISDIR(link->mode) && S_ISDIR(entry->mode))
  			continue;
  		if (S_ISLNK(link->mode) && (link->data == link_root))
  			continue;
  		return false;
  	}
  
  	/* The checks passed.  Increase the registration count on the links */
  	for (entry = table; entry->procname; entry++) {
  		const char *procname = entry->procname;
  		link = find_entry(&head, dir, procname, strlen(procname));
  		head->nreg++;
  	}
  	return true;
  }
  
  static int insert_links(struct ctl_table_header *head)
  {
  	struct ctl_table_set *root_set = &sysctl_table_root.default_set;
  	struct ctl_dir *core_parent = NULL;
  	struct ctl_table_header *links;
  	int err;
  
  	if (head->set == root_set)
  		return 0;
  
  	core_parent = xlate_dir(root_set, head->parent);
  	if (IS_ERR(core_parent))
  		return 0;
  
  	if (get_links(core_parent, head->ctl_table, head->root))
  		return 0;
  
  	core_parent->header.nreg++;
  	spin_unlock(&sysctl_lock);
  
  	links = new_links(core_parent, head->ctl_table, head->root);
  
  	spin_lock(&sysctl_lock);
  	err = -ENOMEM;
  	if (!links)
  		goto out;
  
  	err = 0;
  	if (get_links(core_parent, head->ctl_table, head->root)) {
  		kfree(links);
  		goto out;
  	}
  
  	err = insert_header(core_parent, links);
  	if (err)
  		kfree(links);
  out:
  	drop_sysctl_table(&core_parent->header);
  	return err;
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1187
  /**
f728019bb   Eric W. Biederman   sysctl: register ...
1188
   * __register_sysctl_table - register a leaf sysctl table
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1189
   * @set: Sysctl tree to register on
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
   * @path: The path to the directory the sysctl table is in.
   * @table: the top-level table structure
   *
   * Register a sysctl table hierarchy. @table should be a filled in ctl_table
   * array. A completely 0 filled entry terminates the table.
   *
   * The members of the &struct ctl_table structure are used as follows:
   *
   * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not
   *            enter a sysctl file
   *
   * data - a pointer to data for use by proc_handler
   *
   * maxlen - the maximum size in bytes of the data
   *
f728019bb   Eric W. Biederman   sysctl: register ...
1205
   * mode - the file permissions for the /proc/sys file
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1206
   *
f728019bb   Eric W. Biederman   sysctl: register ...
1207
   * child - must be %NULL.
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1208
1209
1210
   *
   * proc_handler - the text handler routine (described below)
   *
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1211
1212
1213
1214
1215
   * extra1, extra2 - extra pointers usable by the proc handler routines
   *
   * Leaf nodes in the sysctl tree will be represented by a single file
   * under /proc; non-leaf nodes will be represented by directories.
   *
f728019bb   Eric W. Biederman   sysctl: register ...
1216
1217
   * There must be a proc_handler routine for any terminal nodes.
   * Several default handlers are available to cover common cases -
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
   *
   * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(),
   * proc_dointvec_userhz_jiffies(), proc_dointvec_minmax(),
   * proc_doulongvec_ms_jiffies_minmax(), proc_doulongvec_minmax()
   *
   * It is the handler's job to read the input buffer from user memory
   * and process it. The handler should return 0 on success.
   *
   * This routine returns %NULL on a failure to register, and a pointer
   * to the table header on success.
   */
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1229
  struct ctl_table_header *__register_sysctl_table(
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1230
  	struct ctl_table_set *set,
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1231
  	const char *path, struct ctl_table *table)
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1232
  {
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1233
  	struct ctl_table_root *root = set->dir.header.root;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1234
  	struct ctl_table_header *header;
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1235
  	const char *name, *nextname;
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1236
  	struct ctl_dir *dir;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1237
1238
1239
1240
1241
1242
  	struct ctl_table *entry;
  	struct ctl_node *node;
  	int nr_entries = 0;
  
  	for (entry = table; entry->procname; entry++)
  		nr_entries++;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1243

ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1244
1245
  	header = kzalloc(sizeof(struct ctl_table_header) +
  			 sizeof(struct ctl_node)*nr_entries, GFP_KERNEL);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1246
1247
  	if (!header)
  		return NULL;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1248
1249
  	node = (struct ctl_node *)(header + 1);
  	init_header(header, root, set, node, table);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1250
1251
1252
1253
  	if (sysctl_check_table(path, table))
  		goto fail;
  
  	spin_lock(&sysctl_lock);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1254
  	dir = &set->dir;
60f126d93   Eric W. Biederman   sysctl: Comments ...
1255
  	/* Reference moved down the diretory tree get_subdir */
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1256
1257
  	dir->header.nreg++;
  	spin_unlock(&sysctl_lock);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1258

7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1259
  	/* Find the directory for the ctl_table */
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
  	for (name = path; name; name = nextname) {
  		int namelen;
  		nextname = strchr(name, '/');
  		if (nextname) {
  			namelen = nextname - name;
  			nextname++;
  		} else {
  			namelen = strlen(name);
  		}
  		if (namelen == 0)
  			continue;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1271

0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1272
  		dir = get_subdir(dir, name, namelen);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1273
1274
  		if (IS_ERR(dir))
  			goto fail;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1275
  	}
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1276

1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1277
  	spin_lock(&sysctl_lock);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1278
  	if (insert_header(dir, header))
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1279
  		goto fail_put_dir_locked;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1280

7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1281
  	drop_sysctl_table(&dir->header);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1282
1283
1284
  	spin_unlock(&sysctl_lock);
  
  	return header;
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1285

7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1286
1287
  fail_put_dir_locked:
  	drop_sysctl_table(&dir->header);
7c60c48f5   Eric W. Biederman   sysctl: Improve t...
1288
1289
1290
1291
1292
  	spin_unlock(&sysctl_lock);
  fail:
  	kfree(header);
  	dump_stack();
  	return NULL;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1293
  }
fea478d41   Eric W. Biederman   sysctl: Add regis...
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
  /**
   * register_sysctl - register a sysctl table
   * @path: The path to the directory the sysctl table is in.
   * @table: the table structure
   *
   * Register a sysctl table. @table should be a filled in ctl_table
   * array. A completely 0 filled entry terminates the table.
   *
   * See __register_sysctl_table for more details.
   */
  struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table)
  {
  	return __register_sysctl_table(&sysctl_table_root.default_set,
  					path, table);
  }
  EXPORT_SYMBOL(register_sysctl);
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
  static char *append_path(const char *path, char *pos, const char *name)
  {
  	int namelen;
  	namelen = strlen(name);
  	if (((pos - path) + namelen + 2) >= PATH_MAX)
  		return NULL;
  	memcpy(pos, name, namelen);
  	pos[namelen] = '/';
  	pos[namelen + 1] = '\0';
  	pos += namelen + 1;
  	return pos;
  }
f728019bb   Eric W. Biederman   sysctl: register ...
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
  static int count_subheaders(struct ctl_table *table)
  {
  	int has_files = 0;
  	int nr_subheaders = 0;
  	struct ctl_table *entry;
  
  	/* special case: no directory and empty directory */
  	if (!table || !table->procname)
  		return 1;
  
  	for (entry = table; entry->procname; entry++) {
  		if (entry->child)
  			nr_subheaders += count_subheaders(entry->child);
  		else
  			has_files = 1;
  	}
  	return nr_subheaders + has_files;
  }
  
  static int register_leaf_sysctl_tables(const char *path, char *pos,
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1342
  	struct ctl_table_header ***subheader, struct ctl_table_set *set,
f728019bb   Eric W. Biederman   sysctl: register ...
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
  	struct ctl_table *table)
  {
  	struct ctl_table *ctl_table_arg = NULL;
  	struct ctl_table *entry, *files;
  	int nr_files = 0;
  	int nr_dirs = 0;
  	int err = -ENOMEM;
  
  	for (entry = table; entry->procname; entry++) {
  		if (entry->child)
  			nr_dirs++;
  		else
  			nr_files++;
  	}
  
  	files = table;
  	/* If there are mixed files and directories we need a new table */
  	if (nr_dirs && nr_files) {
  		struct ctl_table *new;
6396bb221   Kees Cook   treewide: kzalloc...
1362
  		files = kcalloc(nr_files + 1, sizeof(struct ctl_table),
f728019bb   Eric W. Biederman   sysctl: register ...
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
  				GFP_KERNEL);
  		if (!files)
  			goto out;
  
  		ctl_table_arg = files;
  		for (new = files, entry = table; entry->procname; entry++) {
  			if (entry->child)
  				continue;
  			*new = *entry;
  			new++;
  		}
  	}
  
  	/* Register everything except a directory full of subdirectories */
  	if (nr_files || !nr_dirs) {
  		struct ctl_table_header *header;
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1379
  		header = __register_sysctl_table(set, path, files);
f728019bb   Eric W. Biederman   sysctl: register ...
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
  		if (!header) {
  			kfree(ctl_table_arg);
  			goto out;
  		}
  
  		/* Remember if we need to free the file table */
  		header->ctl_table_arg = ctl_table_arg;
  		**subheader = header;
  		(*subheader)++;
  	}
  
  	/* Recurse into the subdirectories. */
  	for (entry = table; entry->procname; entry++) {
  		char *child_pos;
  
  		if (!entry->child)
  			continue;
  
  		err = -ENAMETOOLONG;
  		child_pos = append_path(path, pos, entry->procname);
  		if (!child_pos)
  			goto out;
  
  		err = register_leaf_sysctl_tables(path, child_pos, subheader,
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1404
  						  set, entry->child);
f728019bb   Eric W. Biederman   sysctl: register ...
1405
1406
1407
1408
1409
1410
1411
1412
1413
  		pos[0] = '\0';
  		if (err)
  			goto out;
  	}
  	err = 0;
  out:
  	/* On failure our caller will unregister all registered subheaders */
  	return err;
  }
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1414
1415
  /**
   * __register_sysctl_paths - register a sysctl table hierarchy
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1416
   * @set: Sysctl tree to register on
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1417
1418
1419
1420
1421
1422
1423
1424
1425
   * @path: The path to the directory the sysctl table is in.
   * @table: the top-level table structure
   *
   * Register a sysctl table hierarchy. @table should be a filled in ctl_table
   * array. A completely 0 filled entry terminates the table.
   *
   * See __register_sysctl_table for more details.
   */
  struct ctl_table_header *__register_sysctl_paths(
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1426
  	struct ctl_table_set *set,
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1427
1428
  	const struct ctl_path *path, struct ctl_table *table)
  {
ec6a52668   Eric W. Biederman   sysctl: Add ctl_t...
1429
  	struct ctl_table *ctl_table_arg = table;
f728019bb   Eric W. Biederman   sysctl: register ...
1430
1431
  	int nr_subheaders = count_subheaders(table);
  	struct ctl_table_header *header = NULL, **subheaders, **subheader;
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
  	const struct ctl_path *component;
  	char *new_path, *pos;
  
  	pos = new_path = kmalloc(PATH_MAX, GFP_KERNEL);
  	if (!new_path)
  		return NULL;
  
  	pos[0] = '\0';
  	for (component = path; component->procname; component++) {
  		pos = append_path(new_path, pos, component->procname);
  		if (!pos)
  			goto out;
  	}
ec6a52668   Eric W. Biederman   sysctl: Add ctl_t...
1445
1446
1447
1448
1449
1450
  	while (table->procname && table->child && !table[1].procname) {
  		pos = append_path(new_path, pos, table->procname);
  		if (!pos)
  			goto out;
  		table = table->child;
  	}
f728019bb   Eric W. Biederman   sysctl: register ...
1451
  	if (nr_subheaders == 1) {
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1452
  		header = __register_sysctl_table(set, new_path, table);
f728019bb   Eric W. Biederman   sysctl: register ...
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
  		if (header)
  			header->ctl_table_arg = ctl_table_arg;
  	} else {
  		header = kzalloc(sizeof(*header) +
  				 sizeof(*subheaders)*nr_subheaders, GFP_KERNEL);
  		if (!header)
  			goto out;
  
  		subheaders = (struct ctl_table_header **) (header + 1);
  		subheader = subheaders;
ec6a52668   Eric W. Biederman   sysctl: Add ctl_t...
1463
  		header->ctl_table_arg = ctl_table_arg;
f728019bb   Eric W. Biederman   sysctl: register ...
1464
1465
  
  		if (register_leaf_sysctl_tables(new_path, pos, &subheader,
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1466
  						set, table))
f728019bb   Eric W. Biederman   sysctl: register ...
1467
1468
  			goto err_register_leaves;
  	}
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1469
1470
1471
  out:
  	kfree(new_path);
  	return header;
f728019bb   Eric W. Biederman   sysctl: register ...
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
  
  err_register_leaves:
  	while (subheader > subheaders) {
  		struct ctl_table_header *subh = *(--subheader);
  		struct ctl_table *table = subh->ctl_table_arg;
  		unregister_sysctl_table(subh);
  		kfree(table);
  	}
  	kfree(header);
  	header = NULL;
  	goto out;
6e9d51641   Eric W. Biederman   sysctl: Add suppo...
1483
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
  /**
   * register_sysctl_table_path - register a sysctl table hierarchy
   * @path: The path to the directory the sysctl table is in.
   * @table: the top-level table structure
   *
   * Register a sysctl table hierarchy. @table should be a filled in ctl_table
   * array. A completely 0 filled entry terminates the table.
   *
   * See __register_sysctl_paths for more details.
   */
  struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,
  						struct ctl_table *table)
  {
60a47a2e8   Eric W. Biederman   sysctl: Modify __...
1497
  	return __register_sysctl_paths(&sysctl_table_root.default_set,
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
  					path, table);
  }
  EXPORT_SYMBOL(register_sysctl_paths);
  
  /**
   * register_sysctl_table - register a sysctl table hierarchy
   * @table: the top-level table structure
   *
   * Register a sysctl table hierarchy. @table should be a filled in ctl_table
   * array. A completely 0 filled entry terminates the table.
   *
   * See register_sysctl_paths for more details.
   */
  struct ctl_table_header *register_sysctl_table(struct ctl_table *table)
  {
  	static const struct ctl_path null_path[] = { {} };
  
  	return register_sysctl_paths(null_path, table);
  }
  EXPORT_SYMBOL(register_sysctl_table);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
  static void put_links(struct ctl_table_header *header)
  {
  	struct ctl_table_set *root_set = &sysctl_table_root.default_set;
  	struct ctl_table_root *root = header->root;
  	struct ctl_dir *parent = header->parent;
  	struct ctl_dir *core_parent;
  	struct ctl_table *entry;
  
  	if (header->set == root_set)
  		return;
  
  	core_parent = xlate_dir(root_set, parent);
  	if (IS_ERR(core_parent))
  		return;
  
  	for (entry = header->ctl_table; entry->procname; entry++) {
  		struct ctl_table_header *link_head;
  		struct ctl_table *link;
  		const char *name = entry->procname;
  
  		link = find_entry(&link_head, core_parent, name, strlen(name));
  		if (link &&
  		    ((S_ISDIR(link->mode) && S_ISDIR(entry->mode)) ||
  		     (S_ISLNK(link->mode) && (link->data == root)))) {
  			drop_sysctl_table(link_head);
  		}
  		else {
87ebdc00e   Andrew Morton   fs/proc: clean up...
1545
  			pr_err("sysctl link missing during unregister: ");
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1546
  			sysctl_print_dir(parent);
87ebdc00e   Andrew Morton   fs/proc: clean up...
1547
1548
  			pr_cont("/%s
  ", name);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1549
1550
1551
  		}
  	}
  }
938aaa4f9   Eric W. Biederman   sysctl: Initial s...
1552
1553
  static void drop_sysctl_table(struct ctl_table_header *header)
  {
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1554
  	struct ctl_dir *parent = header->parent;
938aaa4f9   Eric W. Biederman   sysctl: Initial s...
1555
1556
  	if (--header->nreg)
  		return;
89189557b   YueHaibing   fs/proc/proc_sysc...
1557
  	if (parent) {
23da95880   YueHaibing   fs/proc/proc_sysc...
1558
  		put_links(header);
89189557b   YueHaibing   fs/proc/proc_sysc...
1559
1560
  		start_unregistering(header);
  	}
938aaa4f9   Eric W. Biederman   sysctl: Initial s...
1561
1562
  	if (!--header->count)
  		kfree_rcu(header, rcu);
7ec66d063   Eric W. Biederman   sysctl: Stop requ...
1563
1564
1565
  
  	if (parent)
  		drop_sysctl_table(&parent->header);
938aaa4f9   Eric W. Biederman   sysctl: Initial s...
1566
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1567
1568
1569
1570
1571
1572
1573
1574
1575
  /**
   * unregister_sysctl_table - unregister a sysctl table hierarchy
   * @header: the header returned from register_sysctl_table
   *
   * Unregisters the sysctl table and all children. proc entries may not
   * actually be removed until they are no longer used by anyone.
   */
  void unregister_sysctl_table(struct ctl_table_header * header)
  {
f728019bb   Eric W. Biederman   sysctl: register ...
1576
  	int nr_subheaders;
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1577
1578
1579
1580
  	might_sleep();
  
  	if (header == NULL)
  		return;
f728019bb   Eric W. Biederman   sysctl: register ...
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
  	nr_subheaders = count_subheaders(header->ctl_table_arg);
  	if (unlikely(nr_subheaders > 1)) {
  		struct ctl_table_header **subheaders;
  		int i;
  
  		subheaders = (struct ctl_table_header **)(header + 1);
  		for (i = nr_subheaders -1; i >= 0; i--) {
  			struct ctl_table_header *subh = subheaders[i];
  			struct ctl_table *table = subh->ctl_table_arg;
  			unregister_sysctl_table(subh);
  			kfree(table);
  		}
  		kfree(header);
  		return;
  	}
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1596
  	spin_lock(&sysctl_lock);
938aaa4f9   Eric W. Biederman   sysctl: Initial s...
1597
  	drop_sysctl_table(header);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1598
1599
1600
  	spin_unlock(&sysctl_lock);
  }
  EXPORT_SYMBOL(unregister_sysctl_table);
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1601
  void setup_sysctl_set(struct ctl_table_set *set,
9eb47c26f   Eric W. Biederman   sysctl: Add a roo...
1602
  	struct ctl_table_root *root,
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1603
1604
  	int (*is_seen)(struct ctl_table_set *))
  {
1347440db   Dan Carpenter   sysctl: fix memse...
1605
  	memset(set, 0, sizeof(*set));
0e47c99d7   Eric W. Biederman   sysctl: Replace r...
1606
  	set->is_seen = is_seen;
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1607
  	init_header(&set->dir.header, root, set, NULL, root_table);
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1608
  }
97324cd80   Eric W. Biederman   sysctl: Implement...
1609
1610
  void retire_sysctl_set(struct ctl_table_set *set)
  {
ac13ac6f4   Eric W. Biederman   sysctl: Index sys...
1611
  	WARN_ON(!RB_EMPTY_ROOT(&set->dir.root));
97324cd80   Eric W. Biederman   sysctl: Implement...
1612
  }
1f87f0b52   Eric W. Biederman   sysctl: Move the ...
1613

1e0edd3f6   Alexey Dobriyan   proc: spread __init
1614
  int __init proc_sys_init(void)
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
1615
  {
e1675231c   Alexey Dobriyan   proc: proc_sys_ro...
1616
  	struct proc_dir_entry *proc_sys_root;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
1617
  	proc_sys_root = proc_mkdir("sys", NULL);
9043476f7   Al Viro   [PATCH] sanitize ...
1618
  	proc_sys_root->proc_iops = &proc_sys_dir_operations;
d56c0d45f   Alexey Dobriyan   proc: decouple pr...
1619
  	proc_sys_root->proc_dir_ops = &proc_sys_dir_file_operations;
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
1620
  	proc_sys_root->nlink = 0;
de4e83bd6   Eric W. Biederman   sysctl: Register ...
1621
1622
  
  	return sysctl_init();
77b14db50   Eric W. Biederman   [PATCH] sysctl: r...
1623
  }
3db978d48   Vlastimil Babka   kernel/sysctl: su...
1624

0a477e1ae   Vlastimil Babka   kernel/sysctl: su...
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
  struct sysctl_alias {
  	const char *kernel_param;
  	const char *sysctl_param;
  };
  
  /*
   * Historically some settings had both sysctl and a command line parameter.
   * With the generic sysctl. parameter support, we can handle them at a single
   * place and only keep the historical name for compatibility. This is not meant
   * to add brand new aliases. When adding existing aliases, consider whether
   * the possibly different moment of changing the value (e.g. from early_param
   * to the moment do_sysctl_args() is called) is an issue for the specific
   * parameter.
   */
  static const struct sysctl_alias sysctl_aliases[] = {
f117955a2   Guilherme G. Piccoli   kernel/watchdog.c...
1640
1641
1642
1643
1644
  	{"hardlockup_all_cpu_backtrace",	"kernel.hardlockup_all_cpu_backtrace" },
  	{"hung_task_panic",			"kernel.hung_task_panic" },
  	{"numa_zonelist_order",			"vm.numa_zonelist_order" },
  	{"softlockup_all_cpu_backtrace",	"kernel.softlockup_all_cpu_backtrace" },
  	{"softlockup_panic",			"kernel.softlockup_panic" },
0a477e1ae   Vlastimil Babka   kernel/sysctl: su...
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
  	{ }
  };
  
  static const char *sysctl_find_alias(char *param)
  {
  	const struct sysctl_alias *alias;
  
  	for (alias = &sysctl_aliases[0]; alias->kernel_param != NULL; alias++) {
  		if (strcmp(alias->kernel_param, param) == 0)
  			return alias->sysctl_param;
  	}
  
  	return NULL;
  }
3db978d48   Vlastimil Babka   kernel/sysctl: su...
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
  /* Set sysctl value passed on kernel command line. */
  static int process_sysctl_arg(char *param, char *val,
  			       const char *unused, void *arg)
  {
  	char *path;
  	struct vfsmount **proc_mnt = arg;
  	struct file_system_type *proc_fs_type;
  	struct file *file;
  	int len;
  	int err;
  	loff_t pos = 0;
  	ssize_t wret;
0a477e1ae   Vlastimil Babka   kernel/sysctl: su...
1671
1672
  	if (strncmp(param, "sysctl", sizeof("sysctl") - 1) == 0) {
  		param += sizeof("sysctl") - 1;
3db978d48   Vlastimil Babka   kernel/sysctl: su...
1673

0a477e1ae   Vlastimil Babka   kernel/sysctl: su...
1674
1675
  		if (param[0] != '/' && param[0] != '.')
  			return 0;
3db978d48   Vlastimil Babka   kernel/sysctl: su...
1676

0a477e1ae   Vlastimil Babka   kernel/sysctl: su...
1677
1678
1679
1680
1681
1682
  		param++;
  	} else {
  		param = (char *) sysctl_find_alias(param);
  		if (!param)
  			return 0;
  	}
3db978d48   Vlastimil Babka   kernel/sysctl: su...
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
  
  	/*
  	 * To set sysctl options, we use a temporary mount of proc, look up the
  	 * respective sys/ file and write to it. To avoid mounting it when no
  	 * options were given, we mount it only when the first sysctl option is
  	 * found. Why not a persistent mount? There are problems with a
  	 * persistent mount of proc in that it forces userspace not to use any
  	 * proc mount options.
  	 */
  	if (!*proc_mnt) {
  		proc_fs_type = get_fs_type("proc");
  		if (!proc_fs_type) {
  			pr_err("Failed to find procfs to set sysctl from command line
  ");
  			return 0;
  		}
  		*proc_mnt = kern_mount(proc_fs_type);
  		put_filesystem(proc_fs_type);
  		if (IS_ERR(*proc_mnt)) {
  			pr_err("Failed to mount procfs to set sysctl from command line
  ");
  			return 0;
  		}
  	}
  
  	path = kasprintf(GFP_KERNEL, "sys/%s", param);
  	if (!path)
  		panic("%s: Failed to allocate path for %s
  ", __func__, param);
  	strreplace(path, '.', '/');
  
  	file = file_open_root((*proc_mnt)->mnt_root, *proc_mnt, path, O_WRONLY, 0);
  	if (IS_ERR(file)) {
  		err = PTR_ERR(file);
  		if (err == -ENOENT)
  			pr_err("Failed to set sysctl parameter '%s=%s': parameter not found
  ",
  				param, val);
  		else if (err == -EACCES)
  			pr_err("Failed to set sysctl parameter '%s=%s': permission denied (read-only?)
  ",
  				param, val);
  		else
  			pr_err("Error %pe opening proc file to set sysctl parameter '%s=%s'
  ",
  				file, param, val);
  		goto out;
  	}
  	len = strlen(val);
  	wret = kernel_write(file, val, len, &pos);
  	if (wret < 0) {
  		err = wret;
  		if (err == -EINVAL)
  			pr_err("Failed to set sysctl parameter '%s=%s': invalid value
  ",
  				param, val);
  		else
  			pr_err("Error %pe writing to proc file to set sysctl parameter '%s=%s'
  ",
  				ERR_PTR(err), param, val);
  	} else if (wret != len) {
  		pr_err("Wrote only %zd bytes of %d writing to proc file %s to set sysctl parameter '%s=%s
  ",
  			wret, len, path, param, val);
  	}
  
  	err = filp_close(file, NULL);
  	if (err)
  		pr_err("Error %pe closing proc file to set sysctl parameter '%s=%s
  ",
  			ERR_PTR(err), param, val);
  out:
  	kfree(path);
  	return 0;
  }
  
  void do_sysctl_args(void)
  {
  	char *command_line;
  	struct vfsmount *proc_mnt = NULL;
  
  	command_line = kstrdup(saved_command_line, GFP_KERNEL);
  	if (!command_line)
  		panic("%s: Failed to allocate copy of command line
  ", __func__);
  
  	parse_args("Setting sysctl args", command_line,
  		   NULL, 0, -1, -1, &proc_mnt, process_sysctl_arg);
  
  	if (proc_mnt)
  		kern_unmount(proc_mnt);
  
  	kfree(command_line);
  }