2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Event char devices, giving access to raw input device events.
|
|
|
|
*
|
|
|
|
* Copyright (c) 1999-2002 Vojtech Pavlik
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
|
|
* the Free Software Foundation.
|
|
|
|
*/
|
|
|
|
|
2010-11-30 15:33:07 +08:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define EVDEV_MINOR_BASE 64
|
|
|
|
#define EVDEV_MINORS 32
|
2010-06-11 03:05:24 +08:00
|
|
|
#define EVDEV_MIN_BUFFER_SIZE 64U
|
|
|
|
#define EVDEV_BUF_PACKETS 8
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/poll.h>
|
2009-10-04 20:11:37 +08:00
|
|
|
#include <linux/sched.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
2012-02-06 15:49:25 +08:00
|
|
|
#include <linux/input/mt.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/device.h>
|
2012-10-09 00:07:24 +08:00
|
|
|
#include <linux/cdev.h>
|
2008-10-17 10:31:42 +08:00
|
|
|
#include "input-compat.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct evdev {
|
|
|
|
int open;
|
|
|
|
struct input_handle handle;
|
|
|
|
wait_queue_head_t wait;
|
2010-03-04 22:50:28 +08:00
|
|
|
struct evdev_client __rcu *grab;
|
2007-04-12 13:30:00 +08:00
|
|
|
struct list_head client_list;
|
2007-08-30 12:22:18 +08:00
|
|
|
spinlock_t client_lock; /* protects client_list */
|
|
|
|
struct mutex mutex;
|
2007-06-15 11:32:24 +08:00
|
|
|
struct device dev;
|
2012-10-09 00:07:24 +08:00
|
|
|
struct cdev cdev;
|
2010-07-16 14:27:36 +08:00
|
|
|
bool exist;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client {
|
2011-04-13 14:29:38 +08:00
|
|
|
unsigned int head;
|
|
|
|
unsigned int tail;
|
2011-04-27 13:16:11 +08:00
|
|
|
unsigned int packet_head; /* [future] position of the first element of next packet */
|
2007-08-30 12:22:18 +08:00
|
|
|
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
2005-04-17 06:20:36 +08:00
|
|
|
struct fasync_struct *fasync;
|
|
|
|
struct evdev *evdev;
|
|
|
|
struct list_head node;
|
2012-02-03 16:19:07 +08:00
|
|
|
int clkid;
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
bool revoked;
|
2011-04-13 14:29:38 +08:00
|
|
|
unsigned int bufsize;
|
2010-06-24 00:17:56 +08:00
|
|
|
struct input_event buffer[];
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
Input: evdev - flush queues during EVIOCGKEY-like ioctls
If userspace requests current KEY-state, they very likely assume that no
such events are pending in the output queue of the evdev device.
Otherwise, they will parse events which they already handled via
EVIOCGKEY(). For XKB applications this can cause irreversible keyboard
states if a modifier is locked multiple times because a CTRL-DOWN event is
handled once via EVIOCGKEY() and once from the queue via read(), even
though it should handle it only once.
Therefore, lets do the only logical thing and flush the evdev queue
atomically during this ioctl. We only flush events that are affected by
the given ioctl.
This only affects boolean events like KEY, SND, SW and LED. ABS, REL and
others are not affected as duplicate events can be handled gracefully by
user-space.
Note: This actually breaks semantics of the evdev ABI. However,
investigations showed that userspace already expects the new semantics and
we end up fixing at least all XKB applications.
All applications that are aware of this race-condition mirror the KEY
state for each open-file and detect/drop duplicate events. Hence, they do
not care whether duplicates are posted or not and work fine with this fix.
Also note that we need proper locking to guarantee atomicity and avoid
dead-locks. event_lock must be locked before queue_lock (see input-core).
However, we can safely release event_lock while flushing the queue. This
allows the input-core to proceed with pending events and only stop if it
needs our queue_lock to post new events.
This should guarantee that we don't block event-dispatching for too long
while flushing a single event queue.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-04-08 12:13:19 +08:00
|
|
|
/* flush queued events of type @type, caller must hold client->buffer_lock */
|
|
|
|
static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
|
|
|
|
{
|
|
|
|
unsigned int i, head, num;
|
|
|
|
unsigned int mask = client->bufsize - 1;
|
|
|
|
bool is_report;
|
|
|
|
struct input_event *ev;
|
|
|
|
|
|
|
|
BUG_ON(type == EV_SYN);
|
|
|
|
|
|
|
|
head = client->tail;
|
|
|
|
client->packet_head = client->tail;
|
|
|
|
|
|
|
|
/* init to 1 so a leading SYN_REPORT will not be dropped */
|
|
|
|
num = 1;
|
|
|
|
|
|
|
|
for (i = client->tail; i != client->head; i = (i + 1) & mask) {
|
|
|
|
ev = &client->buffer[i];
|
|
|
|
is_report = ev->type == EV_SYN && ev->code == SYN_REPORT;
|
|
|
|
|
|
|
|
if (ev->type == type) {
|
|
|
|
/* drop matched entry */
|
|
|
|
continue;
|
|
|
|
} else if (is_report && !num) {
|
|
|
|
/* drop empty SYN_REPORT groups */
|
|
|
|
continue;
|
|
|
|
} else if (head != i) {
|
|
|
|
/* move entry to fill the gap */
|
|
|
|
client->buffer[head].time = ev->time;
|
|
|
|
client->buffer[head].type = ev->type;
|
|
|
|
client->buffer[head].code = ev->code;
|
|
|
|
client->buffer[head].value = ev->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
num++;
|
|
|
|
head = (head + 1) & mask;
|
|
|
|
|
|
|
|
if (is_report) {
|
|
|
|
num = 0;
|
|
|
|
client->packet_head = head;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
client->head = head;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* queue SYN_DROPPED event */
|
|
|
|
static void evdev_queue_syn_dropped(struct evdev_client *client)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
struct input_event ev;
|
|
|
|
ktime_t time;
|
|
|
|
|
|
|
|
time = ktime_get();
|
|
|
|
if (client->clkid != CLOCK_MONOTONIC)
|
|
|
|
time = ktime_sub(time, ktime_get_monotonic_offset());
|
|
|
|
|
|
|
|
ev.time = ktime_to_timeval(time);
|
|
|
|
ev.type = EV_SYN;
|
|
|
|
ev.code = SYN_DROPPED;
|
|
|
|
ev.value = 0;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&client->buffer_lock, flags);
|
|
|
|
|
|
|
|
client->buffer[client->head++] = ev;
|
|
|
|
client->head &= client->bufsize - 1;
|
|
|
|
|
|
|
|
if (unlikely(client->head == client->tail)) {
|
|
|
|
/* drop queue but keep our SYN_DROPPED event */
|
|
|
|
client->tail = (client->head - 1) & (client->bufsize - 1);
|
|
|
|
client->packet_head = client->tail;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&client->buffer_lock, flags);
|
|
|
|
}
|
|
|
|
|
2012-08-30 02:48:02 +08:00
|
|
|
static void __pass_event(struct evdev_client *client,
|
|
|
|
const struct input_event *event)
|
2007-08-30 12:22:18 +08:00
|
|
|
{
|
2011-04-13 14:29:38 +08:00
|
|
|
client->buffer[client->head++] = *event;
|
|
|
|
client->head &= client->bufsize - 1;
|
|
|
|
|
|
|
|
if (unlikely(client->head == client->tail)) {
|
|
|
|
/*
|
|
|
|
* This effectively "drops" all unconsumed events, leaving
|
|
|
|
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
|
|
|
|
*/
|
|
|
|
client->tail = (client->head - 2) & (client->bufsize - 1);
|
|
|
|
|
|
|
|
client->buffer[client->tail].time = event->time;
|
|
|
|
client->buffer[client->tail].type = EV_SYN;
|
|
|
|
client->buffer[client->tail].code = SYN_DROPPED;
|
|
|
|
client->buffer[client->tail].value = 0;
|
|
|
|
|
2011-04-27 13:16:11 +08:00
|
|
|
client->packet_head = client->tail;
|
|
|
|
}
|
2007-08-30 12:22:18 +08:00
|
|
|
|
2011-04-27 13:16:11 +08:00
|
|
|
if (event->type == EV_SYN && event->code == SYN_REPORT) {
|
|
|
|
client->packet_head = client->head;
|
2010-01-06 09:56:04 +08:00
|
|
|
kill_fasync(&client->fasync, SIGIO, POLL_IN);
|
2011-04-27 13:16:11 +08:00
|
|
|
}
|
2012-08-30 02:48:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void evdev_pass_values(struct evdev_client *client,
|
|
|
|
const struct input_value *vals, unsigned int count,
|
|
|
|
ktime_t mono, ktime_t real)
|
|
|
|
{
|
|
|
|
struct evdev *evdev = client->evdev;
|
|
|
|
const struct input_value *v;
|
|
|
|
struct input_event event;
|
|
|
|
bool wakeup = false;
|
|
|
|
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
if (client->revoked)
|
|
|
|
return;
|
|
|
|
|
2012-08-30 02:48:02 +08:00
|
|
|
event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
|
|
|
|
mono : real);
|
|
|
|
|
|
|
|
/* Interrupts are disabled, just acquire the lock. */
|
|
|
|
spin_lock(&client->buffer_lock);
|
|
|
|
|
|
|
|
for (v = vals; v != vals + count; v++) {
|
|
|
|
event.type = v->type;
|
|
|
|
event.code = v->code;
|
|
|
|
event.value = v->value;
|
|
|
|
__pass_event(client, &event);
|
|
|
|
if (v->type == EV_SYN && v->code == SYN_REPORT)
|
|
|
|
wakeup = true;
|
|
|
|
}
|
2011-04-27 13:16:11 +08:00
|
|
|
|
|
|
|
spin_unlock(&client->buffer_lock);
|
2012-08-30 02:48:02 +08:00
|
|
|
|
|
|
|
if (wakeup)
|
|
|
|
wake_up_interruptible(&evdev->wait);
|
2007-08-30 12:22:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-08-30 02:48:02 +08:00
|
|
|
* Pass incoming events to all connected clients.
|
2007-08-30 12:22:18 +08:00
|
|
|
*/
|
2012-08-30 02:48:02 +08:00
|
|
|
static void evdev_events(struct input_handle *handle,
|
|
|
|
const struct input_value *vals, unsigned int count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct evdev *evdev = handle->private;
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client;
|
2012-02-03 16:19:07 +08:00
|
|
|
ktime_t time_mono, time_real;
|
|
|
|
|
|
|
|
time_mono = ktime_get();
|
|
|
|
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-14 03:46:55 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
client = rcu_dereference(evdev->grab);
|
2012-02-03 16:19:07 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
if (client)
|
2012-08-30 02:48:02 +08:00
|
|
|
evdev_pass_values(client, vals, count, time_mono, time_real);
|
2007-08-30 12:22:18 +08:00
|
|
|
else
|
|
|
|
list_for_each_entry_rcu(client, &evdev->client_list, node)
|
2012-08-30 02:48:02 +08:00
|
|
|
evdev_pass_values(client, vals, count,
|
|
|
|
time_mono, time_real);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-14 03:46:55 +08:00
|
|
|
rcu_read_unlock();
|
2012-08-30 02:48:02 +08:00
|
|
|
}
|
2007-10-14 03:46:55 +08:00
|
|
|
|
2012-08-30 02:48:02 +08:00
|
|
|
/*
|
|
|
|
* Pass incoming event to all connected clients.
|
|
|
|
*/
|
|
|
|
static void evdev_event(struct input_handle *handle,
|
|
|
|
unsigned int type, unsigned int code, int value)
|
|
|
|
{
|
|
|
|
struct input_value vals[] = { { type, code, value } };
|
|
|
|
|
|
|
|
evdev_events(handle, vals, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int evdev_fasync(int fd, struct file *file, int on)
|
|
|
|
{
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client = file->private_data;
|
2006-06-26 13:48:47 +08:00
|
|
|
|
2009-02-02 05:52:56 +08:00
|
|
|
return fasync_helper(fd, file, on, &client->fasync);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-06-26 13:48:47 +08:00
|
|
|
static int evdev_flush(struct file *file, fl_owner_t id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
2007-08-30 12:22:18 +08:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2006-06-26 13:48:47 +08:00
|
|
|
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
if (!evdev->exist || client->revoked)
|
2007-08-30 12:22:18 +08:00
|
|
|
retval = -ENODEV;
|
|
|
|
else
|
|
|
|
retval = input_flush_device(&evdev->handle, file);
|
2006-06-26 13:48:47 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
mutex_unlock(&evdev->mutex);
|
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-06-15 11:32:24 +08:00
|
|
|
static void evdev_free(struct device *dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-06-15 11:32:24 +08:00
|
|
|
struct evdev *evdev = container_of(dev, struct evdev, dev);
|
|
|
|
|
2008-04-01 12:22:53 +08:00
|
|
|
input_put_device(evdev->handle.dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(evdev);
|
|
|
|
}
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
/*
|
|
|
|
* Grabs an event device (along with underlying input device).
|
|
|
|
* This function is called with evdev->mutex taken.
|
|
|
|
*/
|
|
|
|
static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (evdev->grab)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
error = input_grab_device(&evdev->handle);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
rcu_assign_pointer(evdev->grab, client);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
|
|
|
|
{
|
2012-05-02 15:13:36 +08:00
|
|
|
struct evdev_client *grab = rcu_dereference_protected(evdev->grab,
|
|
|
|
lockdep_is_held(&evdev->mutex));
|
|
|
|
|
|
|
|
if (grab != client)
|
2007-08-30 12:22:18 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rcu_assign_pointer(evdev->grab, NULL);
|
2007-10-14 03:46:55 +08:00
|
|
|
synchronize_rcu();
|
2007-08-30 12:22:18 +08:00
|
|
|
input_release_device(&evdev->handle);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void evdev_attach_client(struct evdev *evdev,
|
|
|
|
struct evdev_client *client)
|
|
|
|
{
|
|
|
|
spin_lock(&evdev->client_lock);
|
|
|
|
list_add_tail_rcu(&client->node, &evdev->client_list);
|
|
|
|
spin_unlock(&evdev->client_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void evdev_detach_client(struct evdev *evdev,
|
|
|
|
struct evdev_client *client)
|
|
|
|
{
|
|
|
|
spin_lock(&evdev->client_lock);
|
|
|
|
list_del_rcu(&client->node);
|
|
|
|
spin_unlock(&evdev->client_lock);
|
2007-10-14 03:46:55 +08:00
|
|
|
synchronize_rcu();
|
2007-08-30 12:22:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int evdev_open_device(struct evdev *evdev)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
if (!evdev->exist)
|
|
|
|
retval = -ENODEV;
|
2007-10-13 02:18:40 +08:00
|
|
|
else if (!evdev->open++) {
|
2007-08-30 12:22:18 +08:00
|
|
|
retval = input_open_device(&evdev->handle);
|
2007-10-13 02:18:40 +08:00
|
|
|
if (retval)
|
|
|
|
evdev->open--;
|
|
|
|
}
|
2007-08-30 12:22:18 +08:00
|
|
|
|
|
|
|
mutex_unlock(&evdev->mutex);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void evdev_close_device(struct evdev *evdev)
|
|
|
|
{
|
|
|
|
mutex_lock(&evdev->mutex);
|
|
|
|
|
|
|
|
if (evdev->exist && !--evdev->open)
|
|
|
|
input_close_device(&evdev->handle);
|
|
|
|
|
|
|
|
mutex_unlock(&evdev->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wake up users waiting for IO so they can disconnect from
|
|
|
|
* dead device.
|
|
|
|
*/
|
|
|
|
static void evdev_hangup(struct evdev *evdev)
|
|
|
|
{
|
|
|
|
struct evdev_client *client;
|
|
|
|
|
|
|
|
spin_lock(&evdev->client_lock);
|
|
|
|
list_for_each_entry(client, &evdev->client_list, node)
|
|
|
|
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
|
|
|
|
spin_unlock(&evdev->client_lock);
|
|
|
|
|
|
|
|
wake_up_interruptible(&evdev->wait);
|
|
|
|
}
|
|
|
|
|
2007-04-12 13:30:00 +08:00
|
|
|
static int evdev_release(struct inode *inode, struct file *file)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
mutex_lock(&evdev->mutex);
|
2012-05-02 15:13:36 +08:00
|
|
|
evdev_ungrab(evdev, client);
|
2007-08-30 12:22:18 +08:00
|
|
|
mutex_unlock(&evdev->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
evdev_detach_client(evdev, client);
|
2007-04-12 13:30:00 +08:00
|
|
|
kfree(client);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
evdev_close_device(evdev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-06-24 00:17:56 +08:00
|
|
|
static unsigned int evdev_compute_buffer_size(struct input_dev *dev)
|
|
|
|
{
|
2010-06-11 03:05:24 +08:00
|
|
|
unsigned int n_events =
|
|
|
|
max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS,
|
|
|
|
EVDEV_MIN_BUFFER_SIZE);
|
|
|
|
|
|
|
|
return roundup_pow_of_two(n_events);
|
2010-06-24 00:17:56 +08:00
|
|
|
}
|
|
|
|
|
2007-04-12 13:30:00 +08:00
|
|
|
static int evdev_open(struct inode *inode, struct file *file)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-10-09 00:07:24 +08:00
|
|
|
struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
|
|
|
|
unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
|
2007-08-30 12:22:18 +08:00
|
|
|
struct evdev_client *client;
|
2007-04-12 13:30:15 +08:00
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-24 00:17:56 +08:00
|
|
|
client = kzalloc(sizeof(struct evdev_client) +
|
|
|
|
bufsize * sizeof(struct input_event),
|
|
|
|
GFP_KERNEL);
|
2012-10-09 00:07:24 +08:00
|
|
|
if (!client)
|
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-24 00:17:56 +08:00
|
|
|
client->bufsize = bufsize;
|
2007-08-30 12:22:18 +08:00
|
|
|
spin_lock_init(&client->buffer_lock);
|
2007-04-12 13:30:00 +08:00
|
|
|
client->evdev = evdev;
|
2007-08-30 12:22:18 +08:00
|
|
|
evdev_attach_client(evdev, client);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
error = evdev_open_device(evdev);
|
|
|
|
if (error)
|
|
|
|
goto err_free_client;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-12 13:30:00 +08:00
|
|
|
file->private_data = client;
|
2010-02-04 16:30:42 +08:00
|
|
|
nonseekable_open(inode, file);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2007-06-15 11:32:24 +08:00
|
|
|
|
|
|
|
err_free_client:
|
2007-08-30 12:22:18 +08:00
|
|
|
evdev_detach_client(evdev, client);
|
2007-06-15 11:32:24 +08:00
|
|
|
kfree(client);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
|
|
|
size_t count, loff_t *ppos)
|
2005-12-12 01:40:37 +08:00
|
|
|
{
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
2005-12-12 01:40:37 +08:00
|
|
|
struct input_event event;
|
2012-02-09 15:08:48 +08:00
|
|
|
int retval = 0;
|
2005-05-29 15:26:43 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
if (count != 0 && count < input_event_size())
|
2011-02-26 01:30:46 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
if (!evdev->exist || client->revoked) {
|
2007-08-30 12:22:18 +08:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
2005-05-29 15:26:43 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
while (retval + input_event_size() <= count) {
|
|
|
|
|
2008-10-17 10:31:42 +08:00
|
|
|
if (input_event_from_user(buffer + retval, &event)) {
|
2007-08-30 12:22:18 +08:00
|
|
|
retval = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-02-26 01:30:46 +08:00
|
|
|
retval += input_event_size();
|
2007-08-30 12:22:18 +08:00
|
|
|
|
|
|
|
input_inject_event(&evdev->handle,
|
|
|
|
event.type, event.code, event.value);
|
2012-05-02 15:13:37 +08:00
|
|
|
}
|
2005-05-29 15:26:43 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&evdev->mutex);
|
2005-05-29 15:26:43 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
static int evdev_fetch_next_event(struct evdev_client *client,
|
|
|
|
struct input_event *event)
|
|
|
|
{
|
|
|
|
int have_event;
|
|
|
|
|
|
|
|
spin_lock_irq(&client->buffer_lock);
|
|
|
|
|
2011-12-31 07:16:44 +08:00
|
|
|
have_event = client->packet_head != client->tail;
|
2007-08-30 12:22:18 +08:00
|
|
|
if (have_event) {
|
|
|
|
*event = client->buffer[client->tail++];
|
2010-06-24 00:17:56 +08:00
|
|
|
client->tail &= client->bufsize - 1;
|
2007-08-30 12:22:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irq(&client->buffer_lock);
|
|
|
|
|
|
|
|
return have_event;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t evdev_read(struct file *file, char __user *buffer,
|
|
|
|
size_t count, loff_t *ppos)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
2007-08-30 12:22:18 +08:00
|
|
|
struct input_event event;
|
2012-05-02 15:13:37 +08:00
|
|
|
size_t read = 0;
|
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
if (count != 0 && count < input_event_size())
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
for (;;) {
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
if (!evdev->exist || client->revoked)
|
2012-05-02 15:13:37 +08:00
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
while (read + input_event_size() <= count &&
|
|
|
|
evdev_fetch_next_event(client, &event)) {
|
2005-12-12 01:40:37 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
if (input_event_to_user(buffer + read, &event))
|
|
|
|
return -EFAULT;
|
2005-12-12 01:40:37 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
read += input_event_size();
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
if (read)
|
|
|
|
break;
|
2011-12-31 07:16:44 +08:00
|
|
|
|
2012-05-02 15:13:37 +08:00
|
|
|
if (!(file->f_flags & O_NONBLOCK)) {
|
|
|
|
error = wait_event_interruptible(evdev->wait,
|
|
|
|
client->packet_head != client->tail ||
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
!evdev->exist || client->revoked);
|
2012-05-02 15:13:37 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return read;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* No kernel lock - fine */
|
|
|
|
static unsigned int evdev_poll(struct file *file, poll_table *wait)
|
|
|
|
{
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
2010-07-16 14:28:42 +08:00
|
|
|
unsigned int mask;
|
2006-06-26 13:48:47 +08:00
|
|
|
|
2007-04-12 13:30:00 +08:00
|
|
|
poll_wait(file, &evdev->wait, wait);
|
2010-07-16 14:28:42 +08:00
|
|
|
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
if (evdev->exist && !client->revoked)
|
|
|
|
mask = POLLOUT | POLLWRNORM;
|
|
|
|
else
|
|
|
|
mask = POLLHUP | POLLERR;
|
|
|
|
|
2011-04-27 13:16:11 +08:00
|
|
|
if (client->packet_head != client->tail)
|
2010-07-16 14:28:42 +08:00
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
|
|
|
|
return mask;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-12-12 01:40:37 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
|
|
|
|
#define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
|
2007-10-19 14:40:32 +08:00
|
|
|
#define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
|
2005-12-12 01:40:37 +08:00
|
|
|
|
|
|
|
#ifdef __BIG_ENDIAN
|
|
|
|
static int bits_to_user(unsigned long *bits, unsigned int maxbit,
|
|
|
|
unsigned int maxlen, void __user *p, int compat)
|
|
|
|
{
|
|
|
|
int len, i;
|
|
|
|
|
|
|
|
if (compat) {
|
2007-10-19 14:40:32 +08:00
|
|
|
len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
|
2007-05-11 13:12:15 +08:00
|
|
|
if (len > maxlen)
|
2005-12-12 01:40:37 +08:00
|
|
|
len = maxlen;
|
|
|
|
|
|
|
|
for (i = 0; i < len / sizeof(compat_long_t); i++)
|
|
|
|
if (copy_to_user((compat_long_t __user *) p + i,
|
|
|
|
(compat_long_t *) bits +
|
|
|
|
i + 1 - ((i % 2) << 1),
|
|
|
|
sizeof(compat_long_t)))
|
|
|
|
return -EFAULT;
|
|
|
|
} else {
|
2007-10-19 14:40:32 +08:00
|
|
|
len = BITS_TO_LONGS(maxbit) * sizeof(long);
|
2005-12-12 01:40:37 +08:00
|
|
|
if (len > maxlen)
|
|
|
|
len = maxlen;
|
|
|
|
|
|
|
|
if (copy_to_user(p, bits, len))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int bits_to_user(unsigned long *bits, unsigned int maxbit,
|
|
|
|
unsigned int maxlen, void __user *p, int compat)
|
|
|
|
{
|
|
|
|
int len = compat ?
|
2007-10-19 14:40:32 +08:00
|
|
|
BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) :
|
|
|
|
BITS_TO_LONGS(maxbit) * sizeof(long);
|
2005-12-12 01:40:37 +08:00
|
|
|
|
|
|
|
if (len > maxlen)
|
|
|
|
len = maxlen;
|
|
|
|
|
|
|
|
return copy_to_user(p, bits, len) ? -EFAULT : len;
|
|
|
|
}
|
|
|
|
#endif /* __BIG_ENDIAN */
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static int bits_to_user(unsigned long *bits, unsigned int maxbit,
|
|
|
|
unsigned int maxlen, void __user *p, int compat)
|
|
|
|
{
|
2007-10-19 14:40:32 +08:00
|
|
|
int len = BITS_TO_LONGS(maxbit) * sizeof(long);
|
2005-12-12 01:40:37 +08:00
|
|
|
|
|
|
|
if (len > maxlen)
|
|
|
|
len = maxlen;
|
|
|
|
|
|
|
|
return copy_to_user(p, bits, len) ? -EFAULT : len;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_COMPAT */
|
|
|
|
|
|
|
|
static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
len = strlen(str) + 1;
|
|
|
|
if (len > maxlen)
|
|
|
|
len = maxlen;
|
|
|
|
|
|
|
|
return copy_to_user(p, str, len) ? -EFAULT : len;
|
|
|
|
}
|
|
|
|
|
2008-08-08 23:46:53 +08:00
|
|
|
#define OLD_KEY_MAX 0x1ff
|
2010-08-03 11:29:10 +08:00
|
|
|
static int handle_eviocgbit(struct input_dev *dev,
|
|
|
|
unsigned int type, unsigned int size,
|
|
|
|
void __user *p, int compat_mode)
|
2008-08-05 23:42:42 +08:00
|
|
|
{
|
2008-08-08 23:46:53 +08:00
|
|
|
static unsigned long keymax_warn_time;
|
2008-08-05 23:42:42 +08:00
|
|
|
unsigned long *bits;
|
|
|
|
int len;
|
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
switch (type) {
|
2008-08-05 23:42:42 +08:00
|
|
|
|
|
|
|
case 0: bits = dev->evbit; len = EV_MAX; break;
|
|
|
|
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
|
|
|
|
case EV_REL: bits = dev->relbit; len = REL_MAX; break;
|
|
|
|
case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
|
|
|
|
case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
|
|
|
|
case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
|
|
|
|
case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
|
|
|
|
case EV_FF: bits = dev->ffbit; len = FF_MAX; break;
|
|
|
|
case EV_SW: bits = dev->swbit; len = SW_MAX; break;
|
|
|
|
default: return -EINVAL;
|
|
|
|
}
|
2008-08-08 23:46:53 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Work around bugs in userspace programs that like to do
|
|
|
|
* EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len'
|
|
|
|
* should be in bytes, not in bits.
|
|
|
|
*/
|
2010-08-03 11:29:10 +08:00
|
|
|
if (type == EV_KEY && size == OLD_KEY_MAX) {
|
2008-08-08 23:46:53 +08:00
|
|
|
len = OLD_KEY_MAX;
|
|
|
|
if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
|
2010-11-30 15:33:07 +08:00
|
|
|
pr_warning("(EVIOCGBIT): Suspicious buffer size %u, "
|
|
|
|
"limiting output to %zu bytes. See "
|
|
|
|
"http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
|
|
|
|
OLD_KEY_MAX,
|
|
|
|
BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
|
2008-08-08 23:46:53 +08:00
|
|
|
}
|
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
return bits_to_user(bits, len, size, p, compat_mode);
|
2008-08-05 23:42:42 +08:00
|
|
|
}
|
2008-08-08 23:46:53 +08:00
|
|
|
#undef OLD_KEY_MAX
|
2008-08-05 23:42:42 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
|
2010-09-10 12:54:22 +08:00
|
|
|
{
|
2010-12-15 15:53:21 +08:00
|
|
|
struct input_keymap_entry ke = {
|
|
|
|
.len = sizeof(unsigned int),
|
|
|
|
.flags = 0,
|
|
|
|
};
|
|
|
|
int __user *ip = (int __user *)p;
|
2010-09-10 12:54:22 +08:00
|
|
|
int error;
|
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
/* legacy case */
|
|
|
|
if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
|
|
|
|
return -EFAULT;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
error = input_get_keycode(dev, &ke);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
if (put_user(ke.keycode, ip + 1))
|
|
|
|
return -EFAULT;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p)
|
|
|
|
{
|
|
|
|
struct input_keymap_entry ke;
|
|
|
|
int error;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
if (copy_from_user(&ke, p, sizeof(ke)))
|
|
|
|
return -EFAULT;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
error = input_get_keycode(dev, &ke);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
if (copy_to_user(p, &ke, sizeof(ke)))
|
|
|
|
return -EFAULT;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p)
|
2010-09-10 12:54:22 +08:00
|
|
|
{
|
2010-12-15 15:53:21 +08:00
|
|
|
struct input_keymap_entry ke = {
|
|
|
|
.len = sizeof(unsigned int),
|
|
|
|
.flags = 0,
|
|
|
|
};
|
|
|
|
int __user *ip = (int __user *)p;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
|
|
|
|
return -EFAULT;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
if (get_user(ke.keycode, ip + 1))
|
|
|
|
return -EFAULT;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
return input_set_keycode(dev, &ke);
|
|
|
|
}
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p)
|
|
|
|
{
|
|
|
|
struct input_keymap_entry ke;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
if (copy_from_user(&ke, p, sizeof(ke)))
|
|
|
|
return -EFAULT;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
if (ke.len > sizeof(ke.scancode))
|
|
|
|
return -EINVAL;
|
2010-09-10 12:54:22 +08:00
|
|
|
|
|
|
|
return input_set_keycode(dev, &ke);
|
|
|
|
}
|
|
|
|
|
Input: evdev - flush queues during EVIOCGKEY-like ioctls
If userspace requests current KEY-state, they very likely assume that no
such events are pending in the output queue of the evdev device.
Otherwise, they will parse events which they already handled via
EVIOCGKEY(). For XKB applications this can cause irreversible keyboard
states if a modifier is locked multiple times because a CTRL-DOWN event is
handled once via EVIOCGKEY() and once from the queue via read(), even
though it should handle it only once.
Therefore, lets do the only logical thing and flush the evdev queue
atomically during this ioctl. We only flush events that are affected by
the given ioctl.
This only affects boolean events like KEY, SND, SW and LED. ABS, REL and
others are not affected as duplicate events can be handled gracefully by
user-space.
Note: This actually breaks semantics of the evdev ABI. However,
investigations showed that userspace already expects the new semantics and
we end up fixing at least all XKB applications.
All applications that are aware of this race-condition mirror the KEY
state for each open-file and detect/drop duplicate events. Hence, they do
not care whether duplicates are posted or not and work fine with this fix.
Also note that we need proper locking to guarantee atomicity and avoid
dead-locks. event_lock must be locked before queue_lock (see input-core).
However, we can safely release event_lock while flushing the queue. This
allows the input-core to proceed with pending events and only stop if it
needs our queue_lock to post new events.
This should guarantee that we don't block event-dispatching for too long
while flushing a single event queue.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-04-08 12:13:19 +08:00
|
|
|
/*
|
|
|
|
* If we transfer state to the user, we should flush all pending events
|
|
|
|
* of the same type from the client's queue. Otherwise, they might end up
|
|
|
|
* with duplicate events, which can screw up client's state tracking.
|
|
|
|
* If bits_to_user fails after flushing the queue, we queue a SYN_DROPPED
|
|
|
|
* event so user-space will notice missing events.
|
|
|
|
*
|
|
|
|
* LOCKING:
|
|
|
|
* We need to take event_lock before buffer_lock to avoid dead-locks. But we
|
|
|
|
* need the even_lock only to guarantee consistent state. We can safely release
|
|
|
|
* it while flushing the queue. This allows input-core to handle filters while
|
|
|
|
* we flush the queue.
|
|
|
|
*/
|
|
|
|
static int evdev_handle_get_val(struct evdev_client *client,
|
|
|
|
struct input_dev *dev, unsigned int type,
|
|
|
|
unsigned long *bits, unsigned int max,
|
|
|
|
unsigned int size, void __user *p, int compat)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
unsigned long *mem;
|
|
|
|
|
|
|
|
mem = kmalloc(sizeof(unsigned long) * max, GFP_KERNEL);
|
|
|
|
if (!mem)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock_irq(&dev->event_lock);
|
|
|
|
spin_lock(&client->buffer_lock);
|
|
|
|
|
|
|
|
memcpy(mem, bits, sizeof(unsigned long) * max);
|
|
|
|
|
|
|
|
spin_unlock(&dev->event_lock);
|
|
|
|
|
|
|
|
__evdev_flush_queue(client, type);
|
|
|
|
|
|
|
|
spin_unlock_irq(&client->buffer_lock);
|
|
|
|
|
|
|
|
ret = bits_to_user(mem, max, size, p, compat);
|
|
|
|
if (ret < 0)
|
|
|
|
evdev_queue_syn_dropped(client);
|
|
|
|
|
|
|
|
kfree(mem);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-06 15:49:25 +08:00
|
|
|
static int evdev_handle_mt_request(struct input_dev *dev,
|
|
|
|
unsigned int size,
|
|
|
|
int __user *ip)
|
|
|
|
{
|
2012-09-15 21:15:58 +08:00
|
|
|
const struct input_mt *mt = dev->mt;
|
2012-02-06 15:49:25 +08:00
|
|
|
unsigned int code;
|
|
|
|
int max_slots;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (get_user(code, &ip[0]))
|
|
|
|
return -EFAULT;
|
2012-09-15 21:15:58 +08:00
|
|
|
if (!mt || !input_is_mt_value(code))
|
2012-02-06 15:49:25 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
max_slots = (size - sizeof(__u32)) / sizeof(__s32);
|
2012-09-15 21:15:58 +08:00
|
|
|
for (i = 0; i < mt->num_slots && i < max_slots; i++) {
|
|
|
|
int value = input_mt_get_value(&mt->slots[i], code);
|
|
|
|
if (put_user(value, &ip[1 + i]))
|
2012-02-06 15:49:25 +08:00
|
|
|
return -EFAULT;
|
2012-09-15 21:15:58 +08:00
|
|
|
}
|
2012-02-06 15:49:25 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
|
|
|
|
struct file *file)
|
|
|
|
{
|
|
|
|
client->revoked = true;
|
|
|
|
evdev_ungrab(evdev, client);
|
|
|
|
input_flush_device(&evdev->handle, file);
|
|
|
|
wake_up_interruptible(&evdev->wait);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|
|
|
void __user *p, int compat_mode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-04-12 13:30:00 +08:00
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct input_dev *dev = evdev->handle.dev;
|
|
|
|
struct input_absinfo abs;
|
2006-07-19 13:40:22 +08:00
|
|
|
struct ff_effect effect;
|
2005-12-12 01:40:37 +08:00
|
|
|
int __user *ip = (int __user *)p;
|
2010-03-09 14:37:10 +08:00
|
|
|
unsigned int i, t, u, v;
|
2010-08-03 11:29:10 +08:00
|
|
|
unsigned int size;
|
2006-07-19 13:40:22 +08:00
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
/* First we check for fixed-length commands */
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (cmd) {
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
case EVIOCGVERSION:
|
|
|
|
return put_user(EV_VERSION, ip);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
case EVIOCGID:
|
|
|
|
if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
2006-04-29 13:13:21 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
case EVIOCGREP:
|
|
|
|
if (!test_bit(EV_REP, dev->evbit))
|
|
|
|
return -ENOSYS;
|
|
|
|
if (put_user(dev->rep[REP_DELAY], ip))
|
|
|
|
return -EFAULT;
|
|
|
|
if (put_user(dev->rep[REP_PERIOD], ip + 1))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
2006-04-29 13:13:21 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
case EVIOCSREP:
|
|
|
|
if (!test_bit(EV_REP, dev->evbit))
|
|
|
|
return -ENOSYS;
|
|
|
|
if (get_user(u, ip))
|
|
|
|
return -EFAULT;
|
|
|
|
if (get_user(v, ip + 1))
|
|
|
|
return -EFAULT;
|
2006-04-29 13:13:21 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
|
|
|
|
input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
|
2005-12-12 01:40:37 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
case EVIOCRMFF:
|
|
|
|
return input_ff_erase(dev, (int)(unsigned long) p, file);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
case EVIOCGEFFECTS:
|
|
|
|
i = test_bit(EV_FF, dev->evbit) ?
|
|
|
|
dev->ff->max_effects : 0;
|
|
|
|
if (put_user(i, ip))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case EVIOCGRAB:
|
|
|
|
if (p)
|
|
|
|
return evdev_grab(evdev, client);
|
|
|
|
else
|
|
|
|
return evdev_ungrab(evdev, client);
|
2010-12-15 15:53:21 +08:00
|
|
|
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
case EVIOCREVOKE:
|
|
|
|
if (p)
|
|
|
|
return -EINVAL;
|
|
|
|
else
|
|
|
|
return evdev_revoke(evdev, client, file);
|
|
|
|
|
2012-02-03 16:19:07 +08:00
|
|
|
case EVIOCSCLOCKID:
|
|
|
|
if (copy_from_user(&i, p, sizeof(unsigned int)))
|
|
|
|
return -EFAULT;
|
|
|
|
if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME)
|
|
|
|
return -EINVAL;
|
|
|
|
client->clkid = i;
|
|
|
|
return 0;
|
|
|
|
|
2010-12-15 15:53:21 +08:00
|
|
|
case EVIOCGKEYCODE:
|
|
|
|
return evdev_handle_get_keycode(dev, p);
|
|
|
|
|
|
|
|
case EVIOCSKEYCODE:
|
|
|
|
return evdev_handle_set_keycode(dev, p);
|
|
|
|
|
|
|
|
case EVIOCGKEYCODE_V2:
|
|
|
|
return evdev_handle_get_keycode_v2(dev, p);
|
|
|
|
|
|
|
|
case EVIOCSKEYCODE_V2:
|
|
|
|
return evdev_handle_set_keycode_v2(dev, p);
|
2010-08-03 11:29:10 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
size = _IOC_SIZE(cmd);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
/* Now check variable-length commands */
|
|
|
|
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
|
|
|
|
switch (EVIOC_MASK_SIZE(cmd)) {
|
2005-05-29 15:30:15 +08:00
|
|
|
|
2010-12-19 03:51:13 +08:00
|
|
|
case EVIOCGPROP(0):
|
|
|
|
return bits_to_user(dev->propbit, INPUT_PROP_MAX,
|
|
|
|
size, p, compat_mode);
|
|
|
|
|
2012-02-06 15:49:25 +08:00
|
|
|
case EVIOCGMTSLOTS(0):
|
|
|
|
return evdev_handle_mt_request(dev, size, ip);
|
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOCGKEY(0):
|
Input: evdev - flush queues during EVIOCGKEY-like ioctls
If userspace requests current KEY-state, they very likely assume that no
such events are pending in the output queue of the evdev device.
Otherwise, they will parse events which they already handled via
EVIOCGKEY(). For XKB applications this can cause irreversible keyboard
states if a modifier is locked multiple times because a CTRL-DOWN event is
handled once via EVIOCGKEY() and once from the queue via read(), even
though it should handle it only once.
Therefore, lets do the only logical thing and flush the evdev queue
atomically during this ioctl. We only flush events that are affected by
the given ioctl.
This only affects boolean events like KEY, SND, SW and LED. ABS, REL and
others are not affected as duplicate events can be handled gracefully by
user-space.
Note: This actually breaks semantics of the evdev ABI. However,
investigations showed that userspace already expects the new semantics and
we end up fixing at least all XKB applications.
All applications that are aware of this race-condition mirror the KEY
state for each open-file and detect/drop duplicate events. Hence, they do
not care whether duplicates are posted or not and work fine with this fix.
Also note that we need proper locking to guarantee atomicity and avoid
dead-locks. event_lock must be locked before queue_lock (see input-core).
However, we can safely release event_lock while flushing the queue. This
allows the input-core to proceed with pending events and only stop if it
needs our queue_lock to post new events.
This should guarantee that we don't block event-dispatching for too long
while flushing a single event queue.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-04-08 12:13:19 +08:00
|
|
|
return evdev_handle_get_val(client, dev, EV_KEY, dev->key,
|
|
|
|
KEY_MAX, size, p, compat_mode);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOCGLED(0):
|
Input: evdev - flush queues during EVIOCGKEY-like ioctls
If userspace requests current KEY-state, they very likely assume that no
such events are pending in the output queue of the evdev device.
Otherwise, they will parse events which they already handled via
EVIOCGKEY(). For XKB applications this can cause irreversible keyboard
states if a modifier is locked multiple times because a CTRL-DOWN event is
handled once via EVIOCGKEY() and once from the queue via read(), even
though it should handle it only once.
Therefore, lets do the only logical thing and flush the evdev queue
atomically during this ioctl. We only flush events that are affected by
the given ioctl.
This only affects boolean events like KEY, SND, SW and LED. ABS, REL and
others are not affected as duplicate events can be handled gracefully by
user-space.
Note: This actually breaks semantics of the evdev ABI. However,
investigations showed that userspace already expects the new semantics and
we end up fixing at least all XKB applications.
All applications that are aware of this race-condition mirror the KEY
state for each open-file and detect/drop duplicate events. Hence, they do
not care whether duplicates are posted or not and work fine with this fix.
Also note that we need proper locking to guarantee atomicity and avoid
dead-locks. event_lock must be locked before queue_lock (see input-core).
However, we can safely release event_lock while flushing the queue. This
allows the input-core to proceed with pending events and only stop if it
needs our queue_lock to post new events.
This should guarantee that we don't block event-dispatching for too long
while flushing a single event queue.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-04-08 12:13:19 +08:00
|
|
|
return evdev_handle_get_val(client, dev, EV_LED, dev->led,
|
|
|
|
LED_MAX, size, p, compat_mode);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOCGSND(0):
|
Input: evdev - flush queues during EVIOCGKEY-like ioctls
If userspace requests current KEY-state, they very likely assume that no
such events are pending in the output queue of the evdev device.
Otherwise, they will parse events which they already handled via
EVIOCGKEY(). For XKB applications this can cause irreversible keyboard
states if a modifier is locked multiple times because a CTRL-DOWN event is
handled once via EVIOCGKEY() and once from the queue via read(), even
though it should handle it only once.
Therefore, lets do the only logical thing and flush the evdev queue
atomically during this ioctl. We only flush events that are affected by
the given ioctl.
This only affects boolean events like KEY, SND, SW and LED. ABS, REL and
others are not affected as duplicate events can be handled gracefully by
user-space.
Note: This actually breaks semantics of the evdev ABI. However,
investigations showed that userspace already expects the new semantics and
we end up fixing at least all XKB applications.
All applications that are aware of this race-condition mirror the KEY
state for each open-file and detect/drop duplicate events. Hence, they do
not care whether duplicates are posted or not and work fine with this fix.
Also note that we need proper locking to guarantee atomicity and avoid
dead-locks. event_lock must be locked before queue_lock (see input-core).
However, we can safely release event_lock while flushing the queue. This
allows the input-core to proceed with pending events and only stop if it
needs our queue_lock to post new events.
This should guarantee that we don't block event-dispatching for too long
while flushing a single event queue.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-04-08 12:13:19 +08:00
|
|
|
return evdev_handle_get_val(client, dev, EV_SND, dev->snd,
|
|
|
|
SND_MAX, size, p, compat_mode);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOCGSW(0):
|
Input: evdev - flush queues during EVIOCGKEY-like ioctls
If userspace requests current KEY-state, they very likely assume that no
such events are pending in the output queue of the evdev device.
Otherwise, they will parse events which they already handled via
EVIOCGKEY(). For XKB applications this can cause irreversible keyboard
states if a modifier is locked multiple times because a CTRL-DOWN event is
handled once via EVIOCGKEY() and once from the queue via read(), even
though it should handle it only once.
Therefore, lets do the only logical thing and flush the evdev queue
atomically during this ioctl. We only flush events that are affected by
the given ioctl.
This only affects boolean events like KEY, SND, SW and LED. ABS, REL and
others are not affected as duplicate events can be handled gracefully by
user-space.
Note: This actually breaks semantics of the evdev ABI. However,
investigations showed that userspace already expects the new semantics and
we end up fixing at least all XKB applications.
All applications that are aware of this race-condition mirror the KEY
state for each open-file and detect/drop duplicate events. Hence, they do
not care whether duplicates are posted or not and work fine with this fix.
Also note that we need proper locking to guarantee atomicity and avoid
dead-locks. event_lock must be locked before queue_lock (see input-core).
However, we can safely release event_lock while flushing the queue. This
allows the input-core to proceed with pending events and only stop if it
needs our queue_lock to post new events.
This should guarantee that we don't block event-dispatching for too long
while flushing a single event queue.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-04-08 12:13:19 +08:00
|
|
|
return evdev_handle_get_val(client, dev, EV_SW, dev->sw,
|
|
|
|
SW_MAX, size, p, compat_mode);
|
2005-09-07 06:19:06 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOCGNAME(0):
|
|
|
|
return str_to_user(dev->name, size, p);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOCGPHYS(0):
|
|
|
|
return str_to_user(dev->phys, size, p);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOCGUNIQ(0):
|
|
|
|
return str_to_user(dev->uniq, size, p);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
case EVIOC_MASK_SIZE(EVIOCSFF):
|
|
|
|
if (input_ff_effect_from_user(p, size, &effect))
|
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
error = input_ff_upload(dev, &effect, file);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
|
|
|
|
return -EFAULT;
|
2005-05-29 15:30:15 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
return error;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
/* Multi-number variable-length handlers */
|
|
|
|
if (_IOC_TYPE(cmd) != 'E')
|
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if (_IOC_DIR(cmd) == _IOC_READ) {
|
2007-08-30 12:22:18 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
|
|
|
|
return handle_eviocgbit(dev,
|
|
|
|
_IOC_NR(cmd) & EV_MAX, size,
|
|
|
|
p, compat_mode);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
|
2008-06-02 13:08:10 +08:00
|
|
|
|
2010-10-18 23:43:30 +08:00
|
|
|
if (!dev->absinfo)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
t = _IOC_NR(cmd) & ABS_MAX;
|
|
|
|
abs = dev->absinfo[t];
|
2008-06-02 13:08:10 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if (copy_to_user(p, &abs, min_t(size_t,
|
|
|
|
size, sizeof(struct input_absinfo))))
|
|
|
|
return -EFAULT;
|
2008-06-02 13:08:10 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2008-06-02 13:08:10 +08:00
|
|
|
|
2010-10-18 23:43:50 +08:00
|
|
|
if (_IOC_DIR(cmd) == _IOC_WRITE) {
|
2008-06-02 13:08:10 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-10-18 23:43:30 +08:00
|
|
|
if (!dev->absinfo)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
t = _IOC_NR(cmd) & ABS_MAX;
|
2005-05-29 15:30:15 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if (copy_from_user(&abs, p, min_t(size_t,
|
|
|
|
size, sizeof(struct input_absinfo))))
|
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
if (size < sizeof(struct input_absinfo))
|
|
|
|
abs.resolution = 0;
|
2010-08-03 11:18:21 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
/* We can't change number of reserved MT slots */
|
|
|
|
if (t == ABS_MT_SLOT)
|
|
|
|
return -EINVAL;
|
2010-07-16 14:10:10 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
/*
|
|
|
|
* Take event lock to ensure that we are not
|
|
|
|
* changing device parameters in the middle
|
|
|
|
* of event.
|
|
|
|
*/
|
|
|
|
spin_lock_irq(&dev->event_lock);
|
|
|
|
dev->absinfo[t] = abs;
|
|
|
|
spin_unlock_irq(&dev->event_lock);
|
2007-08-30 12:22:18 +08:00
|
|
|
|
2010-08-03 11:29:10 +08:00
|
|
|
return 0;
|
2007-08-30 12:22:18 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-08-03 11:29:10 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
|
|
|
|
void __user *p, int compat_mode)
|
|
|
|
{
|
|
|
|
struct evdev_client *client = file->private_data;
|
|
|
|
struct evdev *evdev = client->evdev;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
retval = mutex_lock_interruptible(&evdev->mutex);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
Input: evdev - add EVIOCREVOKE ioctl
If we have multiple sessions on a system, we normally don't want
background sessions to read input events. Otherwise, it could capture
passwords and more entered by the user on the foreground session. This is
a real world problem as the recent XMir development showed:
http://mjg59.dreamwidth.org/27327.html
We currently rely on sessions to release input devices when being
deactivated. This relies on trust across sessions. But that's not given on
usual systems. We therefore need a way to control which processes have
access to input devices.
With VTs the kernel simply routed them through the active /dev/ttyX. This
is not possible with evdev devices, though. Moreover, we want to avoid
routing input-devices through some dispatcher-daemon in userspace (which
would add some latency).
This patch introduces EVIOCREVOKE. If called on an evdev fd, this revokes
device-access irrecoverably for that *single* open-file. Hence, once you
call EVIOCREVOKE on any dup()ed fd, all fds for that open-file will be
rather useless now (but still valid compared to close()!). This allows us
to pass fds directly to session-processes from a trusted source. The
source keeps a dup()ed fd and revokes access once the session-process is
no longer active.
Compared to the EVIOCMUTE proposal, we can avoid the CAP_SYS_ADMIN
restriction now as there is no way to revive the fd again. Hence, a user
is free to call EVIOCREVOKE themself to kill the fd.
Additionally, this ioctl allows multi-layer access-control (again compared
to EVIOCMUTE which was limited to one layer via CAP_SYS_ADMIN). A middle
layer can simply request a new open-file from the layer above and pass it
to the layer below. Now each layer can call EVIOCREVOKE on the fds to
revoke access for all layers below, at the expense of one fd per layer.
There's already ongoing experimental user-space work which demonstrates
how it can be used:
http://lists.freedesktop.org/archives/systemd-devel/2013-August/012897.html
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2013-09-08 03:23:05 +08:00
|
|
|
if (!evdev->exist || client->revoked) {
|
2007-08-30 12:22:18 +08:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = evdev_do_ioctl(file, cmd, p, compat_mode);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&evdev->mutex);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2005-12-12 01:40:37 +08:00
|
|
|
static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
|
|
|
|
}
|
2005-05-29 15:30:15 +08:00
|
|
|
|
2005-12-12 01:40:37 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
2007-08-30 12:22:18 +08:00
|
|
|
static long evdev_ioctl_compat(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
2005-05-29 15:26:43 +08:00
|
|
|
{
|
2005-12-12 01:40:37 +08:00
|
|
|
return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-05-29 15:26:43 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-14 13:31:59 +08:00
|
|
|
static const struct file_operations evdev_fops = {
|
2007-08-30 12:22:18 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.read = evdev_read,
|
|
|
|
.write = evdev_write,
|
|
|
|
.poll = evdev_poll,
|
|
|
|
.open = evdev_open,
|
|
|
|
.release = evdev_release,
|
|
|
|
.unlocked_ioctl = evdev_ioctl,
|
2005-05-29 15:26:43 +08:00
|
|
|
#ifdef CONFIG_COMPAT
|
2007-08-30 12:22:18 +08:00
|
|
|
.compat_ioctl = evdev_ioctl_compat,
|
2005-05-29 15:26:43 +08:00
|
|
|
#endif
|
2007-08-30 12:22:18 +08:00
|
|
|
.fasync = evdev_fasync,
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-16 00:52:59 +08:00
|
|
|
.flush = evdev_flush,
|
|
|
|
.llseek = no_llseek,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
/*
|
|
|
|
* Mark device non-existent. This disables writes, ioctls and
|
|
|
|
* prevents new users from opening the device. Already posted
|
|
|
|
* blocking reads will stay, however new ones will fail.
|
|
|
|
*/
|
|
|
|
static void evdev_mark_dead(struct evdev *evdev)
|
|
|
|
{
|
|
|
|
mutex_lock(&evdev->mutex);
|
2010-07-16 14:27:36 +08:00
|
|
|
evdev->exist = false;
|
2007-08-30 12:22:18 +08:00
|
|
|
mutex_unlock(&evdev->mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void evdev_cleanup(struct evdev *evdev)
|
|
|
|
{
|
|
|
|
struct input_handle *handle = &evdev->handle;
|
|
|
|
|
|
|
|
evdev_mark_dead(evdev);
|
|
|
|
evdev_hangup(evdev);
|
2012-10-09 00:07:24 +08:00
|
|
|
|
|
|
|
cdev_del(&evdev->cdev);
|
2007-08-30 12:22:18 +08:00
|
|
|
|
|
|
|
/* evdev is marked dead so no one else accesses evdev->open */
|
|
|
|
if (evdev->open) {
|
|
|
|
input_flush_device(handle, NULL);
|
|
|
|
input_close_device(handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create new evdev device. Note that input core serializes calls
|
2012-10-09 00:07:24 +08:00
|
|
|
* to connect and disconnect.
|
2007-08-30 12:22:18 +08:00
|
|
|
*/
|
2007-04-12 13:29:46 +08:00
|
|
|
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
|
|
|
|
const struct input_device_id *id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct evdev *evdev;
|
|
|
|
int minor;
|
2012-10-09 00:07:24 +08:00
|
|
|
int dev_no;
|
2007-04-12 13:29:46 +08:00
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-10-09 00:07:24 +08:00
|
|
|
minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
|
|
|
|
if (minor < 0) {
|
|
|
|
error = minor;
|
|
|
|
pr_err("failed to reserve new minor: %d\n", error);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-04-12 13:29:46 +08:00
|
|
|
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
|
2012-10-09 00:07:24 +08:00
|
|
|
if (!evdev) {
|
|
|
|
error = -ENOMEM;
|
|
|
|
goto err_free_minor;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-12 13:30:00 +08:00
|
|
|
INIT_LIST_HEAD(&evdev->client_list);
|
2007-08-30 12:22:18 +08:00
|
|
|
spin_lock_init(&evdev->client_lock);
|
|
|
|
mutex_init(&evdev->mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
init_waitqueue_head(&evdev->wait);
|
2010-07-16 14:27:36 +08:00
|
|
|
evdev->exist = true;
|
2012-10-09 00:07:24 +08:00
|
|
|
|
|
|
|
dev_no = minor;
|
|
|
|
/* Normalize device number if it falls into legacy range */
|
|
|
|
if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
|
|
|
|
dev_no -= EVDEV_MINOR_BASE;
|
|
|
|
dev_set_name(&evdev->dev, "event%d", dev_no);
|
2007-08-30 12:22:18 +08:00
|
|
|
|
2008-04-01 12:22:53 +08:00
|
|
|
evdev->handle.dev = input_get_device(dev);
|
2009-05-10 07:08:04 +08:00
|
|
|
evdev->handle.name = dev_name(&evdev->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
evdev->handle.handler = handler;
|
|
|
|
evdev->handle.private = evdev;
|
|
|
|
|
2012-10-09 00:07:24 +08:00
|
|
|
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
|
2007-06-15 11:32:24 +08:00
|
|
|
evdev->dev.class = &input_class;
|
|
|
|
evdev->dev.parent = &dev->dev;
|
|
|
|
evdev->dev.release = evdev_free;
|
|
|
|
device_initialize(&evdev->dev);
|
2007-04-12 13:29:46 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
error = input_register_handle(&evdev->handle);
|
2007-04-12 13:29:46 +08:00
|
|
|
if (error)
|
2007-06-15 11:32:24 +08:00
|
|
|
goto err_free_evdev;
|
2007-04-12 13:29:46 +08:00
|
|
|
|
2012-10-09 00:07:24 +08:00
|
|
|
cdev_init(&evdev->cdev, &evdev_fops);
|
2012-10-22 08:57:20 +08:00
|
|
|
evdev->cdev.kobj.parent = &evdev->dev.kobj;
|
2012-10-09 00:07:24 +08:00
|
|
|
error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
|
2007-08-30 12:22:18 +08:00
|
|
|
if (error)
|
|
|
|
goto err_unregister_handle;
|
|
|
|
|
|
|
|
error = device_add(&evdev->dev);
|
2007-04-12 13:29:46 +08:00
|
|
|
if (error)
|
2007-08-30 12:22:18 +08:00
|
|
|
goto err_cleanup_evdev;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-12 13:29:46 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-30 12:22:18 +08:00
|
|
|
err_cleanup_evdev:
|
|
|
|
evdev_cleanup(evdev);
|
|
|
|
err_unregister_handle:
|
|
|
|
input_unregister_handle(&evdev->handle);
|
2007-04-12 13:29:46 +08:00
|
|
|
err_free_evdev:
|
2007-06-15 11:32:24 +08:00
|
|
|
put_device(&evdev->dev);
|
2012-10-09 00:07:24 +08:00
|
|
|
err_free_minor:
|
|
|
|
input_free_minor(minor);
|
2007-04-12 13:29:46 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void evdev_disconnect(struct input_handle *handle)
|
|
|
|
{
|
|
|
|
struct evdev *evdev = handle->private;
|
|
|
|
|
2007-06-15 11:32:24 +08:00
|
|
|
device_del(&evdev->dev);
|
2007-08-30 12:22:18 +08:00
|
|
|
evdev_cleanup(evdev);
|
2012-10-09 00:07:24 +08:00
|
|
|
input_free_minor(MINOR(evdev->dev.devt));
|
2007-08-30 12:22:18 +08:00
|
|
|
input_unregister_handle(handle);
|
2007-06-15 11:32:24 +08:00
|
|
|
put_device(&evdev->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-09-14 13:31:59 +08:00
|
|
|
static const struct input_device_id evdev_ids[] = {
|
2005-04-17 06:20:36 +08:00
|
|
|
{ .driver_info = 1 }, /* Matches all devices */
|
|
|
|
{ }, /* Terminating zero entry */
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(input, evdev_ids);
|
|
|
|
|
|
|
|
static struct input_handler evdev_handler = {
|
2007-08-30 12:22:18 +08:00
|
|
|
.event = evdev_event,
|
2012-08-30 02:48:02 +08:00
|
|
|
.events = evdev_events,
|
2007-08-30 12:22:18 +08:00
|
|
|
.connect = evdev_connect,
|
|
|
|
.disconnect = evdev_disconnect,
|
2012-10-09 00:07:24 +08:00
|
|
|
.legacy_minors = true,
|
2007-08-30 12:22:18 +08:00
|
|
|
.minor = EVDEV_MINOR_BASE,
|
|
|
|
.name = "evdev",
|
|
|
|
.id_table = evdev_ids,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int __init evdev_init(void)
|
|
|
|
{
|
2006-09-14 13:32:39 +08:00
|
|
|
return input_register_handler(&evdev_handler);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit evdev_exit(void)
|
|
|
|
{
|
|
|
|
input_unregister_handler(&evdev_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(evdev_init);
|
|
|
|
module_exit(evdev_exit);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
|
|
|
MODULE_DESCRIPTION("Input driver event char devices");
|
|
|
|
MODULE_LICENSE("GPL");
|