[CVE-2009-0029] System call wrappers part 02
[safe/jmp/linux-2.6] / fs / sysfs / bin.c
index 3c5574a..66f6e58 100644 (file)
@@ -1,9 +1,15 @@
 /*
- * bin.c - binary file operations for sysfs.
+ * fs/sysfs/bin.c - sysfs binary file implementation
  *
  * Copyright (c) 2003 Patrick Mochel
  * Copyright (c) 2003 Matthew Wilcox
  * Copyright (c) 2004 Silicon Graphics, Inc.
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Please see Documentation/filesystems/sysfs.txt for more information.
  */
 
 #undef DEBUG
@@ -14,9 +20,9 @@
 #include <linux/kobject.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
 
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
 
 #include "sysfs.h"
 
@@ -30,8 +36,8 @@ static int
 fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
 {
        struct sysfs_dirent *attr_sd = dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
-       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
+       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
+       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
        int rc;
 
        /* need attr_sd for attr, its parent for kobj */
@@ -40,7 +46,7 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
 
        rc = -EIO;
        if (attr->read)
-               rc = attr->read(kobj, buffer, off, count);
+               rc = attr->read(kobj, attr, buffer, off, count);
 
        sysfs_put_active_two(attr_sd);
 
@@ -55,6 +61,7 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
        int size = dentry->d_inode->i_size;
        loff_t offs = *off;
        int count = min_t(size_t, bytes, PAGE_SIZE);
+       char *temp;
 
        if (size) {
                if (offs > size)
@@ -63,23 +70,33 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
                        count = size - offs;
        }
 
+       temp = kmalloc(count, GFP_KERNEL);
+       if (!temp)
+               return -ENOMEM;
+
        mutex_lock(&bb->mutex);
 
        count = fill_read(dentry, bb->buffer, offs, count);
-       if (count < 0)
-               goto out_unlock;
+       if (count < 0) {
+               mutex_unlock(&bb->mutex);
+               goto out_free;
+       }
+
+       memcpy(temp, bb->buffer, count);
 
-       if (copy_to_user(userbuf, bb->buffer, count)) {
+       mutex_unlock(&bb->mutex);
+
+       if (copy_to_user(userbuf, temp, count)) {
                count = -EFAULT;
-               goto out_unlock;
+               goto out_free;
        }
 
        pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
 
        *off = offs + count;
 
- out_unlock:
-       mutex_unlock(&bb->mutex);
+ out_free:
+       kfree(temp);
        return count;
 }
 
@@ -87,8 +104,8 @@ static int
 flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
 {
        struct sysfs_dirent *attr_sd = dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
-       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
+       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
+       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
        int rc;
 
        /* need attr_sd for attr, its parent for kobj */
@@ -97,7 +114,7 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
 
        rc = -EIO;
        if (attr->write)
-               rc = attr->write(kobj, buffer, offset, count);
+               rc = attr->write(kobj, attr, buffer, offset, count);
 
        sysfs_put_active_two(attr_sd);
 
@@ -112,6 +129,7 @@ static ssize_t write(struct file *file, const char __user *userbuf,
        int size = dentry->d_inode->i_size;
        loff_t offs = *off;
        int count = min_t(size_t, bytes, PAGE_SIZE);
+       char *temp;
 
        if (size) {
                if (offs > size)
@@ -120,19 +138,27 @@ static ssize_t write(struct file *file, const char __user *userbuf,
                        count = size - offs;
        }
 
-       mutex_lock(&bb->mutex);
+       temp = kmalloc(count, GFP_KERNEL);
+       if (!temp)
+               return -ENOMEM;
 
-       if (copy_from_user(bb->buffer, userbuf, count)) {
+       if (copy_from_user(temp, userbuf, count)) {
                count = -EFAULT;
-               goto out_unlock;
+               goto out_free;
        }
 
+       mutex_lock(&bb->mutex);
+
+       memcpy(bb->buffer, temp, count);
+
        count = flush_write(dentry, bb->buffer, offs, count);
+       mutex_unlock(&bb->mutex);
+
        if (count > 0)
                *off = offs + count;
 
- out_unlock:
-       mutex_unlock(&bb->mutex);
+out_free:
+       kfree(temp);
        return count;
 }
 
@@ -140,8 +166,8 @@ static int mmap(struct file *file, struct vm_area_struct *vma)
 {
        struct bin_buffer *bb = file->private_data;
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
-       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
+       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
+       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
        int rc;
 
        mutex_lock(&bb->mutex);
@@ -167,12 +193,12 @@ static int mmap(struct file *file, struct vm_area_struct *vma)
 static int open(struct inode * inode, struct file * file)
 {
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
-       struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
+       struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
        struct bin_buffer *bb = NULL;
        int error;
 
-       /* need attr_sd for attr */
-       if (!sysfs_get_active(attr_sd))
+       /* binary file operations requires both @sd and its parent */
+       if (!sysfs_get_active_two(attr_sd))
                return -ENODEV;
 
        error = -EACCES;
@@ -193,13 +219,12 @@ static int open(struct inode * inode, struct file * file)
        mutex_init(&bb->mutex);
        file->private_data = bb;
 
-       /* open succeeded, put active reference and pin attr_sd */
-       sysfs_put_active(attr_sd);
-       sysfs_get(attr_sd);
+       /* open succeeded, put active references */
+       sysfs_put_active_two(attr_sd);
        return 0;
 
  err_out:
-       sysfs_put_active(attr_sd);
+       sysfs_put_active_two(attr_sd);
        kfree(bb);
        return error;
 }
@@ -211,7 +236,6 @@ static int release(struct inode * inode, struct file * file)
 
        if (bb->mmapped)
                sysfs_put_active_two(attr_sd);
-       sysfs_put(attr_sd);
        kfree(bb->buffer);
        kfree(bb);
        return 0;
@@ -234,9 +258,9 @@ const struct file_operations bin_fops = {
 
 int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 {
-       BUG_ON(!kobj || !kobj->dentry || !attr);
+       BUG_ON(!kobj || !kobj->sd || !attr);
 
-       return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
+       return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
 }
 
 
@@ -248,12 +272,7 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 
 void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 {
-       if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
-               printk(KERN_ERR "%s: "
-                       "bad dentry or inode or no such file: \"%s\"\n",
-                       __FUNCTION__, attr->attr.name);
-               dump_stack();
-       }
+       sysfs_hash_and_remove(kobj->sd, attr->attr.name);
 }
 
 EXPORT_SYMBOL_GPL(sysfs_create_bin_file);