Merge branch 'work.lookup' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[muen/linux.git] / fs / proc / generic.c
index b77034a694ef4c267b336da31857c1a7e12916fb..7b4d9714f2485a5c53397bf9dc198419b8b30464 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/uaccess.h>
+#include <linux/seq_file.h>
 
 #include "internal.h"
 
@@ -345,13 +346,12 @@ static const struct inode_operations proc_dir_inode_operations = {
        .setattr        = proc_notify_change,
 };
 
-static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
+/* returns the registered entry, or frees dp and returns NULL on failure */
+struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
+               struct proc_dir_entry *dp)
 {
-       int ret;
-
-       ret = proc_alloc_inum(&dp->low_ino);
-       if (ret)
-               return ret;
+       if (proc_alloc_inum(&dp->low_ino))
+               goto out_free_entry;
 
        write_lock(&proc_subdir_lock);
        dp->parent = dir;
@@ -359,12 +359,16 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
                WARN(1, "proc_dir_entry '%s/%s' already registered\n",
                     dir->name, dp->name);
                write_unlock(&proc_subdir_lock);
-               proc_free_inum(dp->low_ino);
-               return -EEXIST;
+               goto out_free_inum;
        }
        write_unlock(&proc_subdir_lock);
 
-       return 0;
+       return dp;
+out_free_inum:
+       proc_free_inum(dp->low_ino);
+out_free_entry:
+       pde_free(dp);
+       return NULL;
 }
 
 static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
@@ -442,10 +446,7 @@ struct proc_dir_entry *proc_symlink(const char *name,
                if (ent->data) {
                        strcpy((char*)ent->data,dest);
                        ent->proc_iops = &proc_link_inode_operations;
-                       if (proc_register(parent, ent) < 0) {
-                               pde_free(ent);
-                               ent = NULL;
-                       }
+                       ent = proc_register(parent, ent);
                } else {
                        pde_free(ent);
                        ent = NULL;
@@ -469,11 +470,9 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
                ent->proc_fops = &proc_dir_operations;
                ent->proc_iops = &proc_dir_inode_operations;
                parent->nlink++;
-               if (proc_register(parent, ent) < 0) {
-                       pde_free(ent);
+               ent = proc_register(parent, ent);
+               if (!ent)
                        parent->nlink--;
-                       ent = NULL;
-               }
        }
        return ent;
 }
@@ -504,47 +503,47 @@ struct proc_dir_entry *proc_create_mount_point(const char *name)
                ent->proc_fops = NULL;
                ent->proc_iops = NULL;
                parent->nlink++;
-               if (proc_register(parent, ent) < 0) {
-                       pde_free(ent);
+               ent = proc_register(parent, ent);
+               if (!ent)
                        parent->nlink--;
-                       ent = NULL;
-               }
        }
        return ent;
 }
 EXPORT_SYMBOL(proc_create_mount_point);
 
-struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
-                                       struct proc_dir_entry *parent,
-                                       const struct file_operations *proc_fops,
-                                       void *data)
+struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
+               struct proc_dir_entry **parent, void *data)
 {
-       struct proc_dir_entry *pde;
+       struct proc_dir_entry *p;
+
        if ((mode & S_IFMT) == 0)
                mode |= S_IFREG;
-
-       if (!S_ISREG(mode)) {
-               WARN_ON(1);     /* use proc_mkdir() */
+       if ((mode & S_IALLUGO) == 0)
+               mode |= S_IRUGO;
+       if (WARN_ON_ONCE(!S_ISREG(mode)))
                return NULL;
+
+       p = __proc_create(parent, name, mode, 1);
+       if (p) {
+               p->proc_iops = &proc_file_inode_operations;
+               p->data = data;
        }
+       return p;
+}
+
+struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
+               struct proc_dir_entry *parent,
+               const struct file_operations *proc_fops, void *data)
+{
+       struct proc_dir_entry *p;
 
        BUG_ON(proc_fops == NULL);
 
-       if ((mode & S_IALLUGO) == 0)
-               mode |= S_IRUGO;
-       pde = __proc_create(&parent, name, mode, 1);
-       if (!pde)
-               goto out;
-       pde->proc_fops = proc_fops;
-       pde->data = data;
-       pde->proc_iops = &proc_file_inode_operations;
-       if (proc_register(parent, pde) < 0)
-               goto out_free;
-       return pde;
-out_free:
-       pde_free(pde);
-out:
-       return NULL;
+       p = proc_create_reg(name, mode, &parent, data);
+       if (!p)
+               return NULL;
+       p->proc_fops = proc_fops;
+       return proc_register(parent, p);
 }
 EXPORT_SYMBOL(proc_create_data);
  
@@ -556,6 +555,67 @@ struct proc_dir_entry *proc_create(const char *name, umode_t mode,
 }
 EXPORT_SYMBOL(proc_create);
 
+static int proc_seq_open(struct inode *inode, struct file *file)
+{
+       struct proc_dir_entry *de = PDE(inode);
+
+       if (de->state_size)
+               return seq_open_private(file, de->seq_ops, de->state_size);
+       return seq_open(file, de->seq_ops);
+}
+
+static const struct file_operations proc_seq_fops = {
+       .open           = proc_seq_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
+               struct proc_dir_entry *parent, const struct seq_operations *ops,
+               unsigned int state_size, void *data)
+{
+       struct proc_dir_entry *p;
+
+       p = proc_create_reg(name, mode, &parent, data);
+       if (!p)
+               return NULL;
+       p->proc_fops = &proc_seq_fops;
+       p->seq_ops = ops;
+       p->state_size = state_size;
+       return proc_register(parent, p);
+}
+EXPORT_SYMBOL(proc_create_seq_private);
+
+static int proc_single_open(struct inode *inode, struct file *file)
+{
+       struct proc_dir_entry *de = PDE(inode);
+
+       return single_open(file, de->single_show, de->data);
+}
+
+static const struct file_operations proc_single_fops = {
+       .open           = proc_single_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
+               struct proc_dir_entry *parent,
+               int (*show)(struct seq_file *, void *), void *data)
+{
+       struct proc_dir_entry *p;
+
+       p = proc_create_reg(name, mode, &parent, data);
+       if (!p)
+               return NULL;
+       p->proc_fops = &proc_single_fops;
+       p->single_show = show;
+       return proc_register(parent, p);
+}
+EXPORT_SYMBOL(proc_create_single_data);
+
 void proc_set_size(struct proc_dir_entry *de, loff_t size)
 {
        de->size = size;