diff options
Diffstat (limited to 'fs/proc/generic.c')
-rw-r--r-- | fs/proc/generic.c | 60 |
1 files changed, 41 insertions, 19 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index fa678abc9db1..43c127490606 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/proc_fs.h> | 13 | #include <linux/proc_fs.h> |
14 | #include <linux/stat.h> | 14 | #include <linux/stat.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/slab.h> | ||
16 | #include <linux/mount.h> | 17 | #include <linux/mount.h> |
17 | #include <linux/init.h> | 18 | #include <linux/init.h> |
18 | #include <linux/idr.h> | 19 | #include <linux/idr.h> |
@@ -291,19 +292,17 @@ static const struct inode_operations proc_file_inode_operations = { | |||
291 | * returns the struct proc_dir_entry for "/proc/tty/driver", and | 292 | * returns the struct proc_dir_entry for "/proc/tty/driver", and |
292 | * returns "serial" in residual. | 293 | * returns "serial" in residual. |
293 | */ | 294 | */ |
294 | static int xlate_proc_name(const char *name, | 295 | static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret, |
295 | struct proc_dir_entry **ret, const char **residual) | 296 | const char **residual) |
296 | { | 297 | { |
297 | const char *cp = name, *next; | 298 | const char *cp = name, *next; |
298 | struct proc_dir_entry *de; | 299 | struct proc_dir_entry *de; |
299 | int len; | 300 | int len; |
300 | int rtn = 0; | ||
301 | 301 | ||
302 | de = *ret; | 302 | de = *ret; |
303 | if (!de) | 303 | if (!de) |
304 | de = &proc_root; | 304 | de = &proc_root; |
305 | 305 | ||
306 | spin_lock(&proc_subdir_lock); | ||
307 | while (1) { | 306 | while (1) { |
308 | next = strchr(cp, '/'); | 307 | next = strchr(cp, '/'); |
309 | if (!next) | 308 | if (!next) |
@@ -315,16 +314,25 @@ static int xlate_proc_name(const char *name, | |||
315 | break; | 314 | break; |
316 | } | 315 | } |
317 | if (!de) { | 316 | if (!de) { |
318 | rtn = -ENOENT; | 317 | WARN(1, "name '%s'\n", name); |
319 | goto out; | 318 | return -ENOENT; |
320 | } | 319 | } |
321 | cp += len + 1; | 320 | cp += len + 1; |
322 | } | 321 | } |
323 | *residual = cp; | 322 | *residual = cp; |
324 | *ret = de; | 323 | *ret = de; |
325 | out: | 324 | return 0; |
325 | } | ||
326 | |||
327 | static int xlate_proc_name(const char *name, struct proc_dir_entry **ret, | ||
328 | const char **residual) | ||
329 | { | ||
330 | int rv; | ||
331 | |||
332 | spin_lock(&proc_subdir_lock); | ||
333 | rv = __xlate_proc_name(name, ret, residual); | ||
326 | spin_unlock(&proc_subdir_lock); | 334 | spin_unlock(&proc_subdir_lock); |
327 | return rtn; | 335 | return rv; |
328 | } | 336 | } |
329 | 337 | ||
330 | static DEFINE_IDA(proc_inum_ida); | 338 | static DEFINE_IDA(proc_inum_ida); |
@@ -429,7 +437,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir, | |||
429 | unsigned int ino; | 437 | unsigned int ino; |
430 | 438 | ||
431 | ino = de->low_ino; | 439 | ino = de->low_ino; |
432 | de_get(de); | 440 | pde_get(de); |
433 | spin_unlock(&proc_subdir_lock); | 441 | spin_unlock(&proc_subdir_lock); |
434 | error = -EINVAL; | 442 | error = -EINVAL; |
435 | inode = proc_get_inode(dir->i_sb, ino, de); | 443 | inode = proc_get_inode(dir->i_sb, ino, de); |
@@ -445,7 +453,7 @@ out_unlock: | |||
445 | return NULL; | 453 | return NULL; |
446 | } | 454 | } |
447 | if (de) | 455 | if (de) |
448 | de_put(de); | 456 | pde_put(de); |
449 | return ERR_PTR(error); | 457 | return ERR_PTR(error); |
450 | } | 458 | } |
451 | 459 | ||
@@ -509,17 +517,17 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent, | |||
509 | struct proc_dir_entry *next; | 517 | struct proc_dir_entry *next; |
510 | 518 | ||
511 | /* filldir passes info to user space */ | 519 | /* filldir passes info to user space */ |
512 | de_get(de); | 520 | pde_get(de); |
513 | spin_unlock(&proc_subdir_lock); | 521 | spin_unlock(&proc_subdir_lock); |
514 | if (filldir(dirent, de->name, de->namelen, filp->f_pos, | 522 | if (filldir(dirent, de->name, de->namelen, filp->f_pos, |
515 | de->low_ino, de->mode >> 12) < 0) { | 523 | de->low_ino, de->mode >> 12) < 0) { |
516 | de_put(de); | 524 | pde_put(de); |
517 | goto out; | 525 | goto out; |
518 | } | 526 | } |
519 | spin_lock(&proc_subdir_lock); | 527 | spin_lock(&proc_subdir_lock); |
520 | filp->f_pos++; | 528 | filp->f_pos++; |
521 | next = de->next; | 529 | next = de->next; |
522 | de_put(de); | 530 | pde_put(de); |
523 | de = next; | 531 | de = next; |
524 | } while (de); | 532 | } while (de); |
525 | spin_unlock(&proc_subdir_lock); | 533 | spin_unlock(&proc_subdir_lock); |
@@ -662,6 +670,7 @@ struct proc_dir_entry *proc_symlink(const char *name, | |||
662 | } | 670 | } |
663 | return ent; | 671 | return ent; |
664 | } | 672 | } |
673 | EXPORT_SYMBOL(proc_symlink); | ||
665 | 674 | ||
666 | struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode, | 675 | struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode, |
667 | struct proc_dir_entry *parent) | 676 | struct proc_dir_entry *parent) |
@@ -700,6 +709,7 @@ struct proc_dir_entry *proc_mkdir(const char *name, | |||
700 | { | 709 | { |
701 | return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent); | 710 | return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent); |
702 | } | 711 | } |
712 | EXPORT_SYMBOL(proc_mkdir); | ||
703 | 713 | ||
704 | struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, | 714 | struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, |
705 | struct proc_dir_entry *parent) | 715 | struct proc_dir_entry *parent) |
@@ -728,6 +738,7 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, | |||
728 | } | 738 | } |
729 | return ent; | 739 | return ent; |
730 | } | 740 | } |
741 | EXPORT_SYMBOL(create_proc_entry); | ||
731 | 742 | ||
732 | struct proc_dir_entry *proc_create_data(const char *name, mode_t mode, | 743 | struct proc_dir_entry *proc_create_data(const char *name, mode_t mode, |
733 | struct proc_dir_entry *parent, | 744 | struct proc_dir_entry *parent, |
@@ -762,8 +773,9 @@ out_free: | |||
762 | out: | 773 | out: |
763 | return NULL; | 774 | return NULL; |
764 | } | 775 | } |
776 | EXPORT_SYMBOL(proc_create_data); | ||
765 | 777 | ||
766 | void free_proc_entry(struct proc_dir_entry *de) | 778 | static void free_proc_entry(struct proc_dir_entry *de) |
767 | { | 779 | { |
768 | unsigned int ino = de->low_ino; | 780 | unsigned int ino = de->low_ino; |
769 | 781 | ||
@@ -777,6 +789,12 @@ void free_proc_entry(struct proc_dir_entry *de) | |||
777 | kfree(de); | 789 | kfree(de); |
778 | } | 790 | } |
779 | 791 | ||
792 | void pde_put(struct proc_dir_entry *pde) | ||
793 | { | ||
794 | if (atomic_dec_and_test(&pde->count)) | ||
795 | free_proc_entry(pde); | ||
796 | } | ||
797 | |||
780 | /* | 798 | /* |
781 | * Remove a /proc entry and free it if it's not currently in use. | 799 | * Remove a /proc entry and free it if it's not currently in use. |
782 | */ | 800 | */ |
@@ -787,11 +805,13 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
787 | const char *fn = name; | 805 | const char *fn = name; |
788 | int len; | 806 | int len; |
789 | 807 | ||
790 | if (xlate_proc_name(name, &parent, &fn) != 0) | 808 | spin_lock(&proc_subdir_lock); |
809 | if (__xlate_proc_name(name, &parent, &fn) != 0) { | ||
810 | spin_unlock(&proc_subdir_lock); | ||
791 | return; | 811 | return; |
812 | } | ||
792 | len = strlen(fn); | 813 | len = strlen(fn); |
793 | 814 | ||
794 | spin_lock(&proc_subdir_lock); | ||
795 | for (p = &parent->subdir; *p; p=&(*p)->next ) { | 815 | for (p = &parent->subdir; *p; p=&(*p)->next ) { |
796 | if (proc_match(len, fn, *p)) { | 816 | if (proc_match(len, fn, *p)) { |
797 | de = *p; | 817 | de = *p; |
@@ -801,8 +821,10 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) | |||
801 | } | 821 | } |
802 | } | 822 | } |
803 | spin_unlock(&proc_subdir_lock); | 823 | spin_unlock(&proc_subdir_lock); |
804 | if (!de) | 824 | if (!de) { |
825 | WARN(1, "name '%s'\n", name); | ||
805 | return; | 826 | return; |
827 | } | ||
806 | 828 | ||
807 | spin_lock(&de->pde_unload_lock); | 829 | spin_lock(&de->pde_unload_lock); |
808 | /* | 830 | /* |
@@ -845,6 +867,6 @@ continue_removing: | |||
845 | WARN(de->subdir, KERN_WARNING "%s: removing non-empty directory " | 867 | WARN(de->subdir, KERN_WARNING "%s: removing non-empty directory " |
846 | "'%s/%s', leaking at least '%s'\n", __func__, | 868 | "'%s/%s', leaking at least '%s'\n", __func__, |
847 | de->parent->name, de->name, de->subdir->name); | 869 | de->parent->name, de->name, de->subdir->name); |
848 | if (atomic_dec_and_test(&de->count)) | 870 | pde_put(de); |
849 | free_proc_entry(de); | ||
850 | } | 871 | } |
872 | EXPORT_SYMBOL(remove_proc_entry); | ||