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;
}
unsigned short attr;
} char_info_t;
+
+struct filesystem_event
+{
+ int action;
+ data_size_t len;
+ char name[1];
+};
+
typedef struct
{
unsigned int low_part;
struct read_change_reply
{
struct reply_header __header;
- int action;
- /* VARARG(name,string); */
- char __pad_12[4];
+ /* VARARG(events,filesystem_event); */
};
struct set_cursor_reply set_cursor_reply;
};
-#define SERVER_PROTOCOL_VERSION 411
+#define SERVER_PROTOCOL_VERSION 412
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
struct change_record {
struct list entry;
- int action;
- int len;
- char name[1];
+ struct filesystem_event event;
};
struct dir
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 );
}
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 );
+ }
}
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;
@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
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 );
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 */
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 )