diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index 37dfa13..79d4ab0 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -15,9 +15,11 @@
 
 #include <sys/stat.h>
 #include <sys/statvfs.h>
+#include <sys/inotify.h>
 
 #include "qemu-common.h"
 #include "qemu/iov.h"
+#include "qemu/main-loop.h"
 #include "trace.h"
 #include "hw/usb.h"
 #include "hw/usb/desc.h"
@@ -77,6 +79,7 @@ typedef struct MTPState MTPState;
 typedef struct MTPControl MTPControl;
 typedef struct MTPData MTPData;
 typedef struct MTPObject MTPObject;
+typedef struct MTPMonEntry MTPMonEntry;
 
 enum {
     EP_DATA_IN = 1,
@@ -84,6 +87,19 @@ enum {
     EP_EVENT,
 };
 
+struct MTPMonEntry {
+    uint32_t event;
+    uint32_t handle;
+
+    QTAILQ_ENTRY(MTPMonEntry) next;
+};
+
+enum inotify_event_type {
+    CREATE = 1,
+    DELETE = 2,
+    MODIFY = 3,
+};
+
 struct MTPControl {
     uint16_t     code;
     uint32_t     trans;
@@ -108,6 +124,8 @@ struct MTPObject {
     char         *name;
     char         *path;
     struct stat  stat;
+    /* inotify watch cookie */
+    int          watchfd;
     MTPObject    *parent;
     uint32_t     nchildren;
     QLIST_HEAD(, MTPObject) children;
@@ -121,6 +139,8 @@ struct MTPState {
     char         *root;
     char         *desc;
     uint32_t     flags;
+    /* inotify descriptor */
+    int          inotifyfd;
 
     MTPData      *data_in;
     MTPData      *data_out;
@@ -129,6 +149,7 @@ struct MTPState {
     uint32_t     next_handle;
 
     QTAILQ_HEAD(, MTPObject) objects;
+    QTAILQ_HEAD(events, MTPMonEntry) events;
 };
 
 #define TYPE_USB_MTP "usb-mtp"
@@ -270,6 +291,40 @@ static const USBDesc desc = {
 };
 
 /* ----------------------------------------------------------------------- */
+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 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);
+}
 
 static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle,
                                        MTPObject *parent, char *name)
@@ -316,6 +371,46 @@ ignore:
     return NULL;
 }
 
+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_inotify_mon(MTPState *s, MTPObject *o)
+{
+    int watchfd;
+
+    if (!s->inotifyfd) {
+        return 0;
+    }
+    assert(o->format == FMT_ASSOCIATION);
+    if (o->watchfd > 0) {
+        /* already watching */
+        return 0;
+    }
+
+    watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
+    if (watchfd == -1) {
+        return 1;
+    }
+
+    o->watchfd = watchfd;
+    trace_usb_mtp_mon(s->dev.addr, o->path, watchfd);
+
+    return 0;
+}
 static void usb_mtp_object_free(MTPState *s, MTPObject *o)
 {
     MTPObject *iter;
@@ -370,6 +465,149 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
     return child;
 }
 
+static void inotify_watchfn(void *arg)
+{
+    MTPState *s = arg;
+    ssize_t bytes;
+    int error = 0;
+    /* From the man page: atleast one event can be read */
+    int len = sizeof(struct inotify_event) + NAME_MAX + 1;
+    char buf[len];
+
+    for (;;) {
+        char *p;
+        bytes = read(s->inotifyfd, buf, len);
+
+        if (bytes <= 0) {
+            /* Better luck next time */
+            goto done;
+        }
+
+        /*
+         * TODO: Ignore initiator initiated events.
+         * For now we are good because the store is RO
+         */
+        for (p = buf; p < buf + bytes;) {
+            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;
+            char *name, *path;
+
+            /*
+             * TODO: Complain even on the slightest hint that
+             * something has gone wrong. Eventually, it makes
+             * sense to process remaining events, if any.
+             */
+            if (!parent) {
+                error = 1;
+                goto done;
+            }
+
+            switch (mask) {
+            case IN_CREATE:
+                if (event->mask & IN_ISDIR) {
+                    /* Add a new watch asap so as to not lose events */
+                    name = g_strndup(event->name, event->len);
+                    path = g_strdup_printf("%s/%s", parent->path, name);
+
+                    watchfd = usb_mtp_add_watch(s->inotifyfd, path);
+                    g_free(path);
+                    g_free(name);
+
+                    if (watchfd == -1) {
+                        error = 1;
+                        goto done;
+                    }
+                }
+
+                entry = g_new0(MTPMonEntry, 1);
+                entry->handle = s->next_handle;
+                entry->event = CREATE;
+                o = usb_mtp_add_child(s, parent, event->name);
+                if (!o) {
+                    error = 1;
+                    g_free(entry);
+                    goto done;
+                }
+                o->watchfd = watchfd;
+                trace_usb_mtp_inotify_event(s->dev.addr, path,
+                                      event->mask, "Obj Added");
+                break;
+
+            case IN_DELETE:
+                /*
+                 * The kernel issues a IN_IGNORED event
+                 * when a dir containing a watchpoint is
+                 * deleted
+                 */
+                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
+                if (!o) {
+                    error = 1;
+                    goto done;
+                }
+                entry = g_new0(MTPMonEntry, 1);
+                entry->handle = o->handle;
+                entry->event = DELETE;
+                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) {
+                    error = 1;
+                    goto done;
+                }
+                entry = g_new0(MTPMonEntry, 1);
+                entry->handle = o->handle;
+                entry->event = MODIFY;
+                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:
+                error = 1;
+                goto done;
+            }
+
+            if (entry) {
+                QTAILQ_INSERT_HEAD(&s->events, entry, next);
+            }
+            p += sizeof(struct inotify_event) + event->len;
+        }
+    }
+done:
+    if (error) {
+        fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
+    }
+}
+
+static int usb_mtp_inotify_init(MTPState *s)
+{
+    int 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_object_readdir(MTPState *s, MTPObject *o)
 {
     struct dirent *entry;
@@ -639,11 +877,11 @@ static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c,
 {
     MTPData *d = usb_mtp_data_alloc(c);
     uint32_t i = 0, handles[o->nchildren];
-    MTPObject *iter;
+    MTPObject *iter, *next;
 
     trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path);
 
-    QLIST_FOREACH(iter, &o->children, list) {
+    QLIST_FOREACH_SAFE(iter, &o->children, list, next) {
         handles[i++] = iter->handle;
     }
     assert(i == o->nchildren);
@@ -777,11 +1015,15 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
         trace_usb_mtp_op_open_session(s->dev.addr);
         s->session = c->argv[0];
         usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
+        if (usb_mtp_inotify_init(s)) {
+            fprintf(stderr, "usb-mtp: file monitoring init failed\n");
+        }
         break;
     case CMD_CLOSE_SESSION:
         trace_usb_mtp_op_close_session(s->dev.addr);
         s->session = 0;
         s->next_handle = 0;
+        usb_mtp_inotify_cleanup(s);
         usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
         assert(QTAILQ_EMPTY(&s->objects));
         break;
@@ -827,6 +1069,9 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
             return;
         }
         usb_mtp_object_readdir(s, o);
+        if (usb_mtp_inotify_mon(s, o)) {
+            fprintf(stderr, "usb-mtp: adding watch for %s failed\n", o->path);
+        }
         if (c->code == CMD_GET_NUM_OBJECTS) {
             trace_usb_mtp_op_get_num_objects(s->dev.addr, o->handle, o->path);
             nres = 1;
@@ -907,6 +1152,7 @@ static void usb_mtp_handle_reset(USBDevice *dev)
 
     trace_usb_mtp_reset(s->dev.addr);
 
+    usb_mtp_inotify_cleanup(s);
     usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
     s->session = 0;
     usb_mtp_data_free(s->data_in);
diff --git a/trace-events b/trace-events
index ba4473d..84c80fa 100644
--- a/trace-events
+++ b/trace-events
@@ -553,6 +553,9 @@ 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_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_inotify_mon(int dev, const char *path, int fd) "dev %d, path %s watchfd %d"
+usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s"
+usb_mtp_mon(int dev, const char *path, int watchfd) "dev %d path %s watchfd %d"
 
 # hw/usb/host-libusb.c
 usb_host_open_started(int bus, int addr) "dev %d:%d"
