proc: warn on non-existing proc entries
[safe/jmp/linux-2.6] / fs / proc / generic.c
index 0f3d97d..08f4d71 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/stat.h>
 #include <linux/module.h>
 #include <linux/mount.h>
-#include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/idr.h>
 #include <linux/namei.h>
@@ -38,7 +37,7 @@ static int proc_match(int len, const char *name, struct proc_dir_entry *de)
 #define PROC_BLOCK_SIZE        (PAGE_SIZE - 1024)
 
 static ssize_t
-proc_file_read(struct file *file, char __user *buf, size_t nbytes,
+__proc_file_read(struct file *file, char __user *buf, size_t nbytes,
               loff_t *ppos)
 {
        struct inode * inode = file->f_path.dentry->d_inode;
@@ -184,19 +183,47 @@ proc_file_read(struct file *file, char __user *buf, size_t nbytes,
 }
 
 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);
+       ssize_t rv = -EIO;
+
+       spin_lock(&pde->pde_unload_lock);
+       if (!pde->proc_fops) {
+               spin_unlock(&pde->pde_unload_lock);
+               return rv;
+       }
+       pde->pde_users++;
+       spin_unlock(&pde->pde_unload_lock);
+
+       rv = __proc_file_read(file, buf, nbytes, ppos);
+
+       pde_users_dec(pde);
+       return rv;
+}
+
+static ssize_t
 proc_file_write(struct file *file, const char __user *buffer,
                size_t count, loff_t *ppos)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
-       struct proc_dir_entry * dp;
-       
-       dp = PDE(inode);
-
-       if (!dp->write_proc)
-               return -EIO;
+       struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
+       ssize_t rv = -EIO;
+
+       if (pde->write_proc) {
+               spin_lock(&pde->pde_unload_lock);
+               if (!pde->proc_fops) {
+                       spin_unlock(&pde->pde_unload_lock);
+                       return rv;
+               }
+               pde->pde_users++;
+               spin_unlock(&pde->pde_unload_lock);
 
-       /* FIXME: does this routine need ppos?  probably... */
-       return dp->write_proc(file, buffer, count, dp->data);
+               /* FIXME: does this routine need ppos?  probably... */
+               rv = pde->write_proc(file, buffer, count, pde->data);
+               pde_users_dec(pde);
+       }
+       return rv;
 }
 
 
@@ -264,19 +291,17 @@ static const struct inode_operations proc_file_inode_operations = {
  * returns the struct proc_dir_entry for "/proc/tty/driver", and
  * returns "serial" in residual.
  */
-static int xlate_proc_name(const char *name,
-                          struct proc_dir_entry **ret, const char **residual)
+static int __xlate_proc_name(const char *name, struct proc_dir_entry **ret,
+                            const char **residual)
 {
        const char              *cp = name, *next;
        struct proc_dir_entry   *de;
        int                     len;
-       int                     rtn = 0;
 
        de = *ret;
        if (!de)
                de = &proc_root;
 
-       spin_lock(&proc_subdir_lock);
        while (1) {
                next = strchr(cp, '/');
                if (!next)
@@ -288,59 +313,81 @@ static int xlate_proc_name(const char *name,
                                break;
                }
                if (!de) {
-                       rtn = -ENOENT;
-                       goto out;
+                       WARN(1, "name '%s'\n", name);
+                       return -ENOENT;
                }
                cp += len + 1;
        }
        *residual = cp;
        *ret = de;
-out:
+       return 0;
+}
+
+static int xlate_proc_name(const char *name, struct proc_dir_entry **ret,
+                          const char **residual)
+{
+       int rv;
+
+       spin_lock(&proc_subdir_lock);
+       rv = __xlate_proc_name(name, ret, residual);
        spin_unlock(&proc_subdir_lock);
-       return rtn;
+       return rv;
 }
 
-static DEFINE_IDR(proc_inum_idr);
+static DEFINE_IDA(proc_inum_ida);
 static DEFINE_SPINLOCK(proc_inum_lock); /* protects the above */
 
-#define PROC_DYNAMIC_FIRST 0xF0000000UL
+#define PROC_DYNAMIC_FIRST 0xF0000000U
 
 /*
  * Return an inode number between PROC_DYNAMIC_FIRST and
  * 0xffffffff, or zero on failure.
+ *
+ * Current inode allocations in the proc-fs (hex-numbers):
+ *
+ * 00000000            reserved
+ * 00000001-00000fff   static entries  (goners)
+ *      001            root-ino
+ *
+ * 00001000-00001fff   unused
+ * 0001xxxx-7fffxxxx   pid-dir entries for pid 1-7fff
+ * 80000000-efffffff   unused
+ * f0000000-ffffffff   dynamic entries
+ *
+ * Goal:
+ *     Once we split the thing into several virtual filesystems,
+ *     we will get rid of magical ranges (and this comment, BTW).
  */
 static unsigned int get_inode_number(void)
 {
-       int i, inum = 0;
+       unsigned int i;
        int error;
 
 retry:
-       if (idr_pre_get(&proc_inum_idr, GFP_KERNEL) == 0)
+       if (ida_pre_get(&proc_inum_ida, GFP_KERNEL) == 0)
                return 0;
 
        spin_lock(&proc_inum_lock);
-       error = idr_get_new(&proc_inum_idr, NULL, &i);
+       error = ida_get_new(&proc_inum_ida, &i);
        spin_unlock(&proc_inum_lock);
        if (error == -EAGAIN)
                goto retry;
        else if (error)
                return 0;
 
-       inum = (i & MAX_ID_MASK) + PROC_DYNAMIC_FIRST;
-
-       /* inum will never be more than 0xf0ffffff, so no check
-        * for overflow.
-        */
-
-       return inum;
+       if (i > UINT_MAX - PROC_DYNAMIC_FIRST) {
+               spin_lock(&proc_inum_lock);
+               ida_remove(&proc_inum_ida, i);
+               spin_unlock(&proc_inum_lock);
+               return 0;
+       }
+       return PROC_DYNAMIC_FIRST + i;
 }
 
 static void release_inode_number(unsigned int inum)
 {
-       int id = (inum - PROC_DYNAMIC_FIRST) | ~MAX_ID_MASK;
-
        spin_lock(&proc_inum_lock);
-       idr_remove(&proc_inum_idr, id);
+       ida_remove(&proc_inum_ida, inum - PROC_DYNAMIC_FIRST);
        spin_unlock(&proc_inum_lock);
 }
 
@@ -366,7 +413,7 @@ static int proc_delete_dentry(struct dentry * dentry)
        return 1;
 }
 
-static struct dentry_operations proc_dentry_operations =
+static const struct dentry_operations proc_dentry_operations =
 {
        .d_delete       = proc_delete_dentry,
 };
@@ -381,7 +428,6 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
        struct inode *inode = NULL;
        int error = -ENOENT;
 
-       lock_kernel();
        spin_lock(&proc_subdir_lock);
        for (de = de->subdir; de ; de = de->next) {
                if (de->namelen != dentry->d_name.len)
@@ -390,7 +436,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
                        unsigned int ino;
 
                        ino = de->low_ino;
-                       de_get(de);
+                       pde_get(de);
                        spin_unlock(&proc_subdir_lock);
                        error = -EINVAL;
                        inode = proc_get_inode(dir->i_sb, ino, de);
@@ -399,7 +445,6 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir,
        }
        spin_unlock(&proc_subdir_lock);
 out_unlock:
-       unlock_kernel();
 
        if (inode) {
                dentry->d_op = &proc_dentry_operations;
@@ -407,7 +452,7 @@ out_unlock:
                return NULL;
        }
        if (de)
-               de_put(de);
+               pde_put(de);
        return ERR_PTR(error);
 }
 
@@ -434,8 +479,6 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
        struct inode *inode = filp->f_path.dentry->d_inode;
        int ret = 0;
 
-       lock_kernel();
-
        ino = inode->i_ino;
        i = filp->f_pos;
        switch (i) {
@@ -473,23 +516,23 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,
                                struct proc_dir_entry *next;
 
                                /* filldir passes info to user space */
-                               de_get(de);
+                               pde_get(de);
                                spin_unlock(&proc_subdir_lock);
                                if (filldir(dirent, de->name, de->namelen, filp->f_pos,
                                            de->low_ino, de->mode >> 12) < 0) {
-                                       de_put(de);
+                                       pde_put(de);
                                        goto out;
                                }
                                spin_lock(&proc_subdir_lock);
                                filp->f_pos++;
                                next = de->next;
-                               de_put(de);
+                               pde_put(de);
                                de = next;
                        } while (de);
                        spin_unlock(&proc_subdir_lock);
        }
        ret = 1;
-out:   unlock_kernel();
+out:
        return ret;     
 }
 
@@ -506,6 +549,7 @@ int proc_readdir(struct file *filp, void *dirent, filldir_t filldir)
  * the /proc directory.
  */
 static const struct file_operations proc_dir_operations = {
+       .llseek                 = generic_file_llseek,
        .read                   = generic_read_dir,
        .readdir                = proc_readdir,
 };
@@ -549,9 +593,8 @@ 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) {
-                       printk(KERN_WARNING "proc_dir_entry '%s' already "
-                                       "registered\n", dp->name);
-                       dump_stack();
+                       WARN(1, KERN_WARNING "proc_dir_entry '%s/%s' already registered\n",
+                               dir->name, dp->name);
                        break;
                }
 
@@ -597,6 +640,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
        ent->pde_users = 0;
        spin_lock_init(&ent->pde_unload_lock);
        ent->pde_unload_completion = NULL;
+       INIT_LIST_HEAD(&ent->pde_openers);
  out:
        return ent;
 }
@@ -625,6 +669,7 @@ struct proc_dir_entry *proc_symlink(const char *name,
        }
        return ent;
 }
+EXPORT_SYMBOL(proc_symlink);
 
 struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
                struct proc_dir_entry *parent)
@@ -641,11 +686,29 @@ struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
        return ent;
 }
 
+struct proc_dir_entry *proc_net_mkdir(struct net *net, const char *name,
+               struct proc_dir_entry *parent)
+{
+       struct proc_dir_entry *ent;
+
+       ent = __proc_create(&parent, name, S_IFDIR | S_IRUGO | S_IXUGO, 2);
+       if (ent) {
+               ent->data = net;
+               if (proc_register(parent, ent) < 0) {
+                       kfree(ent);
+                       ent = NULL;
+               }
+       }
+       return ent;
+}
+EXPORT_SYMBOL_GPL(proc_net_mkdir);
+
 struct proc_dir_entry *proc_mkdir(const char *name,
                struct proc_dir_entry *parent)
 {
        return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
 }
+EXPORT_SYMBOL(proc_mkdir);
 
 struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
                                         struct proc_dir_entry *parent)
@@ -674,10 +737,12 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
        }
        return ent;
 }
+EXPORT_SYMBOL(create_proc_entry);
 
-struct proc_dir_entry *proc_create(const char *name, mode_t mode,
-                                  struct proc_dir_entry *parent,
-                                  const struct file_operations *proc_fops)
+struct proc_dir_entry *proc_create_data(const char *name, mode_t mode,
+                                       struct proc_dir_entry *parent,
+                                       const struct file_operations *proc_fops,
+                                       void *data)
 {
        struct proc_dir_entry *pde;
        nlink_t nlink;
@@ -698,6 +763,7 @@ struct proc_dir_entry *proc_create(const char *name, mode_t mode,
        if (!pde)
                goto out;
        pde->proc_fops = proc_fops;
+       pde->data = data;
        if (proc_register(parent, pde) < 0)
                goto out_free;
        return pde;
@@ -706,8 +772,9 @@ out_free:
 out:
        return NULL;
 }
+EXPORT_SYMBOL(proc_create_data);
 
-void free_proc_entry(struct proc_dir_entry *de)
+static void free_proc_entry(struct proc_dir_entry *de)
 {
        unsigned int ino = de->low_ino;
 
@@ -721,6 +788,12 @@ void free_proc_entry(struct proc_dir_entry *de)
        kfree(de);
 }
 
+void pde_put(struct proc_dir_entry *pde)
+{
+       if (atomic_dec_and_test(&pde->count))
+               free_proc_entry(pde);
+}
+
 /*
  * Remove a /proc entry and free it if it's not currently in use.
  */
@@ -731,11 +804,13 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
        const char *fn = name;
        int len;
 
-       if (xlate_proc_name(name, &parent, &fn) != 0)
+       spin_lock(&proc_subdir_lock);
+       if (__xlate_proc_name(name, &parent, &fn) != 0) {
+               spin_unlock(&proc_subdir_lock);
                return;
+       }
        len = strlen(fn);
 
-       spin_lock(&proc_subdir_lock);
        for (p = &parent->subdir; *p; p=&(*p)->next ) {
                if (proc_match(len, fn, *p)) {
                        de = *p;
@@ -745,8 +820,10 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
                }
        }
        spin_unlock(&proc_subdir_lock);
-       if (!de)
+       if (!de) {
+               WARN(1, "name '%s'\n", name);
                return;
+       }
 
        spin_lock(&de->pde_unload_lock);
        /*
@@ -770,15 +847,25 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
        spin_unlock(&de->pde_unload_lock);
 
 continue_removing:
+       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);
+
        if (S_ISDIR(de->mode))
                parent->nlink--;
        de->nlink = 0;
-       if (de->subdir) {
-               printk(KERN_WARNING "%s: removing non-empty directory "
+       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);
-               WARN_ON(1);
-       }
-       if (atomic_dec_and_test(&de->count))
-               free_proc_entry(de);
+       pde_put(de);
 }
+EXPORT_SYMBOL(remove_proc_entry);