diff options
Diffstat (limited to 'fs/proc/generic.c')
-rw-r--r-- | fs/proc/generic.c | 163 |
1 files changed, 104 insertions, 59 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 317b72641ebf..7fea13229f33 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -31,9 +31,73 @@ static DEFINE_SPINLOCK(proc_subdir_lock); | |||
31 | 31 | ||
32 | static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) | 32 | static int proc_match(unsigned int len, const char *name, struct proc_dir_entry *de) |
33 | { | 33 | { |
34 | if (de->namelen != len) | 34 | if (len < de->namelen) |
35 | return 0; | 35 | return -1; |
36 | return !memcmp(name, de->name, len); | 36 | if (len > de->namelen) |
37 | return 1; | ||
38 | |||
39 | return memcmp(name, de->name, len); | ||
40 | } | ||
41 | |||
42 | static struct proc_dir_entry *pde_subdir_first(struct proc_dir_entry *dir) | ||
43 | { | ||
44 | return rb_entry_safe(rb_first(&dir->subdir), struct proc_dir_entry, | ||
45 | subdir_node); | ||
46 | } | ||
47 | |||
48 | static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir) | ||
49 | { | ||
50 | return rb_entry_safe(rb_next(&dir->subdir_node), struct proc_dir_entry, | ||
51 | subdir_node); | ||
52 | } | ||
53 | |||
54 | static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir, | ||
55 | const char *name, | ||
56 | unsigned int len) | ||
57 | { | ||
58 | struct rb_node *node = dir->subdir.rb_node; | ||
59 | |||
60 | while (node) { | ||
61 | struct proc_dir_entry *de = container_of(node, | ||
62 | struct proc_dir_entry, | ||
63 | subdir_node); | ||
64 | int result = proc_match(len, name, de); | ||
65 | |||
66 | if (result < 0) | ||
67 | node = node->rb_left; | ||
68 | else if (result > 0) | ||
69 | node = node->rb_right; | ||
70 | else | ||
71 | return de; | ||
72 | } | ||
73 | return NULL; | ||
74 | } | ||
75 | |||
76 | static bool pde_subdir_insert(struct proc_dir_entry *dir, | ||
77 | struct proc_dir_entry *de) | ||
78 | { | ||
79 | struct rb_root *root = &dir->subdir; | ||
80 | struct rb_node **new = &root->rb_node, *parent = NULL; | ||
81 | |||
82 | /* Figure out where to put new node */ | ||
83 | while (*new) { | ||
84 | struct proc_dir_entry *this = | ||
85 | container_of(*new, struct proc_dir_entry, subdir_node); | ||
86 | int result = proc_match(de->namelen, de->name, this); | ||
87 | |||
88 | parent = *new; | ||
89 | if (result < 0) | ||
90 | new = &(*new)->rb_left; | ||
91 | else if (result > 0) | ||
92 | new = &(*new)->rb_right; | ||
93 | else | ||
94 | return false; | ||
95 | } | ||
96 | |||
97 | /* Add new node and rebalance tree. */ | ||
98 | rb_link_node(&de->subdir_node, parent, new); | ||
99 | rb_insert_color(&de->subdir_node, root); | ||
100 | return true; | ||
37 | } | 101 | } |
38 | 102 | ||
39 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) | 103 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) |
@@ -92,10 +156,7 @@ static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret, | |||
92 | break; | 156 | break; |
93 | 157 | ||
94 | len = next - cp; | 158 | len = next - cp; |
95 | for (de = de->subdir; de ; de = de->next) { | 159 | de = pde_subdir_find(de, cp, len); |
96 | if (proc_match(len, cp, de)) | ||
97 | break; | ||
98 | } | ||
99 | if (!de) { | 160 | if (!de) { |
100 | WARN(1, "name '%s'\n", name); | 161 | WARN(1, "name '%s'\n", name); |
101 | return -ENOENT; | 162 | return -ENOENT; |
@@ -183,19 +244,16 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir, | |||
183 | struct inode *inode; | 244 | struct inode *inode; |
184 | 245 | ||
185 | spin_lock(&proc_subdir_lock); | 246 | spin_lock(&proc_subdir_lock); |
186 | for (de = de->subdir; de ; de = de->next) { | 247 | de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len); |
187 | if (de->namelen != dentry->d_name.len) | 248 | if (de) { |
188 | continue; | 249 | pde_get(de); |
189 | if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { | 250 | spin_unlock(&proc_subdir_lock); |
190 | pde_get(de); | 251 | inode = proc_get_inode(dir->i_sb, de); |
191 | spin_unlock(&proc_subdir_lock); | 252 | if (!inode) |
192 | inode = proc_get_inode(dir->i_sb, de); | 253 | return ERR_PTR(-ENOMEM); |
193 | if (!inode) | 254 | d_set_d_op(dentry, &simple_dentry_operations); |
194 | return ERR_PTR(-ENOMEM); | 255 | d_add(dentry, inode); |
195 | d_set_d_op(dentry, &simple_dentry_operations); | 256 | return NULL; |
196 | d_add(dentry, inode); | ||
197 | return NULL; | ||
198 | } | ||
199 | } | 257 | } |
200 | spin_unlock(&proc_subdir_lock); | 258 | spin_unlock(&proc_subdir_lock); |
201 | return ERR_PTR(-ENOENT); | 259 | return ERR_PTR(-ENOENT); |
@@ -225,7 +283,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
225 | return 0; | 283 | return 0; |
226 | 284 | ||
227 | spin_lock(&proc_subdir_lock); | 285 | spin_lock(&proc_subdir_lock); |
228 | de = de->subdir; | 286 | de = pde_subdir_first(de); |
229 | i = ctx->pos - 2; | 287 | i = ctx->pos - 2; |
230 | for (;;) { | 288 | for (;;) { |
231 | if (!de) { | 289 | if (!de) { |
@@ -234,7 +292,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
234 | } | 292 | } |
235 | if (!i) | 293 | if (!i) |
236 | break; | 294 | break; |
237 | de = de->next; | 295 | de = pde_subdir_next(de); |
238 | i--; | 296 | i--; |
239 | } | 297 | } |
240 | 298 | ||
@@ -249,7 +307,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
249 | } | 307 | } |
250 | spin_lock(&proc_subdir_lock); | 308 | spin_lock(&proc_subdir_lock); |
251 | ctx->pos++; | 309 | ctx->pos++; |
252 | next = de->next; | 310 | next = pde_subdir_next(de); |
253 | pde_put(de); | 311 | pde_put(de); |
254 | de = next; | 312 | de = next; |
255 | } while (de); | 313 | } while (de); |
@@ -286,9 +344,8 @@ static const struct inode_operations proc_dir_inode_operations = { | |||
286 | 344 | ||
287 | static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) | 345 | static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) |
288 | { | 346 | { |
289 | struct proc_dir_entry *tmp; | ||
290 | int ret; | 347 | int ret; |
291 | 348 | ||
292 | ret = proc_alloc_inum(&dp->low_ino); | 349 | ret = proc_alloc_inum(&dp->low_ino); |
293 | if (ret) | 350 | if (ret) |
294 | return ret; | 351 | return ret; |
@@ -304,21 +361,21 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp | |||
304 | dp->proc_iops = &proc_file_inode_operations; | 361 | dp->proc_iops = &proc_file_inode_operations; |
305 | } else { | 362 | } else { |
306 | WARN_ON(1); | 363 | WARN_ON(1); |
364 | proc_free_inum(dp->low_ino); | ||
307 | return -EINVAL; | 365 | return -EINVAL; |
308 | } | 366 | } |
309 | 367 | ||
310 | spin_lock(&proc_subdir_lock); | 368 | spin_lock(&proc_subdir_lock); |
311 | |||
312 | for (tmp = dir->subdir; tmp; tmp = tmp->next) | ||
313 | if (strcmp(tmp->name, dp->name) == 0) { | ||
314 | WARN(1, "proc_dir_entry '%s/%s' already registered\n", | ||
315 | dir->name, dp->name); | ||
316 | break; | ||
317 | } | ||
318 | |||
319 | dp->next = dir->subdir; | ||
320 | dp->parent = dir; | 369 | dp->parent = dir; |
321 | dir->subdir = dp; | 370 | if (pde_subdir_insert(dir, dp) == false) { |
371 | WARN(1, "proc_dir_entry '%s/%s' already registered\n", | ||
372 | dir->name, dp->name); | ||
373 | spin_unlock(&proc_subdir_lock); | ||
374 | if (S_ISDIR(dp->mode)) | ||
375 | dir->nlink--; | ||
376 | proc_free_inum(dp->low_ino); | ||
377 | return -EEXIST; | ||
378 | } | ||
322 | spin_unlock(&proc_subdir_lock); | 379 | spin_unlock(&proc_subdir_lock); |
323 | 380 | ||
324 | return 0; | 381 | return 0; |
@@ -354,6 +411,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, | |||
354 | ent->namelen = qstr.len; | 411 | ent->namelen = qstr.len; |
355 | ent->mode = mode; | 412 | ent->mode = mode; |
356 | ent->nlink = nlink; | 413 | ent->nlink = nlink; |
414 | ent->subdir = RB_ROOT; | ||
357 | atomic_set(&ent->count, 1); | 415 | atomic_set(&ent->count, 1); |
358 | spin_lock_init(&ent->pde_unload_lock); | 416 | spin_lock_init(&ent->pde_unload_lock); |
359 | INIT_LIST_HEAD(&ent->pde_openers); | 417 | INIT_LIST_HEAD(&ent->pde_openers); |
@@ -485,7 +543,6 @@ void pde_put(struct proc_dir_entry *pde) | |||
485 | */ | 543 | */ |
486 | void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | 544 | void remove_proc_entry(const char *name, struct proc_dir_entry *parent) |
487 | { | 545 | { |
488 | struct proc_dir_entry **p; | ||
489 | struct proc_dir_entry *de = NULL; | 546 | struct proc_dir_entry *de = NULL; |
490 | const char *fn = name; | 547 | const char *fn = name; |
491 | unsigned int len; | 548 | unsigned int len; |
@@ -497,14 +554,9 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
497 | } | 554 | } |
498 | len = strlen(fn); | 555 | len = strlen(fn); |
499 | 556 | ||
500 | for (p = &parent->subdir; *p; p=&(*p)->next ) { | 557 | de = pde_subdir_find(parent, fn, len); |
501 | if (proc_match(len, fn, *p)) { | 558 | if (de) |
502 | de = *p; | 559 | rb_erase(&de->subdir_node, &parent->subdir); |
503 | *p = de->next; | ||
504 | de->next = NULL; | ||
505 | break; | ||
506 | } | ||
507 | } | ||
508 | spin_unlock(&proc_subdir_lock); | 560 | spin_unlock(&proc_subdir_lock); |
509 | if (!de) { | 561 | if (!de) { |
510 | WARN(1, "name '%s'\n", name); | 562 | WARN(1, "name '%s'\n", name); |
@@ -516,16 +568,15 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
516 | if (S_ISDIR(de->mode)) | 568 | if (S_ISDIR(de->mode)) |
517 | parent->nlink--; | 569 | parent->nlink--; |
518 | de->nlink = 0; | 570 | de->nlink = 0; |
519 | WARN(de->subdir, "%s: removing non-empty directory " | 571 | WARN(pde_subdir_first(de), |
520 | "'%s/%s', leaking at least '%s'\n", __func__, | 572 | "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n", |
521 | de->parent->name, de->name, de->subdir->name); | 573 | __func__, de->parent->name, de->name, pde_subdir_first(de)->name); |
522 | pde_put(de); | 574 | pde_put(de); |
523 | } | 575 | } |
524 | EXPORT_SYMBOL(remove_proc_entry); | 576 | EXPORT_SYMBOL(remove_proc_entry); |
525 | 577 | ||
526 | int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | 578 | int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) |
527 | { | 579 | { |
528 | struct proc_dir_entry **p; | ||
529 | struct proc_dir_entry *root = NULL, *de, *next; | 580 | struct proc_dir_entry *root = NULL, *de, *next; |
530 | const char *fn = name; | 581 | const char *fn = name; |
531 | unsigned int len; | 582 | unsigned int len; |
@@ -537,24 +588,18 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | |||
537 | } | 588 | } |
538 | len = strlen(fn); | 589 | len = strlen(fn); |
539 | 590 | ||
540 | for (p = &parent->subdir; *p; p=&(*p)->next ) { | 591 | root = pde_subdir_find(parent, fn, len); |
541 | if (proc_match(len, fn, *p)) { | ||
542 | root = *p; | ||
543 | *p = root->next; | ||
544 | root->next = NULL; | ||
545 | break; | ||
546 | } | ||
547 | } | ||
548 | if (!root) { | 592 | if (!root) { |
549 | spin_unlock(&proc_subdir_lock); | 593 | spin_unlock(&proc_subdir_lock); |
550 | return -ENOENT; | 594 | return -ENOENT; |
551 | } | 595 | } |
596 | rb_erase(&root->subdir_node, &parent->subdir); | ||
597 | |||
552 | de = root; | 598 | de = root; |
553 | while (1) { | 599 | while (1) { |
554 | next = de->subdir; | 600 | next = pde_subdir_first(de); |
555 | if (next) { | 601 | if (next) { |
556 | de->subdir = next->next; | 602 | rb_erase(&next->subdir_node, &de->subdir); |
557 | next->next = NULL; | ||
558 | de = next; | 603 | de = next; |
559 | continue; | 604 | continue; |
560 | } | 605 | } |