Commit 01932119 authored by Mike McCormack's avatar Mike McCormack Committed by Alexandre Julliard

server: Fill in NtNotifyChangeDirectoryFile's buffer with change data.

parent acb52e52
...@@ -136,33 +136,46 @@ BOOL WINAPI ReadDirectoryChangesW( HANDLE handle, LPVOID buffer, DWORD len, BOOL ...@@ -136,33 +136,46 @@ BOOL WINAPI ReadDirectoryChangesW( HANDLE handle, LPVOID buffer, DWORD len, BOOL
DWORD filter, LPDWORD returned, LPOVERLAPPED overlapped, DWORD filter, LPDWORD returned, LPOVERLAPPED overlapped,
LPOVERLAPPED_COMPLETION_ROUTINE completion ) LPOVERLAPPED_COMPLETION_ROUTINE completion )
{ {
IO_STATUS_BLOCK io; OVERLAPPED ov, *pov;
IO_STATUS_BLOCK *ios;
NTSTATUS status; NTSTATUS status;
BOOL ret = TRUE; BOOL ret = TRUE;
HANDLE event;
TRACE("%p %p %08lx %d %08lx %p %p %p\n", handle, buffer, len, subtree, filter, TRACE("%p %p %08lx %d %08lx %p %p %p\n", handle, buffer, len, subtree, filter,
returned, overlapped, completion ); returned, overlapped, completion );
if (overlapped) if (!overlapped)
event = overlapped->hEvent; {
memset( &ov, 0, sizeof ov );
ov.hEvent = CreateEventW( NULL, 0, 0, NULL );
pov = &ov;
}
else else
event = CreateEventW( NULL, 0, 0, NULL ); pov = overlapped;
status = NtNotifyChangeDirectoryFile( handle, event, NULL, NULL, ios = (PIO_STATUS_BLOCK) pov;
&io, buffer, len, filter, subtree ); ios->Status = STATUS_PENDING;
if (status != STATUS_PENDING) ios->Information = 0;
status = NtNotifyChangeDirectoryFile( handle, pov->hEvent, NULL, NULL,
ios, buffer, len, filter, subtree );
if (status == STATUS_PENDING)
{
if (overlapped)
return TRUE;
WaitForSingleObjectEx( ov.hEvent, INFINITE, TRUE );
CloseHandle( ov.hEvent );
if (returned)
*returned = ios->Information;
status = ios->Status;
}
if (status != STATUS_SUCCESS)
{ {
SetLastError( RtlNtStatusToDosError(status) ); SetLastError( RtlNtStatusToDosError(status) );
ret = FALSE; ret = FALSE;
} }
else if (!overlapped)
WaitForSingleObject( event, INFINITE );
else
overlapped->Internal = STATUS_PENDING;
if (!overlapped)
CloseHandle( event );
return ret; return ret;
} }
...@@ -1778,6 +1778,75 @@ done: ...@@ -1778,6 +1778,75 @@ done:
return status; return status;
} }
struct read_changes_info
{
HANDLE FileHandle;
HANDLE Event;
PIO_APC_ROUTINE ApcRoutine;
PVOID ApcContext;
PVOID Buffer;
ULONG BufferSize;
};
static void WINAPI read_changes_apc( void *user, PIO_STATUS_BLOCK iosb, ULONG status )
{
struct read_changes_info *info = user;
char path[PATH_MAX];
NTSTATUS ret = STATUS_SUCCESS;
int len, action;
TRACE("%p %p %p %08lx\n", info, info->ApcContext, iosb, status);
/*
* FIXME: race me!
*
* hEvent/hDir is set before the output buffer and iosb is updated.
* Since the thread that called NtNotifyChangeDirectoryFile is usually
* waiting, we'll be safe since we're called in that thread's context.
* If a different thread is waiting on our hEvent/hDir we're going to be
* in trouble...
*/
SERVER_START_REQ( read_change )
{
req->handle = info->FileHandle;
wine_server_set_reply( req, path, PATH_MAX );
ret = wine_server_call( req );
action = reply->action;
len = wine_server_reply_size( reply );
}
SERVER_END_REQ;
if (ret == STATUS_SUCCESS && info->Buffer &&
(info->BufferSize > (sizeof (FILE_NOTIFY_INFORMATION) + len*sizeof(WCHAR))))
{
PFILE_NOTIFY_INFORMATION pfni;
pfni = (PFILE_NOTIFY_INFORMATION) info->Buffer;
len = ntdll_umbstowcs( 0, path, len, pfni->FileName,
info->BufferSize - sizeof (*pfni) );
pfni->NextEntryOffset = 0;
pfni->Action = action;
pfni->FileNameLength = len * sizeof (WCHAR);
pfni->FileName[len] = 0;
TRACE("action = %ld name = %s\n", pfni->Action,
debugstr_w(pfni->FileName) );
len = sizeof (*pfni) - sizeof (DWORD) + pfni->FileNameLength;
}
else
{
ret = STATUS_NOTIFY_ENUM_DIR;
len = 0;
}
iosb->u.Status = ret;
iosb->Information = len;
RtlFreeHeap( GetProcessHeap(), 0, info );
}
#define FILE_NOTIFY_ALL ( \ #define FILE_NOTIFY_ALL ( \
FILE_NOTIFY_CHANGE_FILE_NAME | \ FILE_NOTIFY_CHANGE_FILE_NAME | \
FILE_NOTIFY_CHANGE_DIR_NAME | \ FILE_NOTIFY_CHANGE_DIR_NAME | \
...@@ -1797,6 +1866,7 @@ NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event, ...@@ -1797,6 +1866,7 @@ NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree ) ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
{ {
struct read_changes_info *info;
NTSTATUS status; NTSTATUS status;
TRACE("%p %p %p %p %p %p %lu %lu %d\n", TRACE("%p %p %p %p %p %p %lu %lu %d\n",
...@@ -1809,18 +1879,35 @@ NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event, ...@@ -1809,18 +1879,35 @@ NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL)) if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
if (ApcRoutine || ApcContext || Buffer || BufferSize || WatchTree) if (WatchTree || ApcRoutine)
FIXME("parameters ignored %p %p %p %lu %d\n", FIXME("parameters ignored %p %p %d\n",
ApcRoutine, ApcContext, Buffer, BufferSize, WatchTree ); ApcRoutine, ApcContext, WatchTree );
info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof *info );
if (!info)
return STATUS_NO_MEMORY;
info->FileHandle = FileHandle;
info->Event = Event;
info->Buffer = Buffer;
info->BufferSize = BufferSize;
info->ApcRoutine = ApcRoutine;
info->ApcContext = ApcContext;
SERVER_START_REQ( read_directory_changes ) SERVER_START_REQ( read_directory_changes )
{ {
req->handle = FileHandle; req->handle = FileHandle;
req->event = Event; req->event = Event;
req->filter = CompletionFilter; req->filter = CompletionFilter;
req->io_apc = read_changes_apc;
req->io_sb = IoStatusBlock;
req->io_user = info;
status = wine_server_call( req ); status = wine_server_call( req );
} }
SERVER_END_REQ; SERVER_END_REQ;
if (status != STATUS_PENDING)
RtlFreeHeap( GetProcessHeap(), 0, info );
return status; return status;
} }
...@@ -1401,6 +1401,9 @@ struct read_directory_changes_request ...@@ -1401,6 +1401,9 @@ struct read_directory_changes_request
obj_handle_t handle; obj_handle_t handle;
obj_handle_t event; obj_handle_t event;
unsigned int filter; unsigned int filter;
void* io_apc;
void* io_sb;
void* io_user;
}; };
struct read_directory_changes_reply struct read_directory_changes_reply
{ {
...@@ -1408,6 +1411,19 @@ struct read_directory_changes_reply ...@@ -1408,6 +1411,19 @@ struct read_directory_changes_reply
}; };
struct read_change_request
{
struct request_header __header;
obj_handle_t handle;
};
struct read_change_reply
{
struct reply_header __header;
int action;
/* VARARG(name,string); */
};
struct create_mapping_request struct create_mapping_request
{ {
...@@ -3768,6 +3784,7 @@ enum request ...@@ -3768,6 +3784,7 @@ enum request
REQ_move_console_output, REQ_move_console_output,
REQ_send_console_signal, REQ_send_console_signal,
REQ_read_directory_changes, REQ_read_directory_changes,
REQ_read_change,
REQ_create_mapping, REQ_create_mapping,
REQ_open_mapping, REQ_open_mapping,
REQ_get_mapping_info, REQ_get_mapping_info,
...@@ -3986,6 +4003,7 @@ union generic_request ...@@ -3986,6 +4003,7 @@ union generic_request
struct move_console_output_request move_console_output_request; struct move_console_output_request move_console_output_request;
struct send_console_signal_request send_console_signal_request; struct send_console_signal_request send_console_signal_request;
struct read_directory_changes_request read_directory_changes_request; struct read_directory_changes_request read_directory_changes_request;
struct read_change_request read_change_request;
struct create_mapping_request create_mapping_request; struct create_mapping_request create_mapping_request;
struct open_mapping_request open_mapping_request; struct open_mapping_request open_mapping_request;
struct get_mapping_info_request get_mapping_info_request; struct get_mapping_info_request get_mapping_info_request;
...@@ -4202,6 +4220,7 @@ union generic_reply ...@@ -4202,6 +4220,7 @@ union generic_reply
struct move_console_output_reply move_console_output_reply; struct move_console_output_reply move_console_output_reply;
struct send_console_signal_reply send_console_signal_reply; struct send_console_signal_reply send_console_signal_reply;
struct read_directory_changes_reply read_directory_changes_reply; struct read_directory_changes_reply read_directory_changes_reply;
struct read_change_reply read_change_reply;
struct create_mapping_reply create_mapping_reply; struct create_mapping_reply create_mapping_reply;
struct open_mapping_reply open_mapping_reply; struct open_mapping_reply open_mapping_reply;
struct get_mapping_info_reply get_mapping_info_reply; struct get_mapping_info_reply get_mapping_info_reply;
...@@ -4344,6 +4363,6 @@ union generic_reply ...@@ -4344,6 +4363,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply; struct query_symlink_reply query_symlink_reply;
}; };
#define SERVER_PROTOCOL_VERSION 223 #define SERVER_PROTOCOL_VERSION 224
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
...@@ -126,6 +126,13 @@ static inline int inotify_remove_watch( int fd, int wd ) ...@@ -126,6 +126,13 @@ static inline int inotify_remove_watch( int fd, int wd )
#endif #endif
struct change_record {
struct list entry;
int action;
int len;
char name[1];
};
struct dir struct dir
{ {
struct object obj; /* object header */ struct object obj; /* object header */
...@@ -137,6 +144,8 @@ struct dir ...@@ -137,6 +144,8 @@ struct dir
long signaled; /* the file changed */ long signaled; /* the file changed */
struct fd *inotify_fd; /* inotify file descriptor */ struct fd *inotify_fd; /* inotify file descriptor */
int wd; /* inotify watch 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 ); static struct fd *dir_get_fd( struct object *obj );
...@@ -163,6 +172,7 @@ static const struct object_ops dir_ops = ...@@ -163,6 +172,7 @@ static const struct object_ops dir_ops =
static int dir_get_poll_events( struct fd *fd ); static int dir_get_poll_events( struct fd *fd );
static int dir_get_info( 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 = static const struct fd_ops dir_fd_ops =
{ {
...@@ -171,7 +181,7 @@ static const struct fd_ops dir_fd_ops = ...@@ -171,7 +181,7 @@ static const struct fd_ops dir_fd_ops =
no_flush, /* flush */ no_flush, /* flush */
dir_get_info, /* get_file_info */ dir_get_info, /* get_file_info */
default_fd_queue_async, /* queue_async */ 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); static struct list change_list = LIST_INIT(change_list);
...@@ -238,6 +248,8 @@ struct object *create_dir_obj( struct fd *fd ) ...@@ -238,6 +248,8 @@ struct object *create_dir_obj( struct fd *fd )
if (!dir) if (!dir)
return NULL; return NULL;
list_init( &dir->change_q );
list_init( &dir->change_records );
dir->event = NULL; dir->event = NULL;
dir->filter = 0; dir->filter = 0;
dir->notified = 0; dir->notified = 0;
...@@ -321,8 +333,17 @@ static unsigned int dir_map_access( struct object *obj, unsigned int access ) ...@@ -321,8 +333,17 @@ static unsigned int dir_map_access( struct object *obj, unsigned int access )
return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL); 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 ) static void dir_destroy( struct object *obj )
{ {
struct change_record *record;
struct dir *dir = (struct dir *)obj; struct dir *dir = (struct dir *)obj;
assert (obj->ops == &dir_ops); assert (obj->ops == &dir_ops);
...@@ -332,6 +353,9 @@ static void dir_destroy( struct object *obj ) ...@@ -332,6 +353,9 @@ static void dir_destroy( struct object *obj )
if (dir->inotify_fd) if (dir->inotify_fd)
release_object( 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) if (dir->event)
{ {
set_event( dir->event ); set_event( dir->event );
...@@ -356,6 +380,12 @@ static int dir_get_info( struct fd *fd ) ...@@ -356,6 +380,12 @@ static int dir_get_info( struct fd *fd )
return 0; 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 #ifdef USE_INOTIFY
...@@ -378,10 +408,32 @@ static int inotify_get_poll_events( struct fd *fd ) ...@@ -378,10 +408,32 @@ static int inotify_get_poll_events( struct fd *fd )
return POLLIN; 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++; struct change_record *record;
dir_signal_changed( dir );
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 ) static void inotify_poll_event( struct fd *fd, int event )
...@@ -404,7 +456,7 @@ static void inotify_poll_event( struct fd *fd, int event ) ...@@ -404,7 +456,7 @@ static void inotify_poll_event( struct fd *fd, int event )
ie = (struct inotify_event*) &buffer[ofs]; ie = (struct inotify_event*) &buffer[ofs];
if (!ie->len) if (!ie->len)
break; break;
inotify_do_change_notify( dir ); inotify_do_change_notify( dir, ie );
ofs += (sizeof (*ie) + ie->len - 1); ofs += (sizeof (*ie) + ie->len - 1);
} }
} }
...@@ -489,6 +541,11 @@ DECL_HANDLER(read_directory_changes) ...@@ -489,6 +541,11 @@ DECL_HANDLER(read_directory_changes)
if (dir->event) release_object( dir->event ); if (dir->event) release_object( dir->event );
dir->event = 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 */ /* assign it once */
if (!dir->filter) if (!dir->filter)
{ {
...@@ -511,3 +568,28 @@ DECL_HANDLER(read_directory_changes) ...@@ -511,3 +568,28 @@ DECL_HANDLER(read_directory_changes)
end: end:
release_object( dir ); 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 );
}
...@@ -1045,6 +1045,17 @@ enum char_info_mode ...@@ -1045,6 +1045,17 @@ enum char_info_mode
obj_handle_t handle; /* handle to the directory */ obj_handle_t handle; /* handle to the directory */
obj_handle_t event; /* handle to the event */ obj_handle_t event; /* handle to the event */
unsigned int filter; /* notification filter */ unsigned int filter; /* notification filter */
void* io_apc; /* APC routine to queue upon end of async */
void* io_sb; /* I/O status block (unique across all async on this handle) */
void* io_user; /* data to pass back to caller */
@END
@REQ(read_change)
obj_handle_t handle;
@REPLY
int action; /* type of change */
VARARG(name,string); /* name of directory entry that changed */
@END @END
......
...@@ -181,6 +181,7 @@ DECL_HANDLER(read_console_output); ...@@ -181,6 +181,7 @@ DECL_HANDLER(read_console_output);
DECL_HANDLER(move_console_output); DECL_HANDLER(move_console_output);
DECL_HANDLER(send_console_signal); DECL_HANDLER(send_console_signal);
DECL_HANDLER(read_directory_changes); DECL_HANDLER(read_directory_changes);
DECL_HANDLER(read_change);
DECL_HANDLER(create_mapping); DECL_HANDLER(create_mapping);
DECL_HANDLER(open_mapping); DECL_HANDLER(open_mapping);
DECL_HANDLER(get_mapping_info); DECL_HANDLER(get_mapping_info);
...@@ -398,6 +399,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = ...@@ -398,6 +399,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_move_console_output, (req_handler)req_move_console_output,
(req_handler)req_send_console_signal, (req_handler)req_send_console_signal,
(req_handler)req_read_directory_changes, (req_handler)req_read_directory_changes,
(req_handler)req_read_change,
(req_handler)req_create_mapping, (req_handler)req_create_mapping,
(req_handler)req_open_mapping, (req_handler)req_open_mapping,
(req_handler)req_get_mapping_info, (req_handler)req_get_mapping_info,
......
...@@ -1448,7 +1448,22 @@ static void dump_read_directory_changes_request( const struct read_directory_cha ...@@ -1448,7 +1448,22 @@ static void dump_read_directory_changes_request( const struct read_directory_cha
{ {
fprintf( stderr, " handle=%p,", req->handle ); fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " event=%p,", req->event ); fprintf( stderr, " event=%p,", req->event );
fprintf( stderr, " filter=%08x", req->filter ); fprintf( stderr, " filter=%08x,", req->filter );
fprintf( stderr, " io_apc=%p,", req->io_apc );
fprintf( stderr, " io_sb=%p,", req->io_sb );
fprintf( stderr, " io_user=%p", req->io_user );
}
static void dump_read_change_request( const struct read_change_request *req )
{
fprintf( stderr, " handle=%p", req->handle );
}
static void dump_read_change_reply( const struct read_change_reply *req )
{
fprintf( stderr, " action=%d,", req->action );
fprintf( stderr, " name=" );
dump_varargs_string( cur_size );
} }
static void dump_create_mapping_request( const struct create_mapping_request *req ) static void dump_create_mapping_request( const struct create_mapping_request *req )
...@@ -3274,6 +3289,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { ...@@ -3274,6 +3289,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_move_console_output_request, (dump_func)dump_move_console_output_request,
(dump_func)dump_send_console_signal_request, (dump_func)dump_send_console_signal_request,
(dump_func)dump_read_directory_changes_request, (dump_func)dump_read_directory_changes_request,
(dump_func)dump_read_change_request,
(dump_func)dump_create_mapping_request, (dump_func)dump_create_mapping_request,
(dump_func)dump_open_mapping_request, (dump_func)dump_open_mapping_request,
(dump_func)dump_get_mapping_info_request, (dump_func)dump_get_mapping_info_request,
...@@ -3488,6 +3504,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { ...@@ -3488,6 +3504,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)0, (dump_func)0,
(dump_func)0, (dump_func)0,
(dump_func)0, (dump_func)0,
(dump_func)dump_read_change_reply,
(dump_func)dump_create_mapping_reply, (dump_func)dump_create_mapping_reply,
(dump_func)dump_open_mapping_reply, (dump_func)dump_open_mapping_reply,
(dump_func)dump_get_mapping_info_reply, (dump_func)dump_get_mapping_info_reply,
...@@ -3702,6 +3719,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { ...@@ -3702,6 +3719,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"move_console_output", "move_console_output",
"send_console_signal", "send_console_signal",
"read_directory_changes", "read_directory_changes",
"read_change",
"create_mapping", "create_mapping",
"open_mapping", "open_mapping",
"get_mapping_info", "get_mapping_info",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment