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
DWORD filter, LPDWORD returned, LPOVERLAPPED overlapped,
LPOVERLAPPED_COMPLETION_ROUTINE completion )
{
IO_STATUS_BLOCK io;
OVERLAPPED ov, *pov;
IO_STATUS_BLOCK *ios;
NTSTATUS status;
BOOL ret = TRUE;
HANDLE event;
TRACE("%p %p %08lx %d %08lx %p %p %p\n", handle, buffer, len, subtree, filter,
returned, overlapped, completion );
if (overlapped)
event = overlapped->hEvent;
if (!overlapped)
{
memset( &ov, 0, sizeof ov );
ov.hEvent = CreateEventW( NULL, 0, 0, NULL );
pov = &ov;
}
else
event = CreateEventW( NULL, 0, 0, NULL );
pov = overlapped;
status = NtNotifyChangeDirectoryFile( handle, event, NULL, NULL,
&io, buffer, len, filter, subtree );
if (status != STATUS_PENDING)
ios = (PIO_STATUS_BLOCK) pov;
ios->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) );
ret = FALSE;
}
else if (!overlapped)
WaitForSingleObject( event, INFINITE );
else
overlapped->Internal = STATUS_PENDING;
if (!overlapped)
CloseHandle( event );
return ret;
}
......@@ -1778,6 +1778,75 @@ done:
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 ( \
FILE_NOTIFY_CHANGE_FILE_NAME | \
FILE_NOTIFY_CHANGE_DIR_NAME | \
......@@ -1797,6 +1866,7 @@ NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer,
ULONG BufferSize, ULONG CompletionFilter, BOOLEAN WatchTree )
{
struct read_changes_info *info;
NTSTATUS status;
TRACE("%p %p %p %p %p %p %lu %lu %d\n",
......@@ -1809,18 +1879,35 @@ NtNotifyChangeDirectoryFile( HANDLE FileHandle, HANDLE Event,
if (CompletionFilter == 0 || (CompletionFilter & ~FILE_NOTIFY_ALL))
return STATUS_INVALID_PARAMETER;
if (ApcRoutine || ApcContext || Buffer || BufferSize || WatchTree)
FIXME("parameters ignored %p %p %p %lu %d\n",
ApcRoutine, ApcContext, Buffer, BufferSize, WatchTree );
if (WatchTree || ApcRoutine)
FIXME("parameters ignored %p %p %d\n",
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 )
{
req->handle = FileHandle;
req->event = Event;
req->filter = CompletionFilter;
req->io_apc = read_changes_apc;
req->io_sb = IoStatusBlock;
req->io_user = info;
status = wine_server_call( req );
}
SERVER_END_REQ;
if (status != STATUS_PENDING)
RtlFreeHeap( GetProcessHeap(), 0, info );
return status;
}
......@@ -1401,6 +1401,9 @@ struct read_directory_changes_request
obj_handle_t handle;
obj_handle_t event;
unsigned int filter;
void* io_apc;
void* io_sb;
void* io_user;
};
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
{
......@@ -3768,6 +3784,7 @@ enum request
REQ_move_console_output,
REQ_send_console_signal,
REQ_read_directory_changes,
REQ_read_change,
REQ_create_mapping,
REQ_open_mapping,
REQ_get_mapping_info,
......@@ -3986,6 +4003,7 @@ union generic_request
struct move_console_output_request move_console_output_request;
struct send_console_signal_request send_console_signal_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 open_mapping_request open_mapping_request;
struct get_mapping_info_request get_mapping_info_request;
......@@ -4202,6 +4220,7 @@ union generic_reply
struct move_console_output_reply move_console_output_reply;
struct send_console_signal_reply send_console_signal_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 open_mapping_reply open_mapping_reply;
struct get_mapping_info_reply get_mapping_info_reply;
......@@ -4344,6 +4363,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply;
};
#define SERVER_PROTOCOL_VERSION 223
#define SERVER_PROTOCOL_VERSION 224
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
......@@ -126,6 +126,13 @@ static inline int inotify_remove_watch( int fd, int wd )
#endif
struct change_record {
struct list entry;
int action;
int len;
char name[1];
};
struct dir
{
struct object obj; /* object header */
......@@ -137,6 +144,8 @@ struct dir
long signaled; /* the file changed */
struct fd *inotify_fd; /* inotify file 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 );
......@@ -163,6 +172,7 @@ static const struct object_ops dir_ops =
static int dir_get_poll_events( 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 =
{
......@@ -171,7 +181,7 @@ static const struct fd_ops dir_fd_ops =
no_flush, /* flush */
dir_get_info, /* get_file_info */
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);
......@@ -238,6 +248,8 @@ struct object *create_dir_obj( struct fd *fd )
if (!dir)
return NULL;
list_init( &dir->change_q );
list_init( &dir->change_records );
dir->event = NULL;
dir->filter = 0;
dir->notified = 0;
......@@ -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);
}
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 )
{
struct change_record *record;
struct dir *dir = (struct dir *)obj;
assert (obj->ops == &dir_ops);
......@@ -332,6 +353,9 @@ static void dir_destroy( struct object *obj )
if (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)
{
set_event( dir->event );
......@@ -356,6 +380,12 @@ static int dir_get_info( struct fd *fd )
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
......@@ -378,10 +408,32 @@ static int inotify_get_poll_events( struct fd *fd )
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++;
dir_signal_changed( dir );
struct change_record *record;
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 )
......@@ -404,7 +456,7 @@ static void inotify_poll_event( struct fd *fd, int event )
ie = (struct inotify_event*) &buffer[ofs];
if (!ie->len)
break;
inotify_do_change_notify( dir );
inotify_do_change_notify( dir, ie );
ofs += (sizeof (*ie) + ie->len - 1);
}
}
......@@ -489,6 +541,11 @@ DECL_HANDLER(read_directory_changes)
if (dir->event) release_object( dir->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 */
if (!dir->filter)
{
......@@ -511,3 +568,28 @@ DECL_HANDLER(read_directory_changes)
end:
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
obj_handle_t handle; /* handle to the directory */
obj_handle_t event; /* handle to the event */
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
......
......@@ -181,6 +181,7 @@ DECL_HANDLER(read_console_output);
DECL_HANDLER(move_console_output);
DECL_HANDLER(send_console_signal);
DECL_HANDLER(read_directory_changes);
DECL_HANDLER(read_change);
DECL_HANDLER(create_mapping);
DECL_HANDLER(open_mapping);
DECL_HANDLER(get_mapping_info);
......@@ -398,6 +399,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_move_console_output,
(req_handler)req_send_console_signal,
(req_handler)req_read_directory_changes,
(req_handler)req_read_change,
(req_handler)req_create_mapping,
(req_handler)req_open_mapping,
(req_handler)req_get_mapping_info,
......
......@@ -1448,7 +1448,22 @@ static void dump_read_directory_changes_request( const struct read_directory_cha
{
fprintf( stderr, " handle=%p,", req->handle );
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 )
......@@ -3274,6 +3289,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_move_console_output_request,
(dump_func)dump_send_console_signal_request,
(dump_func)dump_read_directory_changes_request,
(dump_func)dump_read_change_request,
(dump_func)dump_create_mapping_request,
(dump_func)dump_open_mapping_request,
(dump_func)dump_get_mapping_info_request,
......@@ -3488,6 +3504,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)0,
(dump_func)0,
(dump_func)0,
(dump_func)dump_read_change_reply,
(dump_func)dump_create_mapping_reply,
(dump_func)dump_open_mapping_reply,
(dump_func)dump_get_mapping_info_reply,
......@@ -3702,6 +3719,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"move_console_output",
"send_console_signal",
"read_directory_changes",
"read_change",
"create_mapping",
"open_mapping",
"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