usb-mtp: Add support for inotify based file monitoring
For now, we use inotify watches to track only a small number of events, namely, add, delete and modify. Note that for delete, the kernel already deactivates the watch for us and we just need to take care of modifying our internal state. inotify is a linux only mechanism. Suggested-by: Gerd Hoffman <kraxel@redhat.com> Signed-off-by: Bandan Das <bsd@redhat.com> Message-id: 1448314625-3855-4-git-send-email-bsd@redhat.com Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
b3c4d4250f
commit
8e3e3897ce
229
hw/usb/dev-mtp.c
229
hw/usb/dev-mtp.c
@ -15,6 +15,10 @@
|
|||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include "qemu/main-loop.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/iov.h"
|
#include "qemu/iov.h"
|
||||||
@ -62,6 +66,11 @@ enum mtp_code {
|
|||||||
/* format codes */
|
/* format codes */
|
||||||
FMT_UNDEFINED_OBJECT = 0x3000,
|
FMT_UNDEFINED_OBJECT = 0x3000,
|
||||||
FMT_ASSOCIATION = 0x3001,
|
FMT_ASSOCIATION = 0x3001,
|
||||||
|
|
||||||
|
/* event codes */
|
||||||
|
EVT_OBJ_ADDED = 0x4002,
|
||||||
|
EVT_OBJ_REMOVED = 0x4003,
|
||||||
|
EVT_OBJ_INFO_CHANGED = 0x4007,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -84,6 +93,17 @@ enum {
|
|||||||
EP_EVENT,
|
EP_EVENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
typedef struct MTPMonEntry MTPMonEntry;
|
||||||
|
|
||||||
|
struct MTPMonEntry {
|
||||||
|
uint32_t event;
|
||||||
|
uint32_t handle;
|
||||||
|
|
||||||
|
QTAILQ_ENTRY(MTPMonEntry) next;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct MTPControl {
|
struct MTPControl {
|
||||||
uint16_t code;
|
uint16_t code;
|
||||||
uint32_t trans;
|
uint32_t trans;
|
||||||
@ -108,6 +128,10 @@ struct MTPObject {
|
|||||||
char *name;
|
char *name;
|
||||||
char *path;
|
char *path;
|
||||||
struct stat stat;
|
struct stat stat;
|
||||||
|
#ifdef __linux__
|
||||||
|
/* inotify watch cookie */
|
||||||
|
int watchfd;
|
||||||
|
#endif
|
||||||
MTPObject *parent;
|
MTPObject *parent;
|
||||||
uint32_t nchildren;
|
uint32_t nchildren;
|
||||||
QLIST_HEAD(, MTPObject) children;
|
QLIST_HEAD(, MTPObject) children;
|
||||||
@ -129,6 +153,11 @@ struct MTPState {
|
|||||||
uint32_t next_handle;
|
uint32_t next_handle;
|
||||||
|
|
||||||
QTAILQ_HEAD(, MTPObject) objects;
|
QTAILQ_HEAD(, MTPObject) objects;
|
||||||
|
#ifdef __linux__
|
||||||
|
/* inotify descriptor */
|
||||||
|
int inotifyfd;
|
||||||
|
QTAILQ_HEAD(events, MTPMonEntry) events;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TYPE_USB_MTP "usb-mtp"
|
#define TYPE_USB_MTP "usb-mtp"
|
||||||
@ -372,6 +401,185 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
|
||||||
|
char *name, int len)
|
||||||
|
{
|
||||||
|
MTPObject *iter;
|
||||||
|
|
||||||
|
QLIST_FOREACH(iter, &parent->children, list) {
|
||||||
|
if (strncmp(iter->name, name, len) == 0) {
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
|
||||||
|
{
|
||||||
|
MTPObject *iter;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(iter, &s->objects, next) {
|
||||||
|
if (iter->watchfd == wd) {
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void inotify_watchfn(void *arg)
|
||||||
|
{
|
||||||
|
MTPState *s = arg;
|
||||||
|
ssize_t bytes;
|
||||||
|
/* From the man page: atleast one event can be read */
|
||||||
|
int len = sizeof(struct inotify_event) + NAME_MAX + 1;
|
||||||
|
int pos;
|
||||||
|
char buf[len];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
bytes = read(s->inotifyfd, buf, len);
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
if (bytes <= 0) {
|
||||||
|
/* Better luck next time */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Ignore initiator initiated events.
|
||||||
|
* For now we are good because the store is RO
|
||||||
|
*/
|
||||||
|
while (bytes > 0) {
|
||||||
|
char *p = buf + pos;
|
||||||
|
struct inotify_event *event = (struct inotify_event *)p;
|
||||||
|
int watchfd = 0;
|
||||||
|
uint32_t mask = event->mask & (IN_CREATE | IN_DELETE |
|
||||||
|
IN_MODIFY | IN_IGNORED);
|
||||||
|
MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd);
|
||||||
|
MTPMonEntry *entry = NULL;
|
||||||
|
MTPObject *o;
|
||||||
|
|
||||||
|
pos = pos + sizeof(struct inotify_event) + event->len;
|
||||||
|
bytes = bytes - pos;
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IN_CREATE:
|
||||||
|
if (usb_mtp_object_lookup_name
|
||||||
|
(parent, event->name, event->len)) {
|
||||||
|
/* Duplicate create event */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = g_new0(MTPMonEntry, 1);
|
||||||
|
entry->handle = s->next_handle;
|
||||||
|
entry->event = EVT_OBJ_ADDED;
|
||||||
|
o = usb_mtp_add_child(s, parent, event->name);
|
||||||
|
if (!o) {
|
||||||
|
g_free(entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
o->watchfd = watchfd;
|
||||||
|
trace_usb_mtp_inotify_event(s->dev.addr, event->name,
|
||||||
|
event->mask, "Obj Added");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IN_DELETE:
|
||||||
|
/*
|
||||||
|
* The kernel issues a IN_IGNORED event
|
||||||
|
* when a dir containing a watchpoint is
|
||||||
|
* deleted, so we don't have to delete the
|
||||||
|
* watchpoint
|
||||||
|
*/
|
||||||
|
o = usb_mtp_object_lookup_name(parent, event->name, event->len);
|
||||||
|
if (!o) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = g_new0(MTPMonEntry, 1);
|
||||||
|
entry->handle = o->handle;
|
||||||
|
entry->event = EVT_OBJ_REMOVED;
|
||||||
|
usb_mtp_object_free(s, o);
|
||||||
|
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||||
|
event->mask, "Obj Deleted");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IN_MODIFY:
|
||||||
|
o = usb_mtp_object_lookup_name(parent, event->name, event->len);
|
||||||
|
if (!o) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = g_new0(MTPMonEntry, 1);
|
||||||
|
entry->handle = o->handle;
|
||||||
|
entry->event = EVT_OBJ_INFO_CHANGED;
|
||||||
|
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||||
|
event->mask, "Obj Modified");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IN_IGNORED:
|
||||||
|
o = usb_mtp_object_lookup_name(parent, event->name, event->len);
|
||||||
|
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||||
|
event->mask, "Obj ignored");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
QTAILQ_INSERT_HEAD(&s->events, entry, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_mtp_inotify_init(MTPState *s)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = inotify_init1(IN_NONBLOCK);
|
||||||
|
if (fd == -1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTAILQ_INIT(&s->events);
|
||||||
|
s->inotifyfd = fd;
|
||||||
|
|
||||||
|
qemu_set_fd_handler(fd, inotify_watchfn, NULL, s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_mtp_inotify_cleanup(MTPState *s)
|
||||||
|
{
|
||||||
|
MTPMonEntry *e;
|
||||||
|
|
||||||
|
if (!s->inotifyfd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s);
|
||||||
|
close(s->inotifyfd);
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(e, &s->events, next) {
|
||||||
|
QTAILQ_REMOVE(&s->events, e, next);
|
||||||
|
g_free(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_mtp_add_watch(int inotifyfd, char *path)
|
||||||
|
{
|
||||||
|
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY |
|
||||||
|
IN_ISDIR;
|
||||||
|
|
||||||
|
return inotify_add_watch(inotifyfd, path, mask);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
|
static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
|
||||||
{
|
{
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
@ -386,6 +594,16 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
|
|||||||
if (!dir) {
|
if (!dir) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef __linux__
|
||||||
|
int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
|
||||||
|
if (watchfd == -1) {
|
||||||
|
fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path);
|
||||||
|
} else {
|
||||||
|
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||||
|
0, "Watch Added");
|
||||||
|
o->watchfd = watchfd;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
usb_mtp_add_child(s, o, entry->d_name);
|
usb_mtp_add_child(s, o, entry->d_name);
|
||||||
}
|
}
|
||||||
@ -778,11 +996,19 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
|
|||||||
trace_usb_mtp_op_open_session(s->dev.addr);
|
trace_usb_mtp_op_open_session(s->dev.addr);
|
||||||
s->session = c->argv[0];
|
s->session = c->argv[0];
|
||||||
usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
|
usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
|
||||||
|
#ifdef __linux__
|
||||||
|
if (usb_mtp_inotify_init(s)) {
|
||||||
|
fprintf(stderr, "usb-mtp: file monitoring init failed\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case CMD_CLOSE_SESSION:
|
case CMD_CLOSE_SESSION:
|
||||||
trace_usb_mtp_op_close_session(s->dev.addr);
|
trace_usb_mtp_op_close_session(s->dev.addr);
|
||||||
s->session = 0;
|
s->session = 0;
|
||||||
s->next_handle = 0;
|
s->next_handle = 0;
|
||||||
|
#ifdef __linux__
|
||||||
|
usb_mtp_inotify_cleanup(s);
|
||||||
|
#endif
|
||||||
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
|
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
|
||||||
assert(QTAILQ_EMPTY(&s->objects));
|
assert(QTAILQ_EMPTY(&s->objects));
|
||||||
break;
|
break;
|
||||||
@ -908,6 +1134,9 @@ static void usb_mtp_handle_reset(USBDevice *dev)
|
|||||||
|
|
||||||
trace_usb_mtp_reset(s->dev.addr);
|
trace_usb_mtp_reset(s->dev.addr);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
usb_mtp_inotify_cleanup(s);
|
||||||
|
#endif
|
||||||
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
|
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
|
||||||
s->session = 0;
|
s->session = 0;
|
||||||
usb_mtp_data_free(s->data_in);
|
usb_mtp_data_free(s->data_in);
|
||||||
|
@ -553,6 +553,7 @@ usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x"
|
|||||||
usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
|
usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
|
||||||
usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
|
usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
|
||||||
usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
|
usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
|
||||||
|
usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s"
|
||||||
|
|
||||||
# hw/usb/host-libusb.c
|
# hw/usb/host-libusb.c
|
||||||
usb_host_open_started(int bus, int addr) "dev %d:%d"
|
usb_host_open_started(int bus, int addr) "dev %d:%d"
|
||||||
|
Loading…
Reference in New Issue
Block a user