server: Fill in NtNotifyChangeDirectoryFile's buffer with change data.
This commit is contained in:
parent
acb52e5272
commit
0193211946
|
@ -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,11 +408,33 @@ 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 )
|
||||
{
|
||||
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",
|
||||
|
|
Loading…
Reference in New Issue