lkml.org 
[lkml]   [2007]   [Jan]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 58/59] sysctl: Reimplement the sysctl proc support
    Date
    From: Eric W. Biederman <ebiederm@xmission.com> - unquoted

    With this change the sysctl inodes can be cached and
    nothing needs to be done when removing a sysctl table.

    For a costk of 2K code we will save about 4K of static tables
    (when we remove de from ctl_table) and 70K in proc_dir_entries
    that we will not allocate, or about half that on a 32bit arch.

    The speed feels about the same, even though we can now cache
    the sysctl dentries :(

    We get the core advantage that we don't need to have a
    1 to 1 mapping between ctl table entries and proc files.
    Making it possible to have /proc/sys vary depending on
    the namespace you are in. The currently merged namespaces
    don't have an issue here but the network namespace under
    /proc/sys/net needs to have different directories depending
    on which network adapters are visible. By simply being
    a cache different directories being visible depending
    on who you are is trivial to implement.

    Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
    ---
    fs/proc/Makefile | 2 +-
    fs/proc/inode.c | 1 +
    fs/proc/internal.h | 2 +
    fs/proc/proc_sysctl.c | 477 +++++++++++++++++++++++++++++++++++++++++++++++++
    fs/proc/root.c | 10 +-
    init/main.c | 4 -
    kernel/sysctl.c | 182 -------------------
    7 files changed, 484 insertions(+), 194 deletions(-)

    diff --git a/fs/proc/Makefile b/fs/proc/Makefile
    index f6c7762..a6b3a8f 100644
    --- a/fs/proc/Makefile
    +++ b/fs/proc/Makefile
    @@ -8,7 +8,7 @@ proc-y := nommu.o task_nommu.o
    proc-$(CONFIG_MMU) := mmu.o task_mmu.o

    proc-y += inode.o root.o base.o generic.o array.o \
    - proc_tty.o proc_misc.o
    + proc_tty.o proc_misc.o proc_sysctl.o

    proc-$(CONFIG_PROC_KCORE) += kcore.o
    proc-$(CONFIG_PROC_VMCORE) += vmcore.o
    diff --git a/fs/proc/inode.c b/fs/proc/inode.c
    index e26945b..0ea8265 100644
    --- a/fs/proc/inode.c
    +++ b/fs/proc/inode.c
    @@ -161,6 +161,7 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino,
    if (!inode)
    goto out_ino;

    + PROC_I(inode)->fd = 0;
    PROC_I(inode)->pde = de;
    if (de) {
    if (de->mode) {
    diff --git a/fs/proc/internal.h b/fs/proc/internal.h
    index 987c773..3c9a305 100644
    --- a/fs/proc/internal.h
    +++ b/fs/proc/internal.h
    @@ -11,6 +11,8 @@

    #include <linux/proc_fs.h>

    +extern int proc_sys_init(void);
    +
    struct vmalloc_info {
    unsigned long used;
    unsigned long largest_chunk;
    diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
    new file mode 100644
    index 0000000..08a2e66
    --- /dev/null
    +++ b/fs/proc/proc_sysctl.c
    @@ -0,0 +1,477 @@
    +/*
    + * /proc/sys support
    + */
    +
    +#include <linux/sysctl.h>
    +#include <linux/proc_fs.h>
    +#include <linux/security.h>
    +#include "internal.h"
    +
    +static struct dentry_operations proc_sys_dentry_operations;
    +static const struct file_operations proc_sys_file_operations;
    +static struct inode_operations proc_sys_inode_operations;
    +
    +static void proc_sys_refresh_inode(struct inode *inode, struct ctl_table *table)
    +{
    + /* Refresh the cached information bits in the inode */
    + if (table) {
    + inode->i_uid = 0;
    + inode->i_gid = 0;
    + inode->i_mode = table->mode;
    + if (table->proc_handler) {
    + inode->i_mode |= S_IFREG;
    + inode->i_nlink = 1;
    + } else {
    + inode->i_mode |= S_IFDIR;
    + inode->i_nlink = 0; /* It is too hard to figure out */
    + }
    + }
    +}
    +
    +static struct inode *proc_sys_make_inode(struct inode *dir, struct ctl_table *table)
    +{
    + struct inode *inode;
    + struct proc_inode *dir_ei, *ei;
    + int depth;
    +
    + inode = new_inode(dir->i_sb);
    + if (!inode)
    + goto out;
    +
    + /* A directory is always one deeper than it's parent */
    + dir_ei = PROC_I(dir);
    + depth = dir_ei->fd + 1;
    +
    + ei = PROC_I(inode);
    + ei->fd = depth;
    + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
    + inode->i_op = &proc_sys_inode_operations;
    + inode->i_fop = &proc_sys_file_operations;
    + proc_sys_refresh_inode(inode, table);
    +out:
    + return inode;
    +}
    +
    +static struct dentry *proc_sys_ancestor(struct dentry *dentry, int depth)
    +{
    + for (;;) {
    + struct proc_inode *ei;
    +
    + ei = PROC_I(dentry->d_inode);
    + if (ei->fd == depth)
    + break; /* found */
    +
    + dentry = dentry->d_parent;
    + }
    + return dentry;
    +}
    +
    +static struct ctl_table *proc_sys_lookup_table_one(struct ctl_table *table,
    + struct qstr *name)
    +{
    + int len;
    + for ( ; table->ctl_name || table->procname; table++) {
    +
    + if (!table->procname)
    + continue;
    +
    + len = strlen(table->procname);
    + if (len != name->len)
    + continue;
    +
    + if (memcmp(table->procname, name->name, len) != 0)
    + continue;
    +
    + /* I have a match */
    + return table;
    + }
    + return NULL;
    +}
    +
    +static struct ctl_table *proc_sys_lookup_table(struct dentry *dentry,
    + struct ctl_table *table)
    +{
    + struct dentry *ancestor;
    + struct proc_inode *ei;
    + int depth, i;
    +
    + ei = PROC_I(dentry->d_inode);
    + depth = ei->fd;
    +
    + if (depth == 0)
    + return table;
    +
    + for (i = 1; table && (i <= depth); i++) {
    + ancestor = proc_sys_ancestor(dentry, i);
    + table = proc_sys_lookup_table_one(table, &ancestor->d_name);
    + if (table)
    + table = table->child;
    + }
    + return table;
    +
    +}
    +static struct ctl_table *proc_sys_lookup_entry(struct dentry *dparent,
    + struct qstr *name,
    + struct ctl_table *table)
    +{
    + table = proc_sys_lookup_table(dparent, table);
    + if (table)
    + table = proc_sys_lookup_table_one(table, name);
    + return table;
    +}
    +
    +static struct ctl_table *do_proc_sys_lookup(struct dentry *parent,
    + struct qstr *name,
    + struct ctl_table_header **ptr)
    +{
    + struct ctl_table_header *head;
    + struct ctl_table *table;
    +
    + for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
    + table = proc_sys_lookup_entry(parent, name, head->ctl_table);
    + if (table)
    + break;
    + }
    + *ptr = head;
    + return table;
    +}
    +
    +static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
    + struct nameidata *nd)
    +{
    + struct ctl_table_header *head;
    + struct inode *inode;
    + struct dentry *err;
    + struct ctl_table *table;
    +
    + err = ERR_PTR(-ENOENT);
    + table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
    + if (!table)
    + goto out;
    +
    + err = ERR_PTR(-ENOMEM);
    + inode = proc_sys_make_inode(dir, table);
    + if (!inode)
    + goto out;
    +
    + err = NULL;
    + dentry->d_op = &proc_sys_dentry_operations;
    + d_add(dentry, inode);
    +
    +out:
    + sysctl_head_finish(head);
    + return err;
    +}
    +
    +static ssize_t proc_sys_read(struct file *filp, char __user *buf,
    + size_t count, loff_t *ppos)
    +{
    + struct dentry *dentry = filp->f_dentry;
    + struct ctl_table_header *head;
    + struct ctl_table *table;
    + ssize_t error, res;
    +
    + table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
    + /* Has the sysctl entry disappeared on us? */
    + error = -ENOENT;
    + if (!table)
    + goto out;
    +
    + /* Has the sysctl entry been replaced by a directory? */
    + error = -EISDIR;
    + if (!table->proc_handler)
    + goto out;
    +
    + /*
    + * At this point we know that the sysctl was not unregistered
    + * and won't be until we finish.
    + */
    + error = -EPERM;
    + if (sysctl_perm(table, MAY_READ))
    + goto out;
    +
    + /* careful: calling conventions are nasty here */
    + res = count;
    + error = table->proc_handler(table, 0, filp, buf, &res, ppos);
    + if (!error)
    + error = res;
    +out:
    + sysctl_head_finish(head);
    +
    + return error;
    +}
    +
    +static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
    + size_t count, loff_t *ppos)
    +{
    + struct dentry *dentry = filp->f_dentry;
    + struct ctl_table_header *head;
    + struct ctl_table *table;
    + ssize_t error, res;
    +
    + table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
    + /* Has the sysctl entry disappeared on us? */
    + error = -ENOENT;
    + if (!table)
    + goto out;
    +
    + /* Has the sysctl entry been replaced by a directory? */
    + error = -EISDIR;
    + if (!table->proc_handler)
    + goto out;
    +
    + /*
    + * At this point we know that the sysctl was not unregistered
    + * and won't be until we finish.
    + */
    + error = -EPERM;
    + if (sysctl_perm(table, MAY_WRITE))
    + goto out;
    +
    + /* careful: calling conventions are nasty here */
    + res = count;
    + error = table->proc_handler(table, 1, filp, buf, &res, ppos);
    + if (!error)
    + error = res;
    +out:
    + sysctl_head_finish(head);
    +
    + return error;
    +}
    +
    +
    +static int proc_sys_fill_cache(struct file *filp, void *dirent,
    + filldir_t filldir, struct ctl_table *table)
    +{
    + struct ctl_table_header *head;
    + struct ctl_table *child_table = NULL;
    + struct dentry *child, *dir = filp->f_path.dentry;
    + struct inode *inode;
    + struct qstr qname;
    + ino_t ino = 0;
    + unsigned type = DT_UNKNOWN;
    + int ret;
    +
    + qname.name = table->procname;
    + qname.len = strlen(table->procname);
    + qname.hash = full_name_hash(qname.name, qname.len);
    +
    + /* Suppress duplicates.
    + * Only fill a directory entry if it is the value that
    + * an ordinary lookup of that name returns. Hide all
    + * others.
    + *
    + * If we ever cache this translation in the dcache
    + * I should do a dcache lookup first. But for now
    + * it is just simpler not to.
    + */
    + ret = 0;
    + child_table = do_proc_sys_lookup(dir, &qname, &head);
    + sysctl_head_finish(head);
    + if (child_table != table)
    + return 0;
    +
    + child = d_lookup(dir, &qname);
    + if (!child) {
    + struct dentry *new;
    + new = d_alloc(dir, &qname);
    + if (new) {
    + inode = proc_sys_make_inode(dir->d_inode, table);
    + if (!inode)
    + child = ERR_PTR(-ENOMEM);
    + else {
    + new->d_op = &proc_sys_dentry_operations;
    + d_add(new, inode);
    + }
    + if (child)
    + dput(new);
    + else
    + child = new;
    + }
    + }
    + if (!child || IS_ERR(child) || !child->d_inode)
    + goto end_instantiate;
    + inode = child->d_inode;
    + if (inode) {
    + ino = inode->i_ino;
    + type = inode->i_mode >> 12;
    + }
    + dput(child);
    +end_instantiate:
    + if (!ino)
    + ino= find_inode_number(dir, &qname);
    + if (!ino)
    + ino = 1;
    + return filldir(dirent, qname.name, qname.len, filp->f_pos, ino, type);
    +}
    +
    +static int proc_sys_readdir(struct file *filp, void *dirent, filldir_t filldir)
    +{
    + struct dentry *dentry = filp->f_dentry;
    + struct inode *inode = dentry->d_inode;
    + struct ctl_table_header *head = NULL;
    + struct ctl_table *table;
    + unsigned long pos;
    + int ret;
    +
    + ret = -ENOTDIR;
    + if (!S_ISDIR(inode->i_mode))
    + goto out;
    +
    + ret = 0;
    + switch(filp->f_pos) {
    + case 0:
    + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
    + goto out;
    + filp->f_pos++;
    + /* fall through */
    + case 1:
    + if (filldir(dirent, "..", 2, filp->f_pos, parent_ino(dentry), DT_DIR) < 0)
    + goto out;
    + filp->f_pos++;
    + /* fall through */
    + default:
    + pos = 2;
    + break;
    + }
    +
    + /* - Find each instance of the directory
    + * - Read all entries in each instance
    + * - Before returning an entry to user space lookup the entry
    + * by name and if I find a different entry don't return
    + * this one because it means it is a buried dup.
    + * For sysctl this should only happen for directory entries.
    + */
    + for (head = sysctl_head_next(NULL); head; head = sysctl_head_next(head)) {
    + table = proc_sys_lookup_table(dentry, head->ctl_table);
    +
    + if (!table)
    + continue;
    +
    + for (; table->ctl_name || table->procname; table++, pos++) {
    + /* Can't do anything without a proc name */
    + if (!table->procname)
    + continue;
    +
    + if (pos < filp->f_pos)
    + continue;
    +
    + if (proc_sys_fill_cache(filp, dirent, filldir, table) < 0)
    + goto out;
    + filp->f_pos = pos + 1;
    + }
    + }
    + ret = 1;
    +out:
    + sysctl_head_finish(head);
    + return ret;
    +}
    +
    +static int proc_sys_permission(struct inode *inode, int mask, struct nameidata *nd)
    +{
    + /*
    + * sysctl entries that are not writeable,
    + * are _NOT_ writeable, capabilities or not.
    + */
    + struct ctl_table_header *head;
    + struct ctl_table *table;
    + struct dentry *dentry;
    + int mode;
    + int depth;
    + int error;
    +
    + head = NULL;
    + depth = PROC_I(inode)->fd;
    +
    + /* First check the cached permissions, in case we don't have
    + * enough information to lookup the sysctl table entry.
    + */
    + error = -EACCES;
    + mode = inode->i_mode;
    +
    + if (current->euid == 0)
    + mode >>= 6;
    + else if (in_group_p(0))
    + mode >>= 3;
    +
    + if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask)
    + error = 0;
    +
    + /* If we can't get a sysctl table entry the permission
    + * checks on the cached mode will have to be enough.
    + */
    + if (!nd || !depth)
    + goto out;
    +
    + dentry = nd->dentry;
    + table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
    +
    + /* If the entry does not exist deny permission */
    + error = -EACCES;
    + if (!table)
    + goto out;
    +
    + /* Use the permissions on the sysctl table entry */
    + error = sysctl_perm(table, mask);
    +out:
    + sysctl_head_finish(head);
    + return error;
    +}
    +
    +static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
    +{
    + struct inode *inode = dentry->d_inode;
    + int error;
    +
    + if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
    + return -EPERM;
    +
    + error = inode_change_ok(inode, attr);
    + if (!error) {
    + error = security_inode_setattr(dentry, attr);
    + if (!error)
    + error = inode_setattr(inode, attr);
    + }
    +
    + return error;
    +}
    +
    +/* I'm lazy and don't distinguish between files and directories,
    + * until access time.
    + */
    +static const struct file_operations proc_sys_file_operations = {
    + .read = proc_sys_read,
    + .write = proc_sys_write,
    + .readdir = proc_sys_readdir,
    +};
    +
    +static struct inode_operations proc_sys_inode_operations = {
    + .lookup = proc_sys_lookup,
    + .permission = proc_sys_permission,
    + .setattr = proc_sys_setattr,
    +};
    +
    +static int proc_sys_revalidate(struct dentry *dentry, struct nameidata *nd)
    +{
    + struct ctl_table_header *head;
    + struct ctl_table *table;
    + table = do_proc_sys_lookup(dentry->d_parent, &dentry->d_name, &head);
    + proc_sys_refresh_inode(dentry->d_inode, table);
    + sysctl_head_finish(head);
    + return !!table;
    +}
    +
    +static struct dentry_operations proc_sys_dentry_operations = {
    + .d_revalidate = proc_sys_revalidate,
    +};
    +
    +struct proc_dir_entry *proc_sys_root;
    +
    +int proc_sys_init(void)
    +{
    + proc_sys_root = proc_mkdir("sys", NULL);
    + proc_sys_root->proc_iops = &proc_sys_inode_operations;
    + proc_sys_root->proc_fops = &proc_sys_file_operations;
    + proc_sys_root->nlink = 0;
    + return 0;
    +}
    diff --git a/fs/proc/root.c b/fs/proc/root.c
    index 8059e92..4d42406 100644
    --- a/fs/proc/root.c
    +++ b/fs/proc/root.c
    @@ -23,10 +23,6 @@

    struct proc_dir_entry *proc_net, *proc_net_stat, *proc_bus, *proc_root_fs, *proc_root_driver;

    -#ifdef CONFIG_SYSCTL
    -struct proc_dir_entry *proc_sys_root;
    -#endif
    -
    static int proc_get_sb(struct file_system_type *fs_type,
    int flags, const char *dev_name, void *data, struct vfsmount *mnt)
    {
    @@ -71,9 +67,6 @@ void __init proc_root_init(void)
    #ifdef CONFIG_SYSVIPC
    proc_mkdir("sysvipc", NULL);
    #endif
    -#ifdef CONFIG_SYSCTL
    - proc_sys_root = proc_mkdir("sys", NULL);
    -#endif
    proc_root_fs = proc_mkdir("fs", NULL);
    proc_root_driver = proc_mkdir("driver", NULL);
    proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
    @@ -86,6 +79,9 @@ void __init proc_root_init(void)
    proc_device_tree_init();
    #endif
    proc_bus = proc_mkdir("bus", NULL);
    +#ifdef CONFIG_SYSCTL
    + proc_sys_init();
    +#endif
    }

    static int proc_root_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat
    diff --git a/init/main.c b/init/main.c
    index 8af5c6e..7926e5d 100644
    --- a/init/main.c
    +++ b/init/main.c
    @@ -86,7 +86,6 @@ extern void init_IRQ(void);
    extern void fork_init(unsigned long);
    extern void mca_init(void);
    extern void sbus_init(void);
    -extern void sysctl_init(void);
    extern void signals_init(void);
    extern void pidhash_init(void);
    extern void pidmap_init(void);
    @@ -688,9 +687,6 @@ static void __init do_basic_setup(void)
    usermodehelper_init();
    driver_init();

    -#ifdef CONFIG_SYSCTL
    - sysctl_init();
    -#endif
    #ifdef CONFIG_PROC_FS
    init_irq_proc();
    #endif
    diff --git a/kernel/sysctl.c b/kernel/sysctl.c
    index ec5e4a1..4b45bdb 100644
    --- a/kernel/sysctl.c
    +++ b/kernel/sysctl.c
    @@ -159,26 +159,6 @@ int sysctl_legacy_va_layout;



    -/* /proc declarations: */
    -
    -#ifdef CONFIG_PROC_SYSCTL
    -
    -static ssize_t proc_readsys(struct file *, char __user *, size_t, loff_t *);
    -static ssize_t proc_writesys(struct file *, const char __user *, size_t, loff_t *);
    -static int proc_opensys(struct inode *, struct file *);
    -
    -const struct file_operations proc_sys_file_operations = {
    - .open = proc_opensys,
    - .read = proc_readsys,
    - .write = proc_writesys,
    -};
    -
    -extern struct proc_dir_entry *proc_sys_root;
    -
    -static void register_proc_table(ctl_table *, struct proc_dir_entry *, void *);
    -static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
    -#endif
    -
    /* The default sysctl tables: */

    static ctl_table root_table[] = {
    @@ -1102,13 +1082,6 @@ struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev)
    return NULL;
    }

    -void __init sysctl_init(void)
    -{
    -#ifdef CONFIG_PROC_SYSCTL
    - register_proc_table(root_table, proc_sys_root, &root_table_header);
    -#endif
    -}
    -
    #ifdef CONFIG_SYSCTL_SYSCALL
    int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp,
    void __user *newval, size_t newlen)
    @@ -1345,9 +1318,6 @@ struct ctl_table_header *register_sysctl_table(ctl_table * table)
    spin_lock(&sysctl_lock);
    list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
    spin_unlock(&sysctl_lock);
    -#ifdef CONFIG_PROC_SYSCTL
    - register_proc_table(table, proc_sys_root, tmp);
    -#endif
    return tmp;
    }

    @@ -1363,9 +1333,6 @@ void unregister_sysctl_table(struct ctl_table_header * header)
    might_sleep();
    spin_lock(&sysctl_lock);
    start_unregistering(header);
    -#ifdef CONFIG_PROC_SYSCTL
    - unregister_proc_table(header->ctl_table, proc_sys_root);
    -#endif
    spin_unlock(&sysctl_lock);
    kfree(header);
    }
    @@ -1389,155 +1356,6 @@ void unregister_sysctl_table(struct ctl_table_header * table)

    #ifdef CONFIG_PROC_SYSCTL

    -/* Scan the sysctl entries in table and add them all into /proc */
    -static void register_proc_table(ctl_table * table, struct proc_dir_entry *root, void *set)
    -{
    - struct proc_dir_entry *de;
    - int len;
    - mode_t mode;
    -
    - for (; table->ctl_name || table->procname; table++) {
    - /* Can't do anything without a proc name. */
    - if (!table->procname)
    - continue;
    - /* Maybe we can't do anything with it... */
    - if (!table->proc_handler && !table->child) {
    - printk(KERN_WARNING "SYSCTL: Can't register %s\n",
    - table->procname);
    - continue;
    - }
    -
    - len = strlen(table->procname);
    - mode = table->mode;
    -
    - de = NULL;
    - if (table->proc_handler)
    - mode |= S_IFREG;
    - else {
    - mode |= S_IFDIR;
    - for (de = root->subdir; de; de = de->next) {
    - if (proc_match(len, table->procname, de))
    - break;
    - }
    - /* If the subdir exists already, de is non-NULL */
    - }
    -
    - if (!de) {
    - de = create_proc_entry(table->procname, mode, root);
    - if (!de)
    - continue;
    - de->set = set;
    - de->data = (void *) table;
    - if (table->proc_handler)
    - de->proc_fops = &proc_sys_file_operations;
    - }
    - table->de = de;
    - if (de->mode & S_IFDIR)
    - register_proc_table(table->child, de, set);
    - }
    -}
    -
    -/*
    - * Unregister a /proc sysctl table and any subdirectories.
    - */
    -static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root)
    -{
    - struct proc_dir_entry *de;
    - for (; table->ctl_name || table->procname; table++) {
    - if (!(de = table->de))
    - continue;
    - if (de->mode & S_IFDIR) {
    - if (!table->child) {
    - printk (KERN_ALERT "Help - malformed sysctl tree on free\n");
    - continue;
    - }
    - unregister_proc_table(table->child, de);
    -
    - /* Don't unregister directories which still have entries.. */
    - if (de->subdir)
    - continue;
    - }
    -
    - /*
    - * In any case, mark the entry as goner; we'll keep it
    - * around if it's busy, but we'll know to do nothing with
    - * its fields. We are under sysctl_lock here.
    - */
    - de->data = NULL;
    -
    - /* Don't unregister proc entries that are still being used.. */
    - if (atomic_read(&de->count))
    - continue;
    -
    - table->de = NULL;
    - remove_proc_entry(table->procname, root);
    - }
    -}
    -
    -static ssize_t do_rw_proc(int write, struct file * file, char __user * buf,
    - size_t count, loff_t *ppos)
    -{
    - int op;
    - struct proc_dir_entry *de = PDE(file->f_path.dentry->d_inode);
    - struct ctl_table *table;
    - size_t res;
    - ssize_t error = -ENOTDIR;
    -
    - spin_lock(&sysctl_lock);
    - if (de && de->data && use_table(de->set)) {
    - /*
    - * at that point we know that sysctl was not unregistered
    - * and won't be until we finish
    - */
    - spin_unlock(&sysctl_lock);
    - table = (struct ctl_table *) de->data;
    - if (!table || !table->proc_handler)
    - goto out;
    - error = -EPERM;
    - op = (write ? 002 : 004);
    - if (sysctl_perm(table, op))
    - goto out;
    -
    - /* careful: calling conventions are nasty here */
    - res = count;
    - error = (*table->proc_handler)(table, write, file,
    - buf, &res, ppos);
    - if (!error)
    - error = res;
    - out:
    - spin_lock(&sysctl_lock);
    - unuse_table(de->set);
    - }
    - spin_unlock(&sysctl_lock);
    - return error;
    -}
    -
    -static int proc_opensys(struct inode *inode, struct file *file)
    -{
    - if (file->f_mode & FMODE_WRITE) {
    - /*
    - * sysctl entries that are not writable,
    - * are _NOT_ writable, capabilities or not.
    - */
    - if (!(inode->i_mode & S_IWUSR))
    - return -EPERM;
    - }
    -
    - return 0;
    -}
    -
    -static ssize_t proc_readsys(struct file * file, char __user * buf,
    - size_t count, loff_t *ppos)
    -{
    - return do_rw_proc(0, file, buf, count, ppos);
    -}
    -
    -static ssize_t proc_writesys(struct file * file, const char __user * buf,
    - size_t count, loff_t *ppos)
    -{
    - return do_rw_proc(1, file, (char __user *) buf, count, ppos);
    -}
    -
    static int _proc_do_string(void* data, int maxlen, int write,
    struct file *filp, void __user *buffer,
    size_t *lenp, loff_t *ppos)
    --
    1.4.4.1.g278f
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2007-01-16 19:09    [W:0.060 / U:60.884 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site