server: Return multiple events in read_changes.
authorPiotr Caban <piotr@codeweavers.com>
Tue, 18 Jan 2011 10:24:33 +0000 (11:24 +0100)
committerAlexandre Julliard <julliard@winehq.org>
Wed, 19 Jan 2011 13:13:33 +0000 (14:13 +0100)
dlls/ntdll/directory.c
include/wine/server_protocol.h
server/change.c
server/protocol.def
server/request.h
server/trace.c

index 711a0b0ad5e143f3ef306602b67c95d31de30e0b..cc4e2875d06a251bcf5886df63577486a610558b 100644 (file)
@@ -2902,49 +2902,72 @@ static void WINAPI read_changes_user_apc( void *arg, IO_STATUS_BLOCK *io, ULONG
 static NTSTATUS read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, NTSTATUS status, void **apc )
 {
     struct read_changes_info *info = user;
-    char path[PATH_MAX];
-    NTSTATUS ret = STATUS_SUCCESS;
-    int len, action, i;
+    char data[PATH_MAX];
+    NTSTATUS ret;
+    int size;
 
     SERVER_START_REQ( read_change )
     {
         req->handle = wine_server_obj_handle( info->FileHandle );
-        wine_server_set_reply( req, path, PATH_MAX );
+        wine_server_set_reply( req, data, PATH_MAX );
         ret = wine_server_call( req );
-        action = reply->action;
-        len = wine_server_reply_size( reply );
+        size = wine_server_reply_size( reply );
     }
     SERVER_END_REQ;
 
-    if (ret == STATUS_SUCCESS && info->Buffer && 
-        (info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
+    if (ret == STATUS_SUCCESS && info->Buffer)
     {
-        PFILE_NOTIFY_INFORMATION pfni;
+        PFILE_NOTIFY_INFORMATION pfni = info->Buffer;
+        int i, left = info->BufferSize;
+        DWORD *last_entry_offset = NULL;
+        struct filesystem_event *event = (struct filesystem_event*)data;
 
-        pfni = info->Buffer;
+        while (size && left >= sizeof(*pfni))
+        {
+            /* convert to an NT style path */
+            for (i=0; i<event->len; i++)
+                if (event->name[i] == '/')
+                    event->name[i] = '\\';
+
+            pfni->Action = event->action;
+            pfni->FileNameLength = ntdll_umbstowcs( 0, event->name, event->len, pfni->FileName,
+                    (left - offsetof(FILE_NOTIFY_INFORMATION, FileName)) / sizeof(WCHAR));
+            last_entry_offset = &pfni->NextEntryOffset;
 
-        /* convert to an NT style path */
-        for (i=0; i<len; i++)
-            if (path[i] == '/')
-                path[i] = '\\';
+            if(pfni->FileNameLength == -1 || pfni->FileNameLength == -2)
+                break;
 
-        len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
-                               info->BufferSize - sizeof (*pfni) );
+            i = offsetof(FILE_NOTIFY_INFORMATION, FileName[pfni->FileNameLength]);
+            pfni->FileNameLength *= sizeof(WCHAR);
+            pfni->NextEntryOffset = i;
+            pfni = (FILE_NOTIFY_INFORMATION*)((char*)pfni + i);
+            left -= i;
+
+            i = (offsetof(struct filesystem_event, name[event->len])
+                    + sizeof(int)-1) / sizeof(int) * sizeof(int);
+            event = (struct filesystem_event*)((char*)event + i);
+            size -= i;
+        }
 
-        pfni->NextEntryOffset = 0;
-        pfni->Action = action;
-        pfni->FileNameLength = len * sizeof (WCHAR);
-        pfni->FileName[len] = 0;
-        len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
+        if (size)
+        {
+            ret = STATUS_NOTIFY_ENUM_DIR;
+            size = 0;
+        }
+        else
+        {
+            *last_entry_offset = 0;
+            size = info->BufferSize - left;
+        }
     }
     else
     {
         ret = STATUS_NOTIFY_ENUM_DIR;
-        len = 0;
+        size = 0;
     }
 
     iosb->u.Status = ret;
-    iosb->Information = len;
+    iosb->Information = size;
     *apc = read_changes_user_apc;
     return ret;
 }
index 72f3cde259c6ca6e444d0abed6b41caaf66628c8..2e940b776a21e55f9081d109b81a6b325f63a948 100644 (file)
@@ -316,6 +316,14 @@ typedef struct
     unsigned short attr;
 } char_info_t;
 
+
+struct filesystem_event
+{
+    int         action;
+    data_size_t len;
+    char        name[1];
+};
+
 typedef struct
 {
     unsigned int low_part;
@@ -1879,9 +1887,7 @@ struct read_change_request
 struct read_change_reply
 {
     struct reply_header __header;
-    int          action;
-    /* VARARG(name,string); */
-    char __pad_12[4];
+    /* VARARG(events,filesystem_event); */
 };
 
 
@@ -5519,6 +5525,6 @@ union generic_reply
     struct set_cursor_reply set_cursor_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 411
+#define SERVER_PROTOCOL_VERSION 412
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
index 5eb1df6289c51bf761b7b73106473d418a2cdba2..e448c13a16b666f0d47778db476121161715d30a 100644 (file)
@@ -123,9 +123,7 @@ static struct fd *inotify_fd;
 
 struct change_record {
     struct list entry;
-    int action;
-    int len;
-    char name[1];
+    struct filesystem_event event;
 };
 
 struct dir
@@ -616,13 +614,13 @@ static void inotify_do_change_notify( struct dir *dir, unsigned int action,
     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 @@ end:
 
 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 );
-        free( record );
-    }
-    else
         set_error( STATUS_NO_DATA_DETECTED );
+        return;
+    }
 
-    release_object( dir );
+    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 );
+    }
 }
index 6245f4cb2ff2f1c7b1212db99d76cb14426e601a..728898f84be565d4ae0b91ccbd9651fcd146df3b 100644 (file)
@@ -332,6 +332,14 @@ typedef struct
     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 @@ enum char_info_mode
 @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
 
 
index e0bffaf501c35d808397f247f3225018cc8f7c23..ae45ab84b48ca8f946cf7c985856a6abe79b4db4 100644 (file)
@@ -1103,8 +1103,7 @@ C_ASSERT( FIELD_OFFSET(struct read_directory_changes_request, async) == 24 );
 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 );
index 83d5b16387f5e49ca8c64a59bc5e6d3c2bc702a6..d083d2d90bcf20e1324ca2dcd78ed82b1c203811 100644 (file)
@@ -991,6 +991,24 @@ static void dump_varargs_object_attributes( const char *prefix, data_size_t size
     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_request( const struct read_change_request *req )
 
 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 )