diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index 711a0b0ad5e..cc4e2875d06 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -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; ilen; i++) + if (event->name[i] == '/') + event->name[i] = '\\'; - /* convert to an NT style path */ - for (i=0; iAction = 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; - len = ntdll_umbstowcs( 0, path, len, pfni->FileName, - info->BufferSize - sizeof (*pfni) ); + if(pfni->FileNameLength == -1 || pfni->FileNameLength == -2) + break; - pfni->NextEntryOffset = 0; - pfni->Action = action; - pfni->FileNameLength = len * sizeof (WCHAR); - pfni->FileName[len] = 0; - len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength; + 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; + } + + 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; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 72f3cde259c..2e940b776a2 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -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 */ diff --git a/server/change.c b/server/change.c index 5eb1df6289c..e448c13a16b 100644 --- a/server/change.c +++ b/server/change.c @@ -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 ); + 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 6245f4cb2ff..728898f84be 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -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 diff --git a/server/request.h b/server/request.h index e0bffaf501c..ae45ab84b48 100644 --- a/server/request.h +++ b/server/request.h @@ -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 ); diff --git a/server/trace.c b/server/trace.c index 83d5b16387f..d083d2d90bc 100644 --- a/server/trace.c +++ b/server/trace.c @@ -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 )