From 2872a9b521ac936c7a8525a8c2bdfb9b4ccf5cfc Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 May 2012 00:13:37 -0700 Subject: [PATCH] Input: evdev - properly handle read/write with count 0 According to the standard count 0 is special - no IO should happen but we can check error conditions (device gone away, etc), and return 0 if there are no errors. We used to return -EINVAL instead and we also could return 0 if an event was "stolen" by another thread. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 69 ++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 9226b4d9118f..6c58bfff01a3 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -345,7 +345,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, struct input_event event; int retval = 0; - if (count < input_event_size()) + if (count != 0 && count < input_event_size()) return -EINVAL; retval = mutex_lock_interruptible(&evdev->mutex); @@ -357,7 +357,8 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, goto out; } - do { + while (retval + input_event_size() <= count) { + if (input_event_from_user(buffer + retval, &event)) { retval = -EFAULT; goto out; @@ -366,7 +367,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, input_inject_event(&evdev->handle, event.type, event.code, event.value); - } while (retval + input_event_size() <= count); + } out: mutex_unlock(&evdev->mutex); @@ -397,35 +398,49 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; - int retval = 0; + size_t read = 0; + int error; - if (count < input_event_size()) + if (count != 0 && count < input_event_size()) return -EINVAL; - if (!(file->f_flags & O_NONBLOCK)) { - retval = wait_event_interruptible(evdev->wait, - client->packet_head != client->tail || - !evdev->exist); - if (retval) - return retval; + for (;;) { + if (!evdev->exist) + return -ENODEV; + + if (client->packet_head == client->tail && + (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + /* + * count == 0 is special - no IO is done but we check + * for error conditions (see above). + */ + if (count == 0) + break; + + while (read + input_event_size() <= count && + evdev_fetch_next_event(client, &event)) { + + if (input_event_to_user(buffer + read, &event)) + return -EFAULT; + + read += input_event_size(); + } + + if (read) + break; + + if (!(file->f_flags & O_NONBLOCK)) { + error = wait_event_interruptible(evdev->wait, + client->packet_head != client->tail || + !evdev->exist); + if (error) + return error; + } } - if (!evdev->exist) - return -ENODEV; - - while (retval + input_event_size() <= count && - evdev_fetch_next_event(client, &event)) { - - if (input_event_to_user(buffer + retval, &event)) - return -EFAULT; - - retval += input_event_size(); - } - - if (retval == 0 && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; - - return retval; + return read; } /* No kernel lock - fine */