diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/proc/generic.c | 164 | ||||
-rw-r--r-- | fs/proc/internal.h | 11 | ||||
-rw-r--r-- | fs/proc/proc_net.c | 1 | ||||
-rw-r--r-- | fs/proc/root.c | 1 |
4 files changed, 113 insertions, 64 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 317b72641ebf..9f8fa1e5e8aa 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -31,9 +31,81 @@ 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 | struct rb_node *node = rb_first(&dir->subdir); | ||
45 | |||
46 | if (node == NULL) | ||
47 | return NULL; | ||
48 | |||
49 | return rb_entry(node, struct proc_dir_entry, subdir_node); | ||
50 | } | ||
51 | |||
52 | static struct proc_dir_entry *pde_subdir_next(struct proc_dir_entry *dir) | ||
53 | { | ||
54 | struct rb_node *node = rb_next(&dir->subdir_node); | ||
55 | |||
56 | if (node == NULL) | ||
57 | return NULL; | ||
58 | |||
59 | return rb_entry(node, struct proc_dir_entry, subdir_node); | ||
60 | } | ||
61 | |||
62 | static struct proc_dir_entry *pde_subdir_find(struct proc_dir_entry *dir, | ||
63 | const char *name, | ||
64 | unsigned int len) | ||
65 | { | ||
66 | struct rb_node *node = dir->subdir.rb_node; | ||
67 | |||
68 | while (node) { | ||
69 | struct proc_dir_entry *de = container_of(node, | ||
70 | struct proc_dir_entry, | ||
71 | subdir_node); | ||
72 | int result = proc_match(len, name, de); | ||
73 | |||
74 | if (result < 0) | ||
75 | node = node->rb_left; | ||
76 | else if (result > 0) | ||
77 | node = node->rb_right; | ||
78 | else | ||
79 | return de; | ||
80 | } | ||
81 | return NULL; | ||
82 | } | ||
83 | |||
84 | static bool pde_subdir_insert(struct proc_dir_entry *dir, | ||
85 | struct proc_dir_entry *de) | ||
86 | { | ||
87 | struct rb_root *root = &dir->subdir; | ||
88 | struct rb_node **new = &root->rb_node, *parent = NULL; | ||
89 | |||
90 | /* Figure out where to put new node */ | ||
91 | while (*new) { | ||
92 | struct proc_dir_entry *this = | ||
93 | container_of(*new, struct proc_dir_entry, subdir_node); | ||
94 | int result = proc_match(de->namelen, de->name, this); | ||
95 | |||
96 | parent = *new; | ||
97 | if (result < 0) | ||
98 | new = &(*new)->rb_left; | ||
99 | else if (result > 0) | ||
100 | new = &(*new)->rb_right; | ||
101 | else | ||
102 | return false; | ||
103 | } | ||
104 | |||
105 | /* Add new node and rebalance tree. */ | ||
106 | rb_link_node(&de->subdir_node, parent, new); | ||
107 | rb_insert_color(&de->subdir_node, root); | ||
108 | return true; | ||
37 | } | 109 | } |
38 | 110 | ||
39 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) | 111 | static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) |
@@ -92,10 +164,7 @@ static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret, | |||
92 | break; | 164 | break; |
93 | 165 | ||
94 | len = next - cp; | 166 | len = next - cp; |
95 | for (de = de->subdir; de ; de = de->next) { | 167 | de = pde_subdir_find(de, cp, len); |
96 | if (proc_match(len, cp, de)) | ||
97 | break; | ||
98 | } | ||
99 | if (!de) { | 168 | if (!de) { |
100 | WARN(1, "name '%s'\n", name); | 169 | WARN(1, "name '%s'\n", name); |
101 | return -ENOENT; | 170 | return -ENOENT; |
@@ -183,19 +252,16 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir, | |||
183 | struct inode *inode; | 252 | struct inode *inode; |
184 | 253 | ||
185 | spin_lock(&proc_subdir_lock); | 254 | spin_lock(&proc_subdir_lock); |
186 | for (de = de->subdir; de ; de = de->next) { | 255 | de = pde_subdir_find(de, dentry->d_name.name, dentry->d_name.len); |
187 | if (de->namelen != dentry->d_name.len) | 256 | if (de) { |
188 | continue; | 257 | pde_get(de); |
189 | if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { | 258 | spin_unlock(&proc_subdir_lock); |
190 | pde_get(de); | 259 | inode = proc_get_inode(dir->i_sb, de); |
191 | spin_unlock(&proc_subdir_lock); | 260 | if (!inode) |
192 | inode = proc_get_inode(dir->i_sb, de); | 261 | return ERR_PTR(-ENOMEM); |
193 | if (!inode) | 262 | d_set_d_op(dentry, &simple_dentry_operations); |
194 | return ERR_PTR(-ENOMEM); | 263 | d_add(dentry, inode); |
195 | d_set_d_op(dentry, &simple_dentry_operations); | 264 | return NULL; |
196 | d_add(dentry, inode); | ||
197 | return NULL; | ||
198 | } | ||
199 | } | 265 | } |
200 | spin_unlock(&proc_subdir_lock); | 266 | spin_unlock(&proc_subdir_lock); |
201 | return ERR_PTR(-ENOENT); | 267 | return ERR_PTR(-ENOENT); |
@@ -225,7 +291,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
225 | return 0; | 291 | return 0; |
226 | 292 | ||
227 | spin_lock(&proc_subdir_lock); | 293 | spin_lock(&proc_subdir_lock); |
228 | de = de->subdir; | 294 | de = pde_subdir_first(de); |
229 | i = ctx->pos - 2; | 295 | i = ctx->pos - 2; |
230 | for (;;) { | 296 | for (;;) { |
231 | if (!de) { | 297 | if (!de) { |
@@ -234,7 +300,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
234 | } | 300 | } |
235 | if (!i) | 301 | if (!i) |
236 | break; | 302 | break; |
237 | de = de->next; | 303 | de = pde_subdir_next(de); |
238 | i--; | 304 | i--; |
239 | } | 305 | } |
240 | 306 | ||
@@ -249,7 +315,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file, | |||
249 | } | 315 | } |
250 | spin_lock(&proc_subdir_lock); | 316 | spin_lock(&proc_subdir_lock); |
251 | ctx->pos++; | 317 | ctx->pos++; |
252 | next = de->next; | 318 | next = pde_subdir_next(de); |
253 | pde_put(de); | 319 | pde_put(de); |
254 | de = next; | 320 | de = next; |
255 | } while (de); | 321 | } while (de); |
@@ -286,9 +352,8 @@ static const struct inode_operations proc_dir_inode_operations = { | |||
286 | 352 | ||
287 | static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) | 353 | static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) |
288 | { | 354 | { |
289 | struct proc_dir_entry *tmp; | ||
290 | int ret; | 355 | int ret; |
291 | 356 | ||
292 | ret = proc_alloc_inum(&dp->low_ino); | 357 | ret = proc_alloc_inum(&dp->low_ino); |
293 | if (ret) | 358 | if (ret) |
294 | return ret; | 359 | return ret; |
@@ -308,17 +373,10 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp | |||
308 | } | 373 | } |
309 | 374 | ||
310 | spin_lock(&proc_subdir_lock); | 375 | 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; | 376 | dp->parent = dir; |
321 | dir->subdir = dp; | 377 | if (pde_subdir_insert(dir, dp) == false) |
378 | WARN(1, "proc_dir_entry '%s/%s' already registered\n", | ||
379 | dir->name, dp->name); | ||
322 | spin_unlock(&proc_subdir_lock); | 380 | spin_unlock(&proc_subdir_lock); |
323 | 381 | ||
324 | return 0; | 382 | return 0; |
@@ -354,6 +412,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, | |||
354 | ent->namelen = qstr.len; | 412 | ent->namelen = qstr.len; |
355 | ent->mode = mode; | 413 | ent->mode = mode; |
356 | ent->nlink = nlink; | 414 | ent->nlink = nlink; |
415 | ent->subdir = RB_ROOT; | ||
357 | atomic_set(&ent->count, 1); | 416 | atomic_set(&ent->count, 1); |
358 | spin_lock_init(&ent->pde_unload_lock); | 417 | spin_lock_init(&ent->pde_unload_lock); |
359 | INIT_LIST_HEAD(&ent->pde_openers); | 418 | INIT_LIST_HEAD(&ent->pde_openers); |
@@ -485,7 +544,6 @@ void pde_put(struct proc_dir_entry *pde) | |||
485 | */ | 544 | */ |
486 | void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | 545 | void remove_proc_entry(const char *name, struct proc_dir_entry *parent) |
487 | { | 546 | { |
488 | struct proc_dir_entry **p; | ||
489 | struct proc_dir_entry *de = NULL; | 547 | struct proc_dir_entry *de = NULL; |
490 | const char *fn = name; | 548 | const char *fn = name; |
491 | unsigned int len; | 549 | unsigned int len; |
@@ -497,14 +555,9 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
497 | } | 555 | } |
498 | len = strlen(fn); | 556 | len = strlen(fn); |
499 | 557 | ||
500 | for (p = &parent->subdir; *p; p=&(*p)->next ) { | 558 | de = pde_subdir_find(parent, fn, len); |
501 | if (proc_match(len, fn, *p)) { | 559 | if (de) |
502 | de = *p; | 560 | 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); | 561 | spin_unlock(&proc_subdir_lock); |
509 | if (!de) { | 562 | if (!de) { |
510 | WARN(1, "name '%s'\n", name); | 563 | WARN(1, "name '%s'\n", name); |
@@ -516,16 +569,15 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
516 | if (S_ISDIR(de->mode)) | 569 | if (S_ISDIR(de->mode)) |
517 | parent->nlink--; | 570 | parent->nlink--; |
518 | de->nlink = 0; | 571 | de->nlink = 0; |
519 | WARN(de->subdir, "%s: removing non-empty directory " | 572 | WARN(pde_subdir_first(de), |
520 | "'%s/%s', leaking at least '%s'\n", __func__, | 573 | "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n", |
521 | de->parent->name, de->name, de->subdir->name); | 574 | __func__, de->parent->name, de->name, pde_subdir_first(de)->name); |
522 | pde_put(de); | 575 | pde_put(de); |
523 | } | 576 | } |
524 | EXPORT_SYMBOL(remove_proc_entry); | 577 | EXPORT_SYMBOL(remove_proc_entry); |
525 | 578 | ||
526 | int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | 579 | int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) |
527 | { | 580 | { |
528 | struct proc_dir_entry **p; | ||
529 | struct proc_dir_entry *root = NULL, *de, *next; | 581 | struct proc_dir_entry *root = NULL, *de, *next; |
530 | const char *fn = name; | 582 | const char *fn = name; |
531 | unsigned int len; | 583 | unsigned int len; |
@@ -537,24 +589,18 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) | |||
537 | } | 589 | } |
538 | len = strlen(fn); | 590 | len = strlen(fn); |
539 | 591 | ||
540 | for (p = &parent->subdir; *p; p=&(*p)->next ) { | 592 | 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) { | 593 | if (!root) { |
549 | spin_unlock(&proc_subdir_lock); | 594 | spin_unlock(&proc_subdir_lock); |
550 | return -ENOENT; | 595 | return -ENOENT; |
551 | } | 596 | } |
597 | rb_erase(&root->subdir_node, &parent->subdir); | ||
598 | |||
552 | de = root; | 599 | de = root; |
553 | while (1) { | 600 | while (1) { |
554 | next = de->subdir; | 601 | next = pde_subdir_first(de); |
555 | if (next) { | 602 | if (next) { |
556 | de->subdir = next->next; | 603 | rb_erase(&next->subdir_node, &de->subdir); |
557 | next->next = NULL; | ||
558 | de = next; | 604 | de = next; |
559 | continue; | 605 | continue; |
560 | } | 606 | } |
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index aa7a0ee182e1..7fb1a4869fd0 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -24,10 +24,9 @@ struct mempolicy; | |||
24 | * tree) of these proc_dir_entries, so that we can dynamically | 24 | * tree) of these proc_dir_entries, so that we can dynamically |
25 | * add new files to /proc. | 25 | * add new files to /proc. |
26 | * | 26 | * |
27 | * The "next" pointer creates a linked list of one /proc directory, | 27 | * parent/subdir are used for the directory structure (every /proc file has a |
28 | * while parent/subdir create the directory structure (every | 28 | * parent, but "subdir" is empty for all non-directory entries). |
29 | * /proc file has a parent, but "subdir" is NULL for all | 29 | * subdir_node is used to build the rb tree "subdir" of the parent. |
30 | * non-directory entries). | ||
31 | */ | 30 | */ |
32 | struct proc_dir_entry { | 31 | struct proc_dir_entry { |
33 | unsigned int low_ino; | 32 | unsigned int low_ino; |
@@ -38,7 +37,9 @@ struct proc_dir_entry { | |||
38 | loff_t size; | 37 | loff_t size; |
39 | const struct inode_operations *proc_iops; | 38 | const struct inode_operations *proc_iops; |
40 | const struct file_operations *proc_fops; | 39 | const struct file_operations *proc_fops; |
41 | struct proc_dir_entry *next, *parent, *subdir; | 40 | struct proc_dir_entry *parent; |
41 | struct rb_root subdir; | ||
42 | struct rb_node subdir_node; | ||
42 | void *data; | 43 | void *data; |
43 | atomic_t count; /* use count */ | 44 | atomic_t count; /* use count */ |
44 | atomic_t in_use; /* number of callers into module in progress; */ | 45 | atomic_t in_use; /* number of callers into module in progress; */ |
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index a63af3e0a612..1bde894bc624 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c | |||
@@ -192,6 +192,7 @@ static __net_init int proc_net_ns_init(struct net *net) | |||
192 | if (!netd) | 192 | if (!netd) |
193 | goto out; | 193 | goto out; |
194 | 194 | ||
195 | netd->subdir = RB_ROOT; | ||
195 | netd->data = net; | 196 | netd->data = net; |
196 | netd->nlink = 2; | 197 | netd->nlink = 2; |
197 | netd->namelen = 3; | 198 | netd->namelen = 3; |
diff --git a/fs/proc/root.c b/fs/proc/root.c index 094e44d4a6be..e74ac9f1a2c0 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -251,6 +251,7 @@ struct proc_dir_entry proc_root = { | |||
251 | .proc_iops = &proc_root_inode_operations, | 251 | .proc_iops = &proc_root_inode_operations, |
252 | .proc_fops = &proc_root_operations, | 252 | .proc_fops = &proc_root_operations, |
253 | .parent = &proc_root, | 253 | .parent = &proc_root, |
254 | .subdir = RB_ROOT, | ||
254 | .name = "/proc", | 255 | .name = "/proc", |
255 | }; | 256 | }; |
256 | 257 | ||