Implement ->proc_write_buf() for /proc/scsi/scsi, /proc/scsi/device_info
and the host files so procfs copies in what is written before it pins
the entry, and parse what was written from that copy. /proc/scsi/scsi
and device_info take up to PAGE_SIZE bytes like before. The host files
pass the copy to ->write_info(), writes to them that are larger than
PAGE_SIZE - 1 bytes fail with -EINVAL from procfs instead of -EOVERFLOW.

Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
 drivers/scsi/scsi_devinfo.c | 29 +++++-----------------------
 drivers/scsi/scsi_proc.c    | 47 +++++++++++----------------------------------
 2 files changed, 16 insertions(+), 60 deletions(-)

diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 0b8f5ff40543..d9093851b23e 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -678,39 +678,20 @@ static const struct seq_operations scsi_devinfo_seq_ops = 
{
  * To use, echo "vendor:model:flag" > /proc/scsi/device_info
  */
 static ssize_t proc_scsi_devinfo_write(struct file *file,
-                                      const char __user *buf,
+                                      char *buffer,
                                       size_t length, loff_t *ppos)
 {
-       char *buffer;
-       ssize_t err = length;
-
-       if (!buf || length>PAGE_SIZE)
+       if (length == PAGE_SIZE && buffer[PAGE_SIZE-1])
                return -EINVAL;
-       buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!buffer)
-               return -ENOMEM;
-       if (copy_from_user(buffer, buf, length)) {
-               err =-EFAULT;
-               goto out;
-       }
-
-       if (length < PAGE_SIZE)
-               buffer[length] = '\0';
-       else if (buffer[PAGE_SIZE-1]) {
-               err = -EINVAL;
-               goto out;
-       }
 
        scsi_dev_info_list_add_str(buffer);
-
-out:
-       kfree(buffer);
-       return err;
+       return length;
 }
 
 static const struct proc_ops scsi_devinfo_proc_ops = {
        .proc_seq_ops   = &scsi_devinfo_seq_ops,
-       .proc_write     = proc_scsi_devinfo_write,
+       .proc_write_buf = proc_scsi_devinfo_write,
+       .proc_write_size = PAGE_SIZE,
 };
 #endif /* CONFIG_SCSI_PROC_FS */
 
diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c
index 59229bf9938e..5962e671cc83 100644
--- a/drivers/scsi/scsi_proc.c
+++ b/drivers/scsi/scsi_proc.c
@@ -62,30 +62,18 @@ struct scsi_proc_entry {
        unsigned int            present;
 };
 
-static ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
+static ssize_t proc_scsi_host_write(struct file *file, char *buf,
                            size_t count, loff_t *ppos)
 {
        struct Scsi_Host *shost = pde_data(file_inode(file));
-       ssize_t ret = -ENOMEM;
-       char *page;
-    
+
        if (count > PROC_BLOCK_SIZE)
                return -EOVERFLOW;
 
        if (!shost->hostt->write_info)
                return -EINVAL;
 
-       page = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (page) {
-               ret = -EFAULT;
-               if (copy_from_user(page, buf, count))
-                       goto out;
-               page[count] = '\0';
-               ret = shost->hostt->write_info(shost, page, count);
-       }
-out:
-       kfree(page);
-       return ret;
+       return shost->hostt->write_info(shost, buf, count);
 }
 
 static int proc_scsi_show(struct seq_file *m, void *v)
@@ -142,7 +130,7 @@ EXPORT_SYMBOL_GPL(scsi_template_proc_dir);
 static const struct proc_ops proc_scsi_ops = {
        .proc_show      = proc_scsi_show,
        .proc_open      = proc_scsi_host_open,
-       .proc_write     = proc_scsi_host_write
+       .proc_write_buf = proc_scsi_host_write
 };
 
 /**
@@ -386,8 +374,8 @@ static int scsi_remove_single_device(uint host, uint 
channel, uint id, uint lun)
 /**
  * proc_scsi_write - handle writes to /proc/scsi/scsi
  * @file: not used
- * @buf: buffer to write
- * @length: length of buf, at most PAGE_SIZE
+ * @buffer: buffer to write
+ * @length: length of buffer, at most PAGE_SIZE
  * @ppos: not used
  *
  * Description: this provides a legacy mechanism to add or remove devices by
@@ -402,32 +390,20 @@ static int scsi_remove_single_device(uint host, uint 
channel, uint id, uint lun)
  */
 
 
-static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
+static ssize_t proc_scsi_write(struct file *file, char *buffer,
                               size_t length, loff_t *ppos)
 {
        int host, channel, id, lun;
-       char *buffer, *end, *p;
+       char *end, *p;
        int err;
 
-       if (!buf || length > PAGE_SIZE)
-               return -EINVAL;
-
-       buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!buffer)
-               return -ENOMEM;
-
-       err = -EFAULT;
-       if (copy_from_user(buffer, buf, length))
-               goto out;
-
        err = -EINVAL;
        if (length < PAGE_SIZE) {
                end = buffer + length;
-               *end = '\0';
        } else {
                end = buffer + PAGE_SIZE - 1;
                if (*end)
-                       goto out;
+                       return err;
        }
 
        /*
@@ -466,8 +442,6 @@ static ssize_t proc_scsi_write(struct file *file, const 
char __user *buf,
        if (!err)
                err = length;
 
- out:
-       kfree(buffer);
        return err;
 }
 
@@ -521,7 +495,8 @@ static const struct seq_operations scsi_seq_ops = {
 
 static const struct proc_ops scsi_scsi_proc_ops = {
        .proc_seq_ops   = &scsi_seq_ops,
-       .proc_write     = proc_scsi_write,
+       .proc_write_buf = proc_scsi_write,
+       .proc_write_size = PAGE_SIZE,
 };
 
 /**

-- 
2.53.0


Reply via email to