Blame view
fs/proc/proc_sysctl.c
45.4 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
77b14db50 [PATCH] sysctl: r... |
2 3 4 |
/* * /proc/sys support */ |
1e0edd3f6 proc: spread __init |
5 |
#include <linux/init.h> |
77b14db50 [PATCH] sysctl: r... |
6 |
#include <linux/sysctl.h> |
f1ecf0685 sysctl: add suppo... |
7 |
#include <linux/poll.h> |
77b14db50 [PATCH] sysctl: r... |
8 |
#include <linux/proc_fs.h> |
87ebdc00e fs/proc: clean up... |
9 |
#include <linux/printk.h> |
77b14db50 [PATCH] sysctl: r... |
10 |
#include <linux/security.h> |
404015308 security: trim se... |
11 |
#include <linux/sched.h> |
5b825c3af sched/headers: Pr... |
12 |
#include <linux/cred.h> |
34286d666 fs: rcu-walk awar... |
13 |
#include <linux/namei.h> |
404015308 security: trim se... |
14 |
#include <linux/mm.h> |
4bd6a7353 sysctl: Convert t... |
15 |
#include <linux/uio.h> |
1f87f0b52 sysctl: Move the ... |
16 |
#include <linux/module.h> |
7b146cebe bpf: Sysctl hook |
17 |
#include <linux/bpf-cgroup.h> |
3db978d48 kernel/sysctl: su... |
18 |
#include <linux/mount.h> |
77b14db50 [PATCH] sysctl: r... |
19 |
#include "internal.h" |
d72f71eb0 constify dentry_o... |
20 |
static const struct dentry_operations proc_sys_dentry_operations; |
77b14db50 [PATCH] sysctl: r... |
21 |
static const struct file_operations proc_sys_file_operations; |
03a44825b procfs: constify ... |
22 |
static const struct inode_operations proc_sys_inode_operations; |
9043476f7 [PATCH] sanitize ... |
23 24 |
static const struct file_operations proc_sys_dir_file_operations; static const struct inode_operations proc_sys_dir_operations; |
77b14db50 [PATCH] sysctl: r... |
25 |
|
eec4844fa 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 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 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 sysctl: Normalize... |
58 59 60 |
static struct ctl_table root_table[] = { { .procname = "", |
7ec66d063 sysctl: Stop requ... |
61 |
.mode = S_IFDIR|S_IRUGO|S_IXUGO, |
a194558e8 sysctl: Normalize... |
62 63 64 |
}, { } }; |
0e47c99d7 sysctl: Replace r... |
65 |
static struct ctl_table_root sysctl_table_root = { |
0e47c99d7 sysctl: Replace r... |
66 |
.default_set.dir.header = { |
7ec66d063 sysctl: Stop requ... |
67 68 |
{{.count = 1, .nreg = 1, |
ac13ac6f4 sysctl: Index sys... |
69 |
.ctl_table = root_table }}, |
0e47c99d7 sysctl: Replace r... |
70 |
.ctl_table_arg = root_table, |
7ec66d063 sysctl: Stop requ... |
71 72 73 |
.root = &sysctl_table_root, .set = &sysctl_table_root.default_set, }, |
1f87f0b52 sysctl: Move the ... |
74 |
}; |
1f87f0b52 sysctl: Move the ... |
75 76 |
static DEFINE_SPINLOCK(sysctl_lock); |
7ec66d063 sysctl: Stop requ... |
77 |
static void drop_sysctl_table(struct ctl_table_header *header); |
0e47c99d7 sysctl: Replace r... |
78 |
static int sysctl_follow_link(struct ctl_table_header **phead, |
13bcc6a28 sysctl: Stop impl... |
79 |
struct ctl_table **pentry); |
0e47c99d7 sysctl: Replace r... |
80 81 |
static int insert_links(struct ctl_table_header *head); static void put_links(struct ctl_table_header *header); |
7ec66d063 sysctl: Stop requ... |
82 |
|
6980128fe 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 fs/proc: clean up... |
87 |
pr_cont("%s/", dir->header.ctl_table[0].procname); |
6980128fe sysctl: Add sysct... |
88 |
} |
076c3eed2 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 sysctl: Comments ... |
103 |
/* Called under sysctl_lock */ |
076c3eed2 sysctl: Rewrite p... |
104 |
static struct ctl_table *find_entry(struct ctl_table_header **phead, |
0e47c99d7 sysctl: Replace r... |
105 |
struct ctl_dir *dir, const char *name, int namelen) |
076c3eed2 sysctl: Rewrite p... |
106 107 108 |
{ struct ctl_table_header *head; struct ctl_table *entry; |
ac13ac6f4 sysctl: Index sys... |
109 |
struct rb_node *node = dir->root.rb_node; |
076c3eed2 sysctl: Rewrite p... |
110 |
|
ac13ac6f4 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 sysctl: Rewrite p... |
130 131 132 133 |
} } return NULL; } |
ac13ac6f4 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 fs/proc: clean up... |
161 |
pr_err("sysctl duplicate entry: "); |
ac13ac6f4 sysctl: Index sys... |
162 |
sysctl_print_dir(head->parent); |
87ebdc00e fs/proc: clean up... |
163 164 |
pr_cont("/%s ", entry->procname); |
ac13ac6f4 sysctl: Index sys... |
165 166 167 168 169 |
return -EEXIST; } } rb_link_node(node, parent, p); |
ea5272f5c rbtree: fix incor... |
170 |
rb_insert_color(node, &head->parent->root); |
ac13ac6f4 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 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 sysctl: Index sys... |
182 |
struct ctl_node *node, struct ctl_table *table) |
e0d045290 sysctl: Factor ou... |
183 |
{ |
7ec66d063 sysctl: Stop requ... |
184 |
head->ctl_table = table; |
e0d045290 sysctl: Factor ou... |
185 |
head->ctl_table_arg = table; |
e0d045290 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 sysctl: Index sys... |
193 |
head->node = node; |
2fd1d2c4c proc: Fix proc_sy... |
194 |
INIT_HLIST_HEAD(&head->inodes); |
ac13ac6f4 sysctl: Index sys... |
195 196 |
if (node) { struct ctl_table *entry; |
4c199a93a rbtree: empty nod... |
197 |
for (entry = table; entry->procname; entry++, node++) |
ac13ac6f4 sysctl: Index sys... |
198 |
node->header = head; |
ac13ac6f4 sysctl: Index sys... |
199 |
} |
e0d045290 sysctl: Factor ou... |
200 |
} |
8425d6aaf sysctl: Factor ou... |
201 202 |
static void erase_header(struct ctl_table_header *head) { |
ac13ac6f4 sysctl: Index sys... |
203 204 205 |
struct ctl_table *entry; for (entry = head->ctl_table; entry->procname; entry++) erase_entry(head, entry); |
8425d6aaf sysctl: Factor ou... |
206 |
} |
0e47c99d7 sysctl: Replace r... |
207 |
static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header) |
8425d6aaf sysctl: Factor ou... |
208 |
{ |
ac13ac6f4 sysctl: Index sys... |
209 |
struct ctl_table *entry; |
0e47c99d7 sysctl: Replace r... |
210 |
int err; |
f9bd6733d 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 sysctl: Replace r... |
221 |
dir->header.nreg++; |
7ec66d063 sysctl: Stop requ... |
222 |
header->parent = dir; |
0e47c99d7 sysctl: Replace r... |
223 224 225 |
err = insert_links(header); if (err) goto fail_links; |
ac13ac6f4 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 sysctl: Replace r... |
231 |
return 0; |
ac13ac6f4 sysctl: Index sys... |
232 233 234 |
fail: erase_header(header); put_links(header); |
0e47c99d7 sysctl: Replace r... |
235 |
fail_links: |
f9bd6733d sysctl: Allow cre... |
236 237 |
if (header->ctl_table == sysctl_mount_point) clear_empty_dir(dir); |
0e47c99d7 sysctl: Replace r... |
238 239 240 |
header->parent = NULL; drop_sysctl_table(&dir->header); return err; |
8425d6aaf sysctl: Factor ou... |
241 |
} |
1f87f0b52 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 proc: Use d_inval... |
258 |
static void proc_sys_invalidate_dcache(struct ctl_table_header *head) |
d6cffbbe9 proc/sysctl: prun... |
259 |
{ |
f90f3cafe proc: Use d_inval... |
260 |
proc_invalidate_siblings_dcache(&head->inodes, &sysctl_lock); |
d6cffbbe9 proc/sysctl: prun... |
261 |
} |
1f87f0b52 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 sysctl: Move the ... |
275 276 277 |
} else { /* anything non-NULL; we'll never dereference it */ p->unregistering = ERR_PTR(-EINVAL); |
ace0c791e proc/sysctl: Don'... |
278 |
spin_unlock(&sysctl_lock); |
1f87f0b52 sysctl: Move the ... |
279 280 |
} /* |
f90f3cafe proc: Use d_inval... |
281 |
* Invalidate dentries for unregistered sysctls: namespaced sysctls |
d6cffbbe9 proc/sysctl: prun... |
282 283 |
* can have duplicate names and contaminate dcache very badly. */ |
f90f3cafe proc: Use d_inval... |
284 |
proc_sys_invalidate_dcache(p); |
d6cffbbe9 proc/sysctl: prun... |
285 |
/* |
1f87f0b52 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 proc/sysctl: Don'... |
289 |
spin_lock(&sysctl_lock); |
8425d6aaf sysctl: Factor ou... |
290 |
erase_header(p); |
1f87f0b52 sysctl: Move the ... |
291 |
} |
1f87f0b52 sysctl: Move the ... |
292 293 |
static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) { |
ab4a1f247 proc_sysctl.c: us... |
294 |
BUG_ON(!head); |
1f87f0b52 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 sysctl: Stop impl... |
312 |
lookup_header_set(struct ctl_table_root *root) |
1f87f0b52 sysctl: Move the ... |
313 314 315 |
{ struct ctl_table_set *set = &root->default_set; if (root->lookup) |
13bcc6a28 sysctl: Stop impl... |
316 |
set = root->lookup(root); |
1f87f0b52 sysctl: Move the ... |
317 318 |
return set; } |
076c3eed2 sysctl: Rewrite p... |
319 |
static struct ctl_table *lookup_entry(struct ctl_table_header **phead, |
7ec66d063 sysctl: Stop requ... |
320 |
struct ctl_dir *dir, |
076c3eed2 sysctl: Rewrite p... |
321 322 323 324 |
const char *name, int namelen) { struct ctl_table_header *head; struct ctl_table *entry; |
076c3eed2 sysctl: Rewrite p... |
325 326 |
spin_lock(&sysctl_lock); |
0e47c99d7 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 sysctl: Rewrite p... |
332 333 334 |
spin_unlock(&sysctl_lock); return entry; } |
ac13ac6f4 sysctl: Index sys... |
335 |
static struct ctl_node *first_usable_entry(struct rb_node *node) |
1f87f0b52 sysctl: Move the ... |
336 |
{ |
ac13ac6f4 sysctl: Index sys... |
337 |
struct ctl_node *ctl_node; |
1f87f0b52 sysctl: Move the ... |
338 |
|
ac13ac6f4 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 sysctl: Move the ... |
343 |
} |
1f87f0b52 sysctl: Move the ... |
344 345 |
return NULL; } |
7ec66d063 sysctl: Stop requ... |
346 |
static void first_entry(struct ctl_dir *dir, |
6a75ce167 sysctl: Rewrite p... |
347 348 |
struct ctl_table_header **phead, struct ctl_table **pentry) { |
ac13ac6f4 sysctl: Index sys... |
349 |
struct ctl_table_header *head = NULL; |
7ec66d063 sysctl: Stop requ... |
350 |
struct ctl_table *entry = NULL; |
ac13ac6f4 sysctl: Index sys... |
351 |
struct ctl_node *ctl_node; |
6a75ce167 sysctl: Rewrite p... |
352 353 |
spin_lock(&sysctl_lock); |
ac13ac6f4 sysctl: Index sys... |
354 |
ctl_node = first_usable_entry(rb_first(&dir->root)); |
6a75ce167 sysctl: Rewrite p... |
355 |
spin_unlock(&sysctl_lock); |
ac13ac6f4 sysctl: Index sys... |
356 357 358 359 |
if (ctl_node) { head = ctl_node->header; entry = &head->ctl_table[ctl_node - head->node]; } |
6a75ce167 sysctl: Rewrite p... |
360 361 362 |
*phead = head; *pentry = entry; } |
7ec66d063 sysctl: Stop requ... |
363 |
static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentry) |
1f87f0b52 sysctl: Move the ... |
364 |
{ |
6a75ce167 sysctl: Rewrite p... |
365 366 |
struct ctl_table_header *head = *phead; struct ctl_table *entry = *pentry; |
ac13ac6f4 sysctl: Index sys... |
367 |
struct ctl_node *ctl_node = &head->node[entry - head->ctl_table]; |
6a75ce167 sysctl: Rewrite p... |
368 |
|
ac13ac6f4 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 sysctl: Rewrite p... |
378 379 380 |
} *phead = head; *pentry = entry; |
1f87f0b52 sysctl: Move the ... |
381 |
} |
1f87f0b52 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 userns: Convert s... |
389 |
if (uid_eq(current_euid(), GLOBAL_ROOT_UID)) |
1f87f0b52 sysctl: Move the ... |
390 |
mode >>= 6; |
091bd3ea4 userns: Convert s... |
391 |
else if (in_egroup_p(GLOBAL_ROOT_GID)) |
1f87f0b52 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 sysctl: Pass usef... |
397 |
static int sysctl_perm(struct ctl_table_header *head, struct ctl_table *table, int op) |
1f87f0b52 sysctl: Move the ... |
398 |
{ |
73f7ef435 sysctl: Pass usef... |
399 |
struct ctl_table_root *root = head->root; |
1f87f0b52 sysctl: Move the ... |
400 401 402 |
int mode; if (root->permissions) |
73f7ef435 sysctl: Pass usef... |
403 |
mode = root->permissions(head, table); |
1f87f0b52 sysctl: Move the ... |
404 405 406 407 408 |
else mode = table->mode; return test_perm(mode, op); } |
9043476f7 [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 [PATCH] sysctl: r... |
411 |
{ |
e79c6a4fc net: make net nam... |
412 |
struct ctl_table_root *root = head->root; |
77b14db50 [PATCH] sysctl: r... |
413 |
struct inode *inode; |
9043476f7 [PATCH] sanitize ... |
414 |
struct proc_inode *ei; |
77b14db50 [PATCH] sysctl: r... |
415 |
|
9043476f7 [PATCH] sanitize ... |
416 |
inode = new_inode(sb); |
77b14db50 [PATCH] sysctl: r... |
417 |
if (!inode) |
ea5751ccd proc/sysctl: don'... |
418 |
return ERR_PTR(-ENOMEM); |
77b14db50 [PATCH] sysctl: r... |
419 |
|
85fe4025c fs: do not assign... |
420 |
inode->i_ino = get_next_ino(); |
77b14db50 [PATCH] sysctl: r... |
421 |
ei = PROC_I(inode); |
9043476f7 [PATCH] sanitize ... |
422 |
|
d6cffbbe9 proc/sysctl: prun... |
423 |
spin_lock(&sysctl_lock); |
ace0c791e proc/sysctl: Don'... |
424 425 426 |
if (unlikely(head->unregistering)) { spin_unlock(&sysctl_lock); iput(inode); |
ea5751ccd proc/sysctl: don'... |
427 |
return ERR_PTR(-ENOENT); |
ace0c791e proc/sysctl: Don'... |
428 429 430 |
} ei->sysctl = head; ei->sysctl_entry = table; |
0afa5ca82 proc: Rename in p... |
431 |
hlist_add_head_rcu(&ei->sibling_inodes, &head->inodes); |
d6cffbbe9 proc/sysctl: prun... |
432 433 |
head->count++; spin_unlock(&sysctl_lock); |
078cd8279 fs: Replace CURRE... |
434 |
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); |
9043476f7 [PATCH] sanitize ... |
435 |
inode->i_mode = table->mode; |
7ec66d063 sysctl: Stop requ... |
436 |
if (!S_ISDIR(table->mode)) { |
9043476f7 [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 [PATCH] sanitize ... |
442 443 |
inode->i_op = &proc_sys_dir_operations; inode->i_fop = &proc_sys_dir_file_operations; |
f9bd6733d sysctl: Allow cre... |
444 445 |
if (is_empty_dir(head)) make_empty_dir_inode(inode); |
9043476f7 [PATCH] sanitize ... |
446 |
} |
e79c6a4fc net: make net nam... |
447 448 449 |
if (root->set_ownership) root->set_ownership(head, table, &inode->i_uid, &inode->i_gid); |
5ec27ec73 fs/proc/proc_sysc... |
450 451 452 453 |
else { inode->i_uid = GLOBAL_ROOT_UID; inode->i_gid = GLOBAL_ROOT_GID; } |
e79c6a4fc net: make net nam... |
454 |
|
77b14db50 [PATCH] sysctl: r... |
455 456 |
return inode; } |
d6cffbbe9 proc/sysctl: prun... |
457 458 459 |
void proc_sys_evict_inode(struct inode *inode, struct ctl_table_header *head) { spin_lock(&sysctl_lock); |
0afa5ca82 proc: Rename in p... |
460 |
hlist_del_init_rcu(&PROC_I(inode)->sibling_inodes); |
d6cffbbe9 proc/sysctl: prun... |
461 462 463 464 |
if (!--head->count) kfree_rcu(head, rcu); spin_unlock(&sysctl_lock); } |
81324364b proc: make grab_h... |
465 |
static struct ctl_table_header *grab_header(struct inode *inode) |
77b14db50 [PATCH] sysctl: r... |
466 |
{ |
3cc3e0463 sysctl: A more ob... |
467 468 |
struct ctl_table_header *head = PROC_I(inode)->sysctl; if (!head) |
0e47c99d7 sysctl: Replace r... |
469 |
head = &sysctl_table_root.default_set.dir.header; |
3cc3e0463 sysctl: A more ob... |
470 |
return sysctl_head_grab(head); |
9043476f7 [PATCH] sanitize ... |
471 |
} |
77b14db50 [PATCH] sysctl: r... |
472 |
|
9043476f7 [PATCH] sanitize ... |
473 |
static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry, |
00cd8dd3b stop passing name... |
474 |
unsigned int flags) |
9043476f7 [PATCH] sanitize ... |
475 476 |
{ struct ctl_table_header *head = grab_header(dir); |
9043476f7 [PATCH] sanitize ... |
477 |
struct ctl_table_header *h = NULL; |
dc12e9094 qstr: constify in... |
478 |
const struct qstr *name = &dentry->d_name; |
9043476f7 [PATCH] sanitize ... |
479 480 481 |
struct ctl_table *p; struct inode *inode; struct dentry *err = ERR_PTR(-ENOENT); |
7ec66d063 sysctl: Stop requ... |
482 |
struct ctl_dir *ctl_dir; |
0e47c99d7 sysctl: Replace r... |
483 |
int ret; |
77b14db50 [PATCH] sysctl: r... |
484 |
|
9043476f7 [PATCH] sanitize ... |
485 486 |
if (IS_ERR(head)) return ERR_CAST(head); |
77b14db50 [PATCH] sysctl: r... |
487 |
|
7ec66d063 sysctl: Stop requ... |
488 |
ctl_dir = container_of(head, struct ctl_dir, header); |
77b14db50 [PATCH] sysctl: r... |
489 |
|
7ec66d063 sysctl: Stop requ... |
490 |
p = lookup_entry(&h, ctl_dir, name->name, name->len); |
9043476f7 [PATCH] sanitize ... |
491 |
if (!p) |
77b14db50 [PATCH] sysctl: r... |
492 |
goto out; |
4e7573203 sysctl: Don't cal... |
493 |
if (S_ISLNK(p->mode)) { |
13bcc6a28 sysctl: Stop impl... |
494 |
ret = sysctl_follow_link(&h, &p); |
4e7573203 sysctl: Don't cal... |
495 496 497 498 |
err = ERR_PTR(ret); if (ret) goto out; } |
0e47c99d7 sysctl: Replace r... |
499 |
|
9043476f7 [PATCH] sanitize ... |
500 |
inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p); |
ea5751ccd proc/sysctl: don'... |
501 502 |
if (IS_ERR(inode)) { err = ERR_CAST(inode); |
77b14db50 [PATCH] sysctl: r... |
503 |
goto out; |
ea5751ccd proc/sysctl: don'... |
504 |
} |
77b14db50 [PATCH] sysctl: r... |
505 |
|
fb045adb9 fs: dcache reduce... |
506 |
d_set_d_op(dentry, &proc_sys_dentry_operations); |
888e2b03e switch the rest o... |
507 |
err = d_splice_alias(inode, dentry); |
77b14db50 [PATCH] sysctl: r... |
508 509 |
out: |
6bf610457 fs/proc: fix pote... |
510 511 |
if (h) sysctl_head_finish(h); |
77b14db50 [PATCH] sysctl: r... |
512 513 514 |
sysctl_head_finish(head); return err; } |
4bd6a7353 sysctl: Convert t... |
515 516 |
static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter, int write) |
77b14db50 [PATCH] sysctl: r... |
517 |
{ |
4bd6a7353 sysctl: Convert t... |
518 |
struct inode *inode = file_inode(iocb->ki_filp); |
9043476f7 [PATCH] sanitize ... |
519 520 |
struct ctl_table_header *head = grab_header(inode); struct ctl_table *table = PROC_I(inode)->sysctl_entry; |
4bd6a7353 sysctl: Convert t... |
521 522 |
size_t count = iov_iter_count(iter); char *kbuf; |
2a2da53b1 Fix pointer misma... |
523 |
ssize_t error; |
77b14db50 [PATCH] sysctl: r... |
524 |
|
9043476f7 [PATCH] sanitize ... |
525 526 |
if (IS_ERR(head)) return PTR_ERR(head); |
77b14db50 [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 sysctl: Pass usef... |
533 |
if (sysctl_perm(head, table, write ? MAY_WRITE : MAY_READ)) |
77b14db50 [PATCH] sysctl: r... |
534 |
goto out; |
9043476f7 [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 sysctl: reject gi... |
539 |
/* don't even try if the size is too large */ |
d4d80e699 Call sysctl_head_... |
540 541 542 |
error = -ENOMEM; if (count >= KMALLOC_MAX_SIZE) goto out; |
4bd6a7353 sysctl: Convert t... |
543 544 545 |
kbuf = kzalloc(count + 1, GFP_KERNEL); if (!kbuf) goto out; |
ef9d965bc sysctl: reject gi... |
546 |
|
32927393d sysctl: pass kern... |
547 |
if (write) { |
4bd6a7353 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 sysctl: pass kern... |
552 553 554 |
} error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, &kbuf, &count, |
4bd6a7353 sysctl: Convert t... |
555 |
&iocb->ki_pos); |
7b146cebe bpf: Sysctl hook |
556 |
if (error) |
32927393d sysctl: pass kern... |
557 |
goto out_free_buf; |
7b146cebe bpf: Sysctl hook |
558 |
|
77b14db50 [PATCH] sysctl: r... |
559 |
/* careful: calling conventions are nasty here */ |
4bd6a7353 sysctl: Convert t... |
560 |
error = table->proc_handler(table, write, kbuf, &count, &iocb->ki_pos); |
32927393d sysctl: pass kern... |
561 562 563 564 565 |
if (error) goto out_free_buf; if (!write) { error = -EFAULT; |
4bd6a7353 sysctl: Convert t... |
566 |
if (copy_to_iter(kbuf, count, iter) < count) |
32927393d sysctl: pass kern... |
567 |
goto out_free_buf; |
4e63acdff bpf: Introduce bp... |
568 |
} |
32927393d sysctl: pass kern... |
569 570 571 |
error = count; out_free_buf: kfree(kbuf); |
77b14db50 [PATCH] sysctl: r... |
572 573 574 575 576 |
out: sysctl_head_finish(head); return error; } |
4bd6a7353 sysctl: Convert t... |
577 |
static ssize_t proc_sys_read(struct kiocb *iocb, struct iov_iter *iter) |
77b14db50 [PATCH] sysctl: r... |
578 |
{ |
4bd6a7353 sysctl: Convert t... |
579 |
return proc_sys_call_handler(iocb, iter, 0); |
7708bfb1c sysctl: merge equ... |
580 |
} |
77b14db50 [PATCH] sysctl: r... |
581 |
|
4bd6a7353 sysctl: Convert t... |
582 |
static ssize_t proc_sys_write(struct kiocb *iocb, struct iov_iter *iter) |
7708bfb1c sysctl: merge equ... |
583 |
{ |
4bd6a7353 sysctl: Convert t... |
584 |
return proc_sys_call_handler(iocb, iter, 1); |
77b14db50 [PATCH] sysctl: r... |
585 |
} |
f1ecf0685 sysctl: add suppo... |
586 587 |
static int proc_sys_open(struct inode *inode, struct file *filp) { |
4e474a00d sysctl: protect p... |
588 |
struct ctl_table_header *head = grab_header(inode); |
f1ecf0685 sysctl: add suppo... |
589 |
struct ctl_table *table = PROC_I(inode)->sysctl_entry; |
4e474a00d sysctl: protect p... |
590 591 592 |
/* sysctl was unregistered */ if (IS_ERR(head)) return PTR_ERR(head); |
f1ecf0685 sysctl: add suppo... |
593 594 |
if (table->poll) filp->private_data = proc_sys_poll_event(table->poll); |
4e474a00d sysctl: protect p... |
595 |
sysctl_head_finish(head); |
f1ecf0685 sysctl: add suppo... |
596 597 |
return 0; } |
076ccb76e fs: annotate ->po... |
598 |
static __poll_t proc_sys_poll(struct file *filp, poll_table *wait) |
f1ecf0685 sysctl: add suppo... |
599 |
{ |
496ad9aa8 new helper: file_... |
600 |
struct inode *inode = file_inode(filp); |
4e474a00d sysctl: protect p... |
601 |
struct ctl_table_header *head = grab_header(inode); |
f1ecf0685 sysctl: add suppo... |
602 |
struct ctl_table *table = PROC_I(inode)->sysctl_entry; |
076ccb76e fs: annotate ->po... |
603 |
__poll_t ret = DEFAULT_POLLMASK; |
4e474a00d sysctl: protect p... |
604 605 606 607 |
unsigned long event; /* sysctl was unregistered */ if (IS_ERR(head)) |
a9a08845e vfs: do bulk POLL... |
608 |
return EPOLLERR | EPOLLHUP; |
f1ecf0685 sysctl: add suppo... |
609 610 611 612 613 614 |
if (!table->proc_handler) goto out; if (!table->poll) goto out; |
4e474a00d sysctl: protect p... |
615 |
event = (unsigned long)filp->private_data; |
f1ecf0685 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 vfs: do bulk POLL... |
620 |
ret = EPOLLIN | EPOLLRDNORM | EPOLLERR | EPOLLPRI; |
f1ecf0685 sysctl: add suppo... |
621 622 623 |
} out: |
4e474a00d sysctl: protect p... |
624 |
sysctl_head_finish(head); |
f1ecf0685 sysctl: add suppo... |
625 626 |
return ret; } |
77b14db50 [PATCH] sysctl: r... |
627 |
|
f0c3b5093 [readdir] convert... |
628 629 |
static bool proc_sys_fill_cache(struct file *file, struct dir_context *ctx, |
9043476f7 [PATCH] sanitize ... |
630 631 |
struct ctl_table_header *head, struct ctl_table *table) |
77b14db50 [PATCH] sysctl: r... |
632 |
{ |
f0c3b5093 [readdir] convert... |
633 |
struct dentry *child, *dir = file->f_path.dentry; |
77b14db50 [PATCH] sysctl: r... |
634 635 636 637 |
struct inode *inode; struct qstr qname; ino_t ino = 0; unsigned type = DT_UNKNOWN; |
77b14db50 [PATCH] sysctl: r... |
638 639 640 |
qname.name = table->procname; qname.len = strlen(table->procname); |
8387ff257 vfs: make the str... |
641 |
qname.hash = full_name_hash(dir, qname.name, qname.len); |
77b14db50 [PATCH] sysctl: r... |
642 |
|
77b14db50 [PATCH] sysctl: r... |
643 644 |
child = d_lookup(dir, &qname); if (!child) { |
76aab3ab6 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 switch the rest o... |
650 |
struct dentry *res; |
9043476f7 [PATCH] sanitize ... |
651 |
inode = proc_sys_make_inode(dir->d_sb, head, table); |
ea5751ccd proc/sysctl: don'... |
652 |
if (IS_ERR(inode)) { |
76aab3ab6 proc_sys_fill_cac... |
653 |
d_lookup_done(child); |
9043476f7 [PATCH] sanitize ... |
654 |
dput(child); |
f0c3b5093 [readdir] convert... |
655 |
return false; |
77b14db50 [PATCH] sysctl: r... |
656 |
} |
76aab3ab6 proc_sys_fill_cac... |
657 |
d_set_d_op(child, &proc_sys_dentry_operations); |
888e2b03e 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 [PATCH] sysctl: r... |
668 669 |
} } |
2b0143b5c VFS: normal files... |
670 |
inode = d_inode(child); |
9043476f7 [PATCH] sanitize ... |
671 672 |
ino = inode->i_ino; type = inode->i_mode >> 12; |
77b14db50 [PATCH] sysctl: r... |
673 |
dput(child); |
f0c3b5093 [readdir] convert... |
674 |
return dir_emit(ctx, qname.name, qname.len, ino, type); |
9043476f7 [PATCH] sanitize ... |
675 |
} |
f0c3b5093 [readdir] convert... |
676 677 |
static bool proc_sys_link_fill_cache(struct file *file, struct dir_context *ctx, |
0e47c99d7 sysctl: Replace r... |
678 679 680 |
struct ctl_table_header *head, struct ctl_table *table) { |
f0c3b5093 [readdir] convert... |
681 |
bool ret = true; |
a0b0d1c34 fs/proc/proc_sysc... |
682 |
|
0e47c99d7 sysctl: Replace r... |
683 |
head = sysctl_head_grab(head); |
a0b0d1c34 fs/proc/proc_sysc... |
684 685 |
if (IS_ERR(head)) return false; |
0e47c99d7 sysctl: Replace r... |
686 |
|
835b94e05 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 sysctl: Replace r... |
690 |
|
f0c3b5093 [readdir] convert... |
691 |
ret = proc_sys_fill_cache(file, ctx, head, table); |
0e47c99d7 sysctl: Replace r... |
692 693 694 695 |
out: sysctl_head_finish(head); return ret; } |
e5eea0981 sysctl: remove ty... |
696 |
static int scan(struct ctl_table_header *head, struct ctl_table *table, |
9043476f7 [PATCH] sanitize ... |
697 |
unsigned long *pos, struct file *file, |
f0c3b5093 [readdir] convert... |
698 |
struct dir_context *ctx) |
9043476f7 [PATCH] sanitize ... |
699 |
{ |
f0c3b5093 [readdir] convert... |
700 |
bool res; |
9043476f7 [PATCH] sanitize ... |
701 |
|
f0c3b5093 [readdir] convert... |
702 703 |
if ((*pos)++ < ctx->pos) return true; |
9043476f7 [PATCH] sanitize ... |
704 |
|
0e47c99d7 sysctl: Replace r... |
705 |
if (unlikely(S_ISLNK(table->mode))) |
f0c3b5093 [readdir] convert... |
706 |
res = proc_sys_link_fill_cache(file, ctx, head, table); |
0e47c99d7 sysctl: Replace r... |
707 |
else |
f0c3b5093 [readdir] convert... |
708 |
res = proc_sys_fill_cache(file, ctx, head, table); |
9043476f7 [PATCH] sanitize ... |
709 |
|
f0c3b5093 [readdir] convert... |
710 711 |
if (res) ctx->pos = *pos; |
9043476f7 [PATCH] sanitize ... |
712 |
|
6a75ce167 sysctl: Rewrite p... |
713 |
return res; |
77b14db50 [PATCH] sysctl: r... |
714 |
} |
f0c3b5093 [readdir] convert... |
715 |
static int proc_sys_readdir(struct file *file, struct dir_context *ctx) |
77b14db50 [PATCH] sysctl: r... |
716 |
{ |
f0c3b5093 [readdir] convert... |
717 |
struct ctl_table_header *head = grab_header(file_inode(file)); |
9043476f7 [PATCH] sanitize ... |
718 |
struct ctl_table_header *h = NULL; |
6a75ce167 sysctl: Rewrite p... |
719 |
struct ctl_table *entry; |
7ec66d063 sysctl: Stop requ... |
720 |
struct ctl_dir *ctl_dir; |
77b14db50 [PATCH] sysctl: r... |
721 |
unsigned long pos; |
9043476f7 [PATCH] sanitize ... |
722 723 724 |
if (IS_ERR(head)) return PTR_ERR(head); |
77b14db50 [PATCH] sysctl: r... |
725 |
|
7ec66d063 sysctl: Stop requ... |
726 |
ctl_dir = container_of(head, struct ctl_dir, header); |
77b14db50 [PATCH] sysctl: r... |
727 |
|
f0c3b5093 [readdir] convert... |
728 |
if (!dir_emit_dots(file, ctx)) |
93362fa47 sysctl: Drop refe... |
729 |
goto out; |
f0c3b5093 [readdir] convert... |
730 |
|
77b14db50 [PATCH] sysctl: r... |
731 |
pos = 2; |
7ec66d063 sysctl: Stop requ... |
732 |
for (first_entry(ctl_dir, &h, &entry); h; next_entry(&h, &entry)) { |
f0c3b5093 [readdir] convert... |
733 |
if (!scan(h, entry, &pos, file, ctx)) { |
9043476f7 [PATCH] sanitize ... |
734 735 |
sysctl_head_finish(h); break; |
77b14db50 [PATCH] sysctl: r... |
736 737 |
} } |
93362fa47 sysctl: Drop refe... |
738 |
out: |
77b14db50 [PATCH] sysctl: r... |
739 |
sysctl_head_finish(head); |
f0c3b5093 [readdir] convert... |
740 |
return 0; |
77b14db50 [PATCH] sysctl: r... |
741 |
} |
10556cb21 ->permission() sa... |
742 |
static int proc_sys_permission(struct inode *inode, int mask) |
77b14db50 [PATCH] sysctl: r... |
743 744 745 746 747 |
{ /* * sysctl entries that are not writeable, * are _NOT_ writeable, capabilities or not. */ |
f696a3659 [PATCH] move exec... |
748 749 |
struct ctl_table_header *head; struct ctl_table *table; |
77b14db50 [PATCH] sysctl: r... |
750 |
int error; |
f696a3659 [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 [PATCH] sanitize ... |
756 757 |
if (IS_ERR(head)) return PTR_ERR(head); |
77b14db50 [PATCH] sysctl: r... |
758 |
|
f696a3659 [PATCH] move exec... |
759 |
table = PROC_I(inode)->sysctl_entry; |
9043476f7 [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 sysctl: Pass usef... |
763 |
error = sysctl_perm(head, table, mask & ~MAY_NOT_BLOCK); |
77b14db50 [PATCH] sysctl: r... |
764 |
|
77b14db50 [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 VFS: normal files... |
771 |
struct inode *inode = d_inode(dentry); |
77b14db50 [PATCH] sysctl: r... |
772 773 774 775 |
int error; if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) return -EPERM; |
31051c85b fs: Give dentry t... |
776 |
error = setattr_prepare(dentry, attr); |
1025774ce remove inode_setattr |
777 778 |
if (error) return error; |
1025774ce remove inode_setattr |
779 780 781 |
setattr_copy(inode, attr); mark_inode_dirty(inode); return 0; |
77b14db50 [PATCH] sysctl: r... |
782 |
} |
a528d35e8 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 [PATCH] sanitize ... |
785 |
{ |
a528d35e8 statx: Add a syst... |
786 |
struct inode *inode = d_inode(path->dentry); |
9043476f7 [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 [PATCH] sysctl: r... |
800 |
static const struct file_operations proc_sys_file_operations = { |
f1ecf0685 sysctl: add suppo... |
801 802 |
.open = proc_sys_open, .poll = proc_sys_poll, |
4bd6a7353 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 llseek: automatic... |
807 |
.llseek = default_llseek, |
9043476f7 [PATCH] sanitize ... |
808 809 810 |
}; static const struct file_operations proc_sys_dir_file_operations = { |
887df0789 procfs: report EI... |
811 |
.read = generic_read_dir, |
f50752eaa switch all procfs... |
812 |
.iterate_shared = proc_sys_readdir, |
3222a3e55 [PATCH] fix ->lls... |
813 |
.llseek = generic_file_llseek, |
77b14db50 [PATCH] sysctl: r... |
814 |
}; |
03a44825b procfs: constify ... |
815 |
static const struct inode_operations proc_sys_inode_operations = { |
9043476f7 [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 [PATCH] sysctl: r... |
822 823 824 |
.lookup = proc_sys_lookup, .permission = proc_sys_permission, .setattr = proc_sys_setattr, |
9043476f7 [PATCH] sanitize ... |
825 |
.getattr = proc_sys_getattr, |
77b14db50 [PATCH] sysctl: r... |
826 |
}; |
0b728e191 stop passing name... |
827 |
static int proc_sys_revalidate(struct dentry *dentry, unsigned int flags) |
77b14db50 [PATCH] sysctl: r... |
828 |
{ |
0b728e191 stop passing name... |
829 |
if (flags & LOOKUP_RCU) |
34286d666 fs: rcu-walk awar... |
830 |
return -ECHILD; |
2b0143b5c VFS: normal files... |
831 |
return !PROC_I(d_inode(dentry))->sysctl->unregistering; |
9043476f7 [PATCH] sanitize ... |
832 |
} |
fe15ce446 fs: change d_dele... |
833 |
static int proc_sys_delete(const struct dentry *dentry) |
9043476f7 [PATCH] sanitize ... |
834 |
{ |
2b0143b5c VFS: normal files... |
835 |
return !!PROC_I(d_inode(dentry))->sysctl->unregistering; |
9043476f7 [PATCH] sanitize ... |
836 |
} |
1f87f0b52 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 get rid of 'paren... |
851 |
static int proc_sys_compare(const struct dentry *dentry, |
621e155a3 fs: change d_comp... |
852 |
unsigned int len, const char *str, const struct qstr *name) |
9043476f7 [PATCH] sanitize ... |
853 |
{ |
dfef6dcd3 unfuck proc_sysct... |
854 |
struct ctl_table_header *head; |
da53be12b Don't pass inode ... |
855 |
struct inode *inode; |
31e6b01f4 fs: rcu-walk for ... |
856 857 |
/* Although proc doesn't have negative dentries, rcu-walk means * that inode here can be NULL */ |
dfef6dcd3 unfuck proc_sysct... |
858 |
/* AV: can it, indeed? */ |
2b0143b5c VFS: normal files... |
859 |
inode = d_inode_rcu(dentry); |
31e6b01f4 fs: rcu-walk for ... |
860 |
if (!inode) |
dfef6dcd3 unfuck proc_sysct... |
861 |
return 1; |
621e155a3 fs: change d_comp... |
862 |
if (name->len != len) |
9043476f7 [PATCH] sanitize ... |
863 |
return 1; |
621e155a3 fs: change d_comp... |
864 |
if (memcmp(name->name, str, len)) |
9043476f7 [PATCH] sanitize ... |
865 |
return 1; |
dfef6dcd3 unfuck proc_sysct... |
866 867 |
head = rcu_dereference(PROC_I(inode)->sysctl); return !head || !sysctl_is_seen(head); |
77b14db50 [PATCH] sysctl: r... |
868 |
} |
d72f71eb0 constify dentry_o... |
869 |
static const struct dentry_operations proc_sys_dentry_operations = { |
77b14db50 [PATCH] sysctl: r... |
870 |
.d_revalidate = proc_sys_revalidate, |
9043476f7 [PATCH] sanitize ... |
871 872 |
.d_delete = proc_sys_delete, .d_compare = proc_sys_compare, |
77b14db50 [PATCH] sysctl: r... |
873 |
}; |
0e47c99d7 sysctl: Replace r... |
874 875 |
static struct ctl_dir *find_subdir(struct ctl_dir *dir, const char *name, int namelen) |
1f87f0b52 sysctl: Move the ... |
876 |
{ |
7ec66d063 sysctl: Stop requ... |
877 878 |
struct ctl_table_header *head; struct ctl_table *entry; |
1f87f0b52 sysctl: Move the ... |
879 |
|
0e47c99d7 sysctl: Replace r... |
880 |
entry = find_entry(&head, dir, name, namelen); |
7ec66d063 sysctl: Stop requ... |
881 882 |
if (!entry) return ERR_PTR(-ENOENT); |
51f72f4a0 sysctl: An easier... |
883 884 885 |
if (!S_ISDIR(entry->mode)) return ERR_PTR(-ENOTDIR); return container_of(head, struct ctl_dir, header); |
7ec66d063 sysctl: Stop requ... |
886 887 888 |
} static struct ctl_dir *new_dir(struct ctl_table_set *set, |
0e47c99d7 sysctl: Replace r... |
889 |
const char *name, int namelen) |
7ec66d063 sysctl: Stop requ... |
890 891 892 |
{ struct ctl_table *table; struct ctl_dir *new; |
ac13ac6f4 sysctl: Index sys... |
893 |
struct ctl_node *node; |
7ec66d063 sysctl: Stop requ... |
894 |
char *new_name; |
1f87f0b52 sysctl: Move the ... |
895 |
|
ac13ac6f4 sysctl: Index sys... |
896 897 898 |
new = kzalloc(sizeof(*new) + sizeof(struct ctl_node) + sizeof(struct ctl_table)*2 + namelen + 1, GFP_KERNEL); |
7ec66d063 sysctl: Stop requ... |
899 |
if (!new) |
1f87f0b52 sysctl: Move the ... |
900 |
return NULL; |
ac13ac6f4 sysctl: Index sys... |
901 902 |
node = (struct ctl_node *)(new + 1); table = (struct ctl_table *)(node + 1); |
7ec66d063 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 sysctl: Index sys... |
908 |
init_header(&new->header, set->dir.header.root, set, node, table); |
7ec66d063 sysctl: Stop requ... |
909 910 |
return new; |
1f87f0b52 sysctl: Move the ... |
911 |
} |
60f126d93 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 sysctl: Replace r... |
924 925 |
static struct ctl_dir *get_subdir(struct ctl_dir *dir, const char *name, int namelen) |
1f87f0b52 sysctl: Move the ... |
926 |
{ |
0e47c99d7 sysctl: Replace r... |
927 |
struct ctl_table_set *set = dir->header.set; |
7ec66d063 sysctl: Stop requ... |
928 |
struct ctl_dir *subdir, *new = NULL; |
0eb97f38d sysctl: Correct e... |
929 |
int err; |
1f87f0b52 sysctl: Move the ... |
930 |
|
7ec66d063 sysctl: Stop requ... |
931 |
spin_lock(&sysctl_lock); |
0e47c99d7 sysctl: Replace r... |
932 |
subdir = find_subdir(dir, name, namelen); |
7ec66d063 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 sysctl: Comments ... |
944 |
/* Was the subdir added while we dropped the lock? */ |
0e47c99d7 sysctl: Replace r... |
945 |
subdir = find_subdir(dir, name, namelen); |
7ec66d063 sysctl: Stop requ... |
946 947 948 949 |
if (!IS_ERR(subdir)) goto found; if (PTR_ERR(subdir) != -ENOENT) goto failed; |
60f126d93 sysctl: Comments ... |
950 |
/* Nope. Use the our freshly made directory entry. */ |
0eb97f38d sysctl: Correct e... |
951 952 953 |
err = insert_header(dir, &new->header); subdir = ERR_PTR(err); if (err) |
0e47c99d7 sysctl: Replace r... |
954 |
goto failed; |
7ec66d063 sysctl: Stop requ... |
955 956 957 958 |
subdir = new; found: subdir->header.nreg++; failed: |
a1c83681d fs: Drop unlikely... |
959 |
if (IS_ERR(subdir)) { |
87ebdc00e fs/proc: clean up... |
960 |
pr_err("sysctl could not get directory: "); |
6980128fe sysctl: Add sysct... |
961 |
sysctl_print_dir(dir); |
87ebdc00e fs/proc: clean up... |
962 963 |
pr_cont("/%*.*s %ld ", |
7ec66d063 sysctl: Stop requ... |
964 |
namelen, namelen, name, PTR_ERR(subdir)); |
1f87f0b52 sysctl: Move the ... |
965 |
} |
7ec66d063 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 sysctl: Move the ... |
971 |
} |
0e47c99d7 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 sysctl: Stop impl... |
986 |
struct ctl_table **pentry) |
0e47c99d7 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 sysctl: Replace r... |
994 995 996 |
ret = 0; spin_lock(&sysctl_lock); root = (*pentry)->data; |
13bcc6a28 sysctl: Stop impl... |
997 |
set = lookup_header_set(root); |
0e47c99d7 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 sysctl: Improve t... |
1017 |
static int sysctl_err(const char *path, struct ctl_table *table, char *fmt, ...) |
1f87f0b52 sysctl: Move the ... |
1018 |
{ |
7c60c48f5 sysctl: Improve t... |
1019 1020 |
struct va_format vaf; va_list args; |
1f87f0b52 sysctl: Move the ... |
1021 |
|
7c60c48f5 sysctl: Improve t... |
1022 1023 1024 |
va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; |
87ebdc00e fs/proc: clean up... |
1025 1026 1027 |
pr_err("sysctl table check failed: %s/%s %pV ", path, table->procname, &vaf); |
1f87f0b52 sysctl: Move the ... |
1028 |
|
7c60c48f5 sysctl: Improve t... |
1029 1030 |
va_end(args); return -EINVAL; |
1f87f0b52 sysctl: Move the ... |
1031 |
} |
4f2fec00a sysctl: simplify ... |
1032 1033 1034 |
static int sysctl_check_table_array(const char *path, struct ctl_table *table) { int err = 0; |
61d9b56a8 sysctl: add unsig... |
1035 1036 |
if ((table->proc_handler == proc_douintvec) || (table->proc_handler == proc_douintvec_minmax)) { |
4f2fec00a sysctl: simplify ... |
1037 |
if (table->maxlen != sizeof(unsigned int)) |
64a11f3dc fs/proc/proc_sysc... |
1038 |
err |= sysctl_err(path, table, "array not allowed"); |
4f2fec00a sysctl: simplify ... |
1039 1040 1041 1042 |
} return err; } |
7c60c48f5 sysctl: Improve t... |
1043 |
static int sysctl_check_table(const char *path, struct ctl_table *table) |
1f87f0b52 sysctl: Move the ... |
1044 |
{ |
7c60c48f5 sysctl: Improve t... |
1045 |
int err = 0; |
1f87f0b52 sysctl: Move the ... |
1046 |
for (; table->procname; table++) { |
1f87f0b52 sysctl: Move the ... |
1047 |
if (table->child) |
89c5b53b1 sysctl: fix lax s... |
1048 |
err |= sysctl_err(path, table, "Not a file"); |
7c60c48f5 sysctl: Improve t... |
1049 1050 1051 |
if ((table->proc_handler == proc_dostring) || (table->proc_handler == proc_dointvec) || |
1680a3868 sysctl: add sanit... |
1052 |
(table->proc_handler == proc_douintvec) || |
61d9b56a8 sysctl: add unsig... |
1053 |
(table->proc_handler == proc_douintvec_minmax) || |
7c60c48f5 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 sysctl: fix lax s... |
1061 |
err |= sysctl_err(path, table, "No data"); |
7c60c48f5 sysctl: Improve t... |
1062 |
if (!table->maxlen) |
89c5b53b1 sysctl: fix lax s... |
1063 |
err |= sysctl_err(path, table, "No maxlen"); |
4f2fec00a sysctl: simplify ... |
1064 1065 |
else err |= sysctl_check_table_array(path, table); |
7c60c48f5 sysctl: Improve t... |
1066 1067 |
} if (!table->proc_handler) |
89c5b53b1 sysctl: fix lax s... |
1068 |
err |= sysctl_err(path, table, "No proc_handler"); |
7c60c48f5 sysctl: Improve t... |
1069 1070 |
if ((table->mode & (S_IRUGO|S_IWUGO)) != table->mode) |
89c5b53b1 sysctl: fix lax s... |
1071 |
err |= sysctl_err(path, table, "bogus .mode 0%o", |
7c60c48f5 sysctl: Improve t... |
1072 |
table->mode); |
1f87f0b52 sysctl: Move the ... |
1073 |
} |
7c60c48f5 sysctl: Improve t... |
1074 |
return err; |
1f87f0b52 sysctl: Move the ... |
1075 |
} |
1f87f0b52 sysctl: Move the ... |
1076 |
|
0e47c99d7 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 sysctl: Index sys... |
1082 |
struct ctl_node *node; |
0e47c99d7 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 sysctl: Index sys... |
1094 |
sizeof(struct ctl_node)*nr_entries + |
0e47c99d7 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 sysctl: Index sys... |
1101 1102 |
node = (struct ctl_node *)(links + 1); link_table = (struct ctl_table *)(node + nr_entries); |
0e47c99d7 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 sysctl: Index sys... |
1113 |
init_header(links, dir->header.root, dir->header.set, node, link_table); |
0e47c99d7 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 sysctl: Move the ... |
1187 |
/** |
f728019bb sysctl: register ... |
1188 |
* __register_sysctl_table - register a leaf sysctl table |
60a47a2e8 sysctl: Modify __... |
1189 |
* @set: Sysctl tree to register on |
1f87f0b52 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 sysctl: register ... |
1205 |
* mode - the file permissions for the /proc/sys file |
1f87f0b52 sysctl: Move the ... |
1206 |
* |
f728019bb sysctl: register ... |
1207 |
* child - must be %NULL. |
1f87f0b52 sysctl: Move the ... |
1208 1209 1210 |
* * proc_handler - the text handler routine (described below) * |
1f87f0b52 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 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 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 sysctl: Add suppo... |
1229 |
struct ctl_table_header *__register_sysctl_table( |
60a47a2e8 sysctl: Modify __... |
1230 |
struct ctl_table_set *set, |
6e9d51641 sysctl: Add suppo... |
1231 |
const char *path, struct ctl_table *table) |
1f87f0b52 sysctl: Move the ... |
1232 |
{ |
60a47a2e8 sysctl: Modify __... |
1233 |
struct ctl_table_root *root = set->dir.header.root; |
1f87f0b52 sysctl: Move the ... |
1234 |
struct ctl_table_header *header; |
6e9d51641 sysctl: Add suppo... |
1235 |
const char *name, *nextname; |
7ec66d063 sysctl: Stop requ... |
1236 |
struct ctl_dir *dir; |
ac13ac6f4 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 sysctl: Move the ... |
1243 |
|
ac13ac6f4 sysctl: Index sys... |
1244 1245 |
header = kzalloc(sizeof(struct ctl_table_header) + sizeof(struct ctl_node)*nr_entries, GFP_KERNEL); |
1f87f0b52 sysctl: Move the ... |
1246 1247 |
if (!header) return NULL; |
ac13ac6f4 sysctl: Index sys... |
1248 1249 |
node = (struct ctl_node *)(header + 1); init_header(header, root, set, node, table); |
7ec66d063 sysctl: Stop requ... |
1250 1251 1252 1253 |
if (sysctl_check_table(path, table)) goto fail; spin_lock(&sysctl_lock); |
0e47c99d7 sysctl: Replace r... |
1254 |
dir = &set->dir; |
60f126d93 sysctl: Comments ... |
1255 |
/* Reference moved down the diretory tree get_subdir */ |
7ec66d063 sysctl: Stop requ... |
1256 1257 |
dir->header.nreg++; spin_unlock(&sysctl_lock); |
1f87f0b52 sysctl: Move the ... |
1258 |
|
7ec66d063 sysctl: Stop requ... |
1259 |
/* Find the directory for the ctl_table */ |
6e9d51641 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 sysctl: Move the ... |
1271 |
|
0e47c99d7 sysctl: Replace r... |
1272 |
dir = get_subdir(dir, name, namelen); |
7ec66d063 sysctl: Stop requ... |
1273 1274 |
if (IS_ERR(dir)) goto fail; |
1f87f0b52 sysctl: Move the ... |
1275 |
} |
0e47c99d7 sysctl: Replace r... |
1276 |
|
1f87f0b52 sysctl: Move the ... |
1277 |
spin_lock(&sysctl_lock); |
0e47c99d7 sysctl: Replace r... |
1278 |
if (insert_header(dir, header)) |
7ec66d063 sysctl: Stop requ... |
1279 |
goto fail_put_dir_locked; |
0e47c99d7 sysctl: Replace r... |
1280 |
|
7ec66d063 sysctl: Stop requ... |
1281 |
drop_sysctl_table(&dir->header); |
1f87f0b52 sysctl: Move the ... |
1282 1283 1284 |
spin_unlock(&sysctl_lock); return header; |
0e47c99d7 sysctl: Replace r... |
1285 |
|
7ec66d063 sysctl: Stop requ... |
1286 1287 |
fail_put_dir_locked: drop_sysctl_table(&dir->header); |
7c60c48f5 sysctl: Improve t... |
1288 1289 1290 1291 1292 |
spin_unlock(&sysctl_lock); fail: kfree(header); dump_stack(); return NULL; |
1f87f0b52 sysctl: Move the ... |
1293 |
} |
fea478d41 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 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 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 sysctl: Modify __... |
1342 |
struct ctl_table_header ***subheader, struct ctl_table_set *set, |
f728019bb 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 treewide: kzalloc... |
1362 |
files = kcalloc(nr_files + 1, sizeof(struct ctl_table), |
f728019bb 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 sysctl: Modify __... |
1379 |
header = __register_sysctl_table(set, path, files); |
f728019bb 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 sysctl: Modify __... |
1404 |
set, entry->child); |
f728019bb 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 sysctl: Add suppo... |
1414 1415 |
/** * __register_sysctl_paths - register a sysctl table hierarchy |
60a47a2e8 sysctl: Modify __... |
1416 |
* @set: Sysctl tree to register on |
6e9d51641 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 sysctl: Modify __... |
1426 |
struct ctl_table_set *set, |
6e9d51641 sysctl: Add suppo... |
1427 1428 |
const struct ctl_path *path, struct ctl_table *table) { |
ec6a52668 sysctl: Add ctl_t... |
1429 |
struct ctl_table *ctl_table_arg = table; |
f728019bb sysctl: register ... |
1430 1431 |
int nr_subheaders = count_subheaders(table); struct ctl_table_header *header = NULL, **subheaders, **subheader; |
6e9d51641 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 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 sysctl: register ... |
1451 |
if (nr_subheaders == 1) { |
60a47a2e8 sysctl: Modify __... |
1452 |
header = __register_sysctl_table(set, new_path, table); |
f728019bb 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 sysctl: Add ctl_t... |
1463 |
header->ctl_table_arg = ctl_table_arg; |
f728019bb sysctl: register ... |
1464 1465 |
if (register_leaf_sysctl_tables(new_path, pos, &subheader, |
60a47a2e8 sysctl: Modify __... |
1466 |
set, table)) |
f728019bb sysctl: register ... |
1467 1468 |
goto err_register_leaves; } |
6e9d51641 sysctl: Add suppo... |
1469 1470 1471 |
out: kfree(new_path); return header; |
f728019bb 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 sysctl: Add suppo... |
1483 |
} |
1f87f0b52 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 sysctl: Modify __... |
1497 |
return __register_sysctl_paths(&sysctl_table_root.default_set, |
1f87f0b52 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 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 fs/proc: clean up... |
1545 |
pr_err("sysctl link missing during unregister: "); |
0e47c99d7 sysctl: Replace r... |
1546 |
sysctl_print_dir(parent); |
87ebdc00e fs/proc: clean up... |
1547 1548 |
pr_cont("/%s ", name); |
0e47c99d7 sysctl: Replace r... |
1549 1550 1551 |
} } } |
938aaa4f9 sysctl: Initial s... |
1552 1553 |
static void drop_sysctl_table(struct ctl_table_header *header) { |
7ec66d063 sysctl: Stop requ... |
1554 |
struct ctl_dir *parent = header->parent; |
938aaa4f9 sysctl: Initial s... |
1555 1556 |
if (--header->nreg) return; |
89189557b fs/proc/proc_sysc... |
1557 |
if (parent) { |
23da95880 fs/proc/proc_sysc... |
1558 |
put_links(header); |
89189557b fs/proc/proc_sysc... |
1559 1560 |
start_unregistering(header); } |
938aaa4f9 sysctl: Initial s... |
1561 1562 |
if (!--header->count) kfree_rcu(header, rcu); |
7ec66d063 sysctl: Stop requ... |
1563 1564 1565 |
if (parent) drop_sysctl_table(&parent->header); |
938aaa4f9 sysctl: Initial s... |
1566 |
} |
1f87f0b52 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 sysctl: register ... |
1576 |
int nr_subheaders; |
1f87f0b52 sysctl: Move the ... |
1577 1578 1579 1580 |
might_sleep(); if (header == NULL) return; |
f728019bb 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 sysctl: Move the ... |
1596 |
spin_lock(&sysctl_lock); |
938aaa4f9 sysctl: Initial s... |
1597 |
drop_sysctl_table(header); |
1f87f0b52 sysctl: Move the ... |
1598 1599 1600 |
spin_unlock(&sysctl_lock); } EXPORT_SYMBOL(unregister_sysctl_table); |
0e47c99d7 sysctl: Replace r... |
1601 |
void setup_sysctl_set(struct ctl_table_set *set, |
9eb47c26f sysctl: Add a roo... |
1602 |
struct ctl_table_root *root, |
1f87f0b52 sysctl: Move the ... |
1603 1604 |
int (*is_seen)(struct ctl_table_set *)) { |
1347440db sysctl: fix memse... |
1605 |
memset(set, 0, sizeof(*set)); |
0e47c99d7 sysctl: Replace r... |
1606 |
set->is_seen = is_seen; |
ac13ac6f4 sysctl: Index sys... |
1607 |
init_header(&set->dir.header, root, set, NULL, root_table); |
1f87f0b52 sysctl: Move the ... |
1608 |
} |
97324cd80 sysctl: Implement... |
1609 1610 |
void retire_sysctl_set(struct ctl_table_set *set) { |
ac13ac6f4 sysctl: Index sys... |
1611 |
WARN_ON(!RB_EMPTY_ROOT(&set->dir.root)); |
97324cd80 sysctl: Implement... |
1612 |
} |
1f87f0b52 sysctl: Move the ... |
1613 |
|
1e0edd3f6 proc: spread __init |
1614 |
int __init proc_sys_init(void) |
77b14db50 [PATCH] sysctl: r... |
1615 |
{ |
e1675231c proc: proc_sys_ro... |
1616 |
struct proc_dir_entry *proc_sys_root; |
77b14db50 [PATCH] sysctl: r... |
1617 |
proc_sys_root = proc_mkdir("sys", NULL); |
9043476f7 [PATCH] sanitize ... |
1618 |
proc_sys_root->proc_iops = &proc_sys_dir_operations; |
d56c0d45f proc: decouple pr... |
1619 |
proc_sys_root->proc_dir_ops = &proc_sys_dir_file_operations; |
77b14db50 [PATCH] sysctl: r... |
1620 |
proc_sys_root->nlink = 0; |
de4e83bd6 sysctl: Register ... |
1621 1622 |
return sysctl_init(); |
77b14db50 [PATCH] sysctl: r... |
1623 |
} |
3db978d48 kernel/sysctl: su... |
1624 |
|
0a477e1ae 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 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 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 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 kernel/sysctl: su... |
1671 1672 |
if (strncmp(param, "sysctl", sizeof("sysctl") - 1) == 0) { param += sizeof("sysctl") - 1; |
3db978d48 kernel/sysctl: su... |
1673 |
|
0a477e1ae kernel/sysctl: su... |
1674 1675 |
if (param[0] != '/' && param[0] != '.') return 0; |
3db978d48 kernel/sysctl: su... |
1676 |
|
0a477e1ae kernel/sysctl: su... |
1677 1678 1679 1680 1681 1682 |
param++; } else { param = (char *) sysctl_find_alias(param); if (!param) return 0; } |
3db978d48 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); } |