server: Fill in NtNotifyChangeDirectoryFile's buffer with change data.
diff --git a/server/change.c b/server/change.c
index 7ec4c0b..d6213fe 100644
--- a/server/change.c
+++ b/server/change.c
@@ -126,6 +126,13 @@
 
 #endif
 
+struct change_record {
+    struct list entry;
+    int action;
+    int len;
+    char name[1];
+};
+
 struct dir
 {
     struct object  obj;      /* object header */
@@ -137,6 +144,8 @@
     long           signaled; /* the file changed */
     struct fd     *inotify_fd; /* inotify file descriptor */
     int            wd;       /* inotify watch descriptor */
+    struct list    change_q; /* change readers */
+    struct list    change_records;   /* data for the change */
 };
 
 static struct fd *dir_get_fd( struct object *obj );
@@ -163,6 +172,7 @@
 
 static int dir_get_poll_events( struct fd *fd );
 static int dir_get_info( struct fd *fd );
+static void dir_cancel_async( struct fd *fd );
 
 static const struct fd_ops dir_fd_ops =
 {
@@ -171,7 +181,7 @@
     no_flush,                 /* flush */
     dir_get_info,             /* get_file_info */
     default_fd_queue_async,   /* queue_async */
-    default_fd_cancel_async   /* cancel_async */
+    dir_cancel_async          /* cancel_async */
 };
 
 static struct list change_list = LIST_INIT(change_list);
@@ -238,6 +248,8 @@
     if (!dir)
         return NULL;
 
+    list_init( &dir->change_q );
+    list_init( &dir->change_records );
     dir->event = NULL;
     dir->filter = 0;
     dir->notified = 0;
@@ -321,8 +333,17 @@
     return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
 }
 
+static struct change_record *get_first_change_record( struct dir *dir )
+{
+    struct list *ptr = list_head( &dir->change_records );
+    if (!ptr) return NULL;
+    list_remove( ptr );
+    return LIST_ENTRY( ptr, struct change_record, entry );
+}
+
 static void dir_destroy( struct object *obj )
 {
+    struct change_record *record;
     struct dir *dir = (struct dir *)obj;
     assert (obj->ops == &dir_ops);
 
@@ -332,6 +353,9 @@
     if (dir->inotify_fd)
         release_object( dir->inotify_fd );
 
+    async_terminate_queue( &dir->change_q, STATUS_CANCELLED );
+    while ((record = get_first_change_record( dir ))) free( record );
+
     if (dir->event)
     {
         set_event( dir->event );
@@ -356,6 +380,12 @@
     return 0;
 }
 
+static void dir_cancel_async( struct fd *fd )
+{
+    struct dir *dir = (struct dir *) get_fd_user( fd );
+    async_terminate_queue( &dir->change_q, STATUS_CANCELLED );
+}
+
 
 #ifdef USE_INOTIFY
 
@@ -378,10 +408,32 @@
     return POLLIN;
 }
 
-static void inotify_do_change_notify( struct dir *dir )
+static void inotify_do_change_notify( struct dir *dir, struct inotify_event *ie )
 {
-    dir->signaled++;
-    dir_signal_changed( dir );
+    struct change_record *record;
+
+    record = malloc( sizeof (*record) + ie->len - 1 ) ;
+    if (!record)
+        return;
+
+    if( ie->mask & IN_CREATE )
+        record->action = FILE_ACTION_ADDED;
+    else if( ie->mask & IN_DELETE )
+        record->action = FILE_ACTION_REMOVED;
+    else
+        record->action = FILE_ACTION_MODIFIED;
+    memcpy( record->name, ie->name, ie->len );
+    record->len = strlen( ie->name );
+
+    list_add_tail( &dir->change_records, &record->entry );
+
+    if (!list_empty( &dir->change_q ))
+        async_terminate_head( &dir->change_q, STATUS_ALERTED );
+    else
+    {
+        dir->signaled++;
+        dir_signal_changed( dir );
+    }
 }
 
 static void inotify_poll_event( struct fd *fd, int event )
@@ -404,7 +456,7 @@
         ie = (struct inotify_event*) &buffer[ofs];
         if (!ie->len)
             break;
-        inotify_do_change_notify( dir );
+        inotify_do_change_notify( dir, ie );
         ofs += (sizeof (*ie) + ie->len - 1);
     }
 }
@@ -489,6 +541,11 @@
     if (dir->event) release_object( dir->event );
     dir->event = event;
 
+    /* requests don't timeout */
+    if ( req->io_apc && !create_async( current, NULL, &dir->change_q,
+                        req->io_apc, req->io_user, req->io_sb ))
+        return;
+
     /* assign it once */
     if (!dir->filter)
     {
@@ -511,3 +568,28 @@
 end:
     release_object( dir );
 }
+
+DECL_HANDLER(read_change)
+{
+    struct change_record *record;
+    struct dir *dir;
+
+    dir = get_dir_obj( current->process, req->handle, 0 );
+    if (!dir)
+        return;
+
+    if ((record = get_first_change_record( dir )) != NULL)
+    {
+        reply->action = record->action;
+        set_reply_data( record->name, record->len );
+        free( record );
+    }
+    else
+        set_error( STATUS_NO_DATA_DETECTED );
+
+    /* now signal it */
+    dir->signaled++;
+    dir_signal_changed( dir );
+
+    release_object( dir );
+}