server: Return multiple events in read_changes.
diff --git a/server/change.c b/server/change.c
index 5eb1df6..e448c13 100644
--- a/server/change.c
+++ b/server/change.c
@@ -123,9 +123,7 @@
 
 struct change_record {
     struct list entry;
-    int action;
-    int len;
-    char name[1];
+    struct filesystem_event event;
 };
 
 struct dir
@@ -616,13 +614,13 @@
     if (dir->want_data)
     {
         size_t len = strlen(relpath);
-        record = malloc( offsetof(struct change_record, name[len]) );
+        record = malloc( offsetof(struct change_record, event.name[len]) );
         if (!record)
             return;
 
-        record->action = action;
-        memcpy( record->name, relpath, len );
-        record->len = len;
+        record->event.action = action;
+        memcpy( record->event.name, relpath, len );
+        record->event.len = len;
 
         list_add_tail( &dir->change_records, &record->entry );
     }
@@ -1145,21 +1143,54 @@
 
 DECL_HANDLER(read_change)
 {
-    struct change_record *record;
+    struct change_record *record, *next;
     struct dir *dir;
+    struct list events;
+    char *data, *event;
+    int size = 0;
 
     dir = get_dir_obj( current->process, req->handle, 0 );
     if (!dir)
         return;
 
-    if ((record = get_first_change_record( dir )) != NULL)
+    list_init( &events );
+    list_move_tail( &events, &dir->change_records );
+    release_object( dir );
+
+    if (list_empty( &events ))
     {
-        reply->action = record->action;
-        set_reply_data( record->name, record->len );
+        set_error( STATUS_NO_DATA_DETECTED );
+        return;
+    }
+
+    LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
+    {
+        size += (offsetof(struct filesystem_event, name[record->event.len])
+                + sizeof(int)-1) / sizeof(int) * sizeof(int);
+    }
+
+    if (size > get_reply_max_size())
+        set_error( STATUS_BUFFER_TOO_SMALL );
+    else if ((data = mem_alloc( size )) != NULL)
+    {
+        event = data;
+        LIST_FOR_EACH_ENTRY( record, &events, struct change_record, entry )
+        {
+            data_size_t len = offsetof( struct filesystem_event, name[record->event.len] );
+            memcpy( event, &record->event, len );
+            event += len;
+            if (len % sizeof(int))
+            {
+                memset( event, 0, sizeof(int) - len % sizeof(int) );
+                event += sizeof(int) - len % sizeof(int);
+            }
+        }
+        set_reply_data_ptr( data, size );
+    }
+
+    LIST_FOR_EACH_ENTRY_SAFE( record, next, &events, struct change_record, entry )
+    {
+        list_remove( &record->entry );
         free( record );
     }
-    else
-        set_error( STATUS_NO_DATA_DETECTED );
-
-    release_object( dir );
 }
diff --git a/server/protocol.def b/server/protocol.def
index 6245f4c..728898f 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -332,6 +332,14 @@
     unsigned short attr;
 } char_info_t;
 
+/* structure returned in filesystem events */
+struct filesystem_event
+{
+    int         action;
+    data_size_t len;
+    char        name[1];
+};
+
 typedef struct
 {
     unsigned int low_part;
@@ -1448,8 +1456,7 @@
 @REQ(read_change)
     obj_handle_t handle;
 @REPLY
-    int          action;        /* type of change */
-    VARARG(name,string);        /* name of directory entry that changed */
+    VARARG(events,filesystem_event);  /* collected filesystem events */
 @END
 
 
diff --git a/server/request.h b/server/request.h
index e0bffaf..ae45ab8 100644
--- a/server/request.h
+++ b/server/request.h
@@ -1103,8 +1103,7 @@
 C_ASSERT( sizeof(struct read_directory_changes_request) == 64 );
 C_ASSERT( FIELD_OFFSET(struct read_change_request, handle) == 12 );
 C_ASSERT( sizeof(struct read_change_request) == 16 );
-C_ASSERT( FIELD_OFFSET(struct read_change_reply, action) == 8 );
-C_ASSERT( sizeof(struct read_change_reply) == 16 );
+C_ASSERT( sizeof(struct read_change_reply) == 8 );
 C_ASSERT( FIELD_OFFSET(struct create_mapping_request, access) == 12 );
 C_ASSERT( FIELD_OFFSET(struct create_mapping_request, attributes) == 16 );
 C_ASSERT( FIELD_OFFSET(struct create_mapping_request, protect) == 20 );
diff --git a/server/trace.c b/server/trace.c
index 83d5b16..d083d2d 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -991,6 +991,24 @@
     fputc( '}', stderr );
 }
 
+static void dump_varargs_filesystem_event( const char *prefix, data_size_t size )
+{
+    fprintf( stderr,"%s{", prefix );
+    while (size)
+    {
+        const struct filesystem_event *event = cur_data;
+        data_size_t len = (offsetof( struct filesystem_event, name[event->len] ) + sizeof(int)-1)
+                           / sizeof(int) * sizeof(int);
+        if (size < len) break;
+        fprintf( stderr, "{action=%x,len=%u,name=\"%.*s\"}",
+                 event->action, event->len, event->len, event->name );
+        size -= len;
+        remove_data( len );
+        if (size)fputc( ',', stderr );
+    }
+    fputc( '}', stderr );
+}
+
 typedef void (*dump_func)( const void *req );
 
 /* Everything below this line is generated automatically by tools/make_requests */
@@ -1865,8 +1883,7 @@
 
 static void dump_read_change_reply( const struct read_change_reply *req )
 {
-    fprintf( stderr, " action=%d", req->action );
-    dump_varargs_string( ", name=", cur_size );
+    dump_varargs_filesystem_event( " events=", cur_size );
 }
 
 static void dump_create_mapping_request( const struct create_mapping_request *req )