aboutsummaryrefslogtreecommitdiff
path: root/fs/proc/generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/proc/generic.c')
-rw-r--r--fs/proc/generic.c175
1 files changed, 109 insertions, 66 deletions
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index 76ddae83daa..21e1a8f1659 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -15,6 +15,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/printk.h>
#include <linux/mount.h>
#include <linux/init.h>
#include <linux/idr.h>
@@ -42,7 +43,7 @@ static ssize_t
__proc_file_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
- struct inode * inode = file->f_path.dentry->d_inode;
+ struct inode * inode = file_inode(file);
char *page;
ssize_t retval=0;
int eof=0;
@@ -132,11 +133,8 @@ __proc_file_read(struct file *file, char __user *buf, size_t nbytes,
}
if (start == NULL) {
- if (n > PAGE_SIZE) {
- printk(KERN_ERR
- "proc_file_read: Apparent buffer overflow!\n");
+ if (n > PAGE_SIZE) /* Apparent buffer overflow */
n = PAGE_SIZE;
- }
n -= *ppos;
if (n <= 0)
break;
@@ -144,26 +142,19 @@ __proc_file_read(struct file *file, char __user *buf, size_t nbytes,
n = count;
start = page + *ppos;
} else if (start < page) {
- if (n > PAGE_SIZE) {
- printk(KERN_ERR
- "proc_file_read: Apparent buffer overflow!\n");
+ if (n > PAGE_SIZE) /* Apparent buffer overflow */
n = PAGE_SIZE;
- }
if (n > count) {
/*
* Don't reduce n because doing so might
* cut off part of a data block.
*/
- printk(KERN_WARNING
- "proc_file_read: Read count exceeded\n");
+ pr_warn("proc_file_read: count exceeded\n");
}
} else /* start >= page */ {
unsigned long startoff = (unsigned long)(start - page);
- if (n > (PAGE_SIZE - startoff)) {
- printk(KERN_ERR
- "proc_file_read: Apparent buffer overflow!\n");
+ if (n > (PAGE_SIZE - startoff)) /* buffer overflow? */
n = PAGE_SIZE - startoff;
- }
if (n > count)
n = count;
}
@@ -188,7 +179,7 @@ static ssize_t
proc_file_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
- struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
+ struct proc_dir_entry *pde = PDE(file_inode(file));
ssize_t rv = -EIO;
spin_lock(&pde->pde_unload_lock);
@@ -209,7 +200,7 @@ static ssize_t
proc_file_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
- struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
+ struct proc_dir_entry *pde = PDE(file_inode(file));
ssize_t rv = -EIO;
if (pde->write_proc) {
@@ -412,8 +403,7 @@ static const struct dentry_operations proc_dentry_operations =
struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
struct dentry *dentry)
{
- struct inode *inode = NULL;
- int error = -ENOENT;
+ struct inode *inode;
spin_lock(&proc_subdir_lock);
for (de = de->subdir; de ; de = de->next) {
@@ -422,22 +412,16 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
pde_get(de);
spin_unlock(&proc_subdir_lock);
- error = -ENOMEM;
inode = proc_get_inode(dir->i_sb, de);
- goto out_unlock;
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ d_set_d_op(dentry, &proc_dentry_operations);
+ d_add(dentry, inode);
+ return NULL;
}
}
spin_unlock(&proc_subdir_lock);
-out_unlock:
-
- if (inode) {
- d_set_d_op(dentry, &proc_dentry_operations);
- d_add(dentry, inode);
- return NULL;
- }
- if (de)
- pde_put(de);
- return ERR_PTR(error);
+ return ERR_PTR(-ENOENT);
}
struct dentry *proc_lookup(struct inode *dir, struct dentry *dentry,
@@ -460,7 +444,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
{
unsigned int ino;
int i;
- struct inode *inode = filp->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(filp);
int ret = 0;
ino = inode->i_ino;
@@ -522,7 +506,7 @@ out:
int proc_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct inode *inode = filp->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(filp);
return proc_readdir_de(PDE(inode), filp, dirent, filldir);
}
@@ -576,7 +560,7 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
for (tmp = dir->subdir; tmp; tmp = tmp->next)
if (strcmp(tmp->name, dp->name) == 0) {
- WARN(1, KERN_WARNING "proc_dir_entry '%s/%s' already registered\n",
+ WARN(1, "proc_dir_entry '%s/%s' already registered\n",
dir->name, dp->name);
break;
}
@@ -771,6 +755,41 @@ void pde_put(struct proc_dir_entry *pde)
free_proc_entry(pde);
}
+static void entry_rundown(struct proc_dir_entry *de)
+{
+ spin_lock(&de->pde_unload_lock);
+ /*
+ * Stop accepting new callers into module. If you're
+ * dynamically allocating ->proc_fops, save a pointer somewhere.
+ */
+ de->proc_fops = NULL;
+ /* Wait until all existing callers into module are done. */
+ if (de->pde_users > 0) {
+ DECLARE_COMPLETION_ONSTACK(c);
+
+ if (!de->pde_unload_completion)
+ de->pde_unload_completion = &c;
+
+ spin_unlock(&de->pde_unload_lock);
+
+ wait_for_completion(de->pde_unload_completion);
+
+ spin_lock(&de->pde_unload_lock);
+ }
+
+ while (!list_empty(&de->pde_openers)) {
+ struct pde_opener *pdeo;
+
+ pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
+ list_del(&pdeo->lh);
+ spin_unlock(&de->pde_unload_lock);
+ pdeo->release(pdeo->inode, pdeo->file);
+ kfree(pdeo);
+ spin_lock(&de->pde_unload_lock);
+ }
+ spin_unlock(&de->pde_unload_lock);
+}
+
/*
* Remove a /proc entry and free it if it's not currently in use.
*/
@@ -802,44 +821,68 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
return;
}
- spin_lock(&de->pde_unload_lock);
- /*
- * Stop accepting new callers into module. If you're
- * dynamically allocating ->proc_fops, save a pointer somewhere.
- */
- de->proc_fops = NULL;
- /* Wait until all existing callers into module are done. */
- if (de->pde_users > 0) {
- DECLARE_COMPLETION_ONSTACK(c);
+ entry_rundown(de);
- if (!de->pde_unload_completion)
- de->pde_unload_completion = &c;
+ if (S_ISDIR(de->mode))
+ parent->nlink--;
+ de->nlink = 0;
+ WARN(de->subdir, "%s: removing non-empty directory "
+ "'%s/%s', leaking at least '%s'\n", __func__,
+ de->parent->name, de->name, de->subdir->name);
+ pde_put(de);
+}
+EXPORT_SYMBOL(remove_proc_entry);
- spin_unlock(&de->pde_unload_lock);
+int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry **p;
+ struct proc_dir_entry *root = NULL, *de, *next;
+ const char *fn = name;
+ unsigned int len;
- wait_for_completion(de->pde_unload_completion);
+ spin_lock(&proc_subdir_lock);
+ if (__xlate_proc_name(name, &parent, &fn) != 0) {
+ spin_unlock(&proc_subdir_lock);
+ return -ENOENT;
+ }
+ len = strlen(fn);
- spin_lock(&de->pde_unload_lock);
+ for (p = &parent->subdir; *p; p=&(*p)->next ) {
+ if (proc_match(len, fn, *p)) {
+ root = *p;
+ *p = root->next;
+ root->next = NULL;
+ break;
+ }
}
+ if (!root) {
+ spin_unlock(&proc_subdir_lock);
+ return -ENOENT;
+ }
+ de = root;
+ while (1) {
+ next = de->subdir;
+ if (next) {
+ de->subdir = next->next;
+ next->next = NULL;
+ de = next;
+ continue;
+ }
+ spin_unlock(&proc_subdir_lock);
- while (!list_empty(&de->pde_openers)) {
- struct pde_opener *pdeo;
+ entry_rundown(de);
+ next = de->parent;
+ if (S_ISDIR(de->mode))
+ next->nlink--;
+ de->nlink = 0;
+ if (de == root)
+ break;
+ pde_put(de);
- pdeo = list_first_entry(&de->pde_openers, struct pde_opener, lh);
- list_del(&pdeo->lh);
- spin_unlock(&de->pde_unload_lock);
- pdeo->release(pdeo->inode, pdeo->file);
- kfree(pdeo);
- spin_lock(&de->pde_unload_lock);
+ spin_lock(&proc_subdir_lock);
+ de = next;
}
- spin_unlock(&de->pde_unload_lock);
-
- if (S_ISDIR(de->mode))
- parent->nlink--;
- de->nlink = 0;
- WARN(de->subdir, KERN_WARNING "%s: removing non-empty directory "
- "'%s/%s', leaking at least '%s'\n", __func__,
- de->parent->name, de->name, de->subdir->name);
- pde_put(de);
+ pde_put(root);
+ return 0;
}
-EXPORT_SYMBOL(remove_proc_entry);
+EXPORT_SYMBOL(remove_proc_subtree);