server: Fix the file notification interface to use directory handles.
This commit is contained in:
parent
6d85f3bf21
commit
08351071fd
|
@ -65,7 +65,7 @@ HANDLE WINAPI FindFirstChangeNotificationW( LPCWSTR lpPathName, BOOL bWatchSubtr
|
||||||
if (!RtlDosPathNameToNtPathName_U( lpPathName, &nt_name, NULL, NULL ))
|
if (!RtlDosPathNameToNtPathName_U( lpPathName, &nt_name, NULL, NULL ))
|
||||||
{
|
{
|
||||||
SetLastError( ERROR_PATH_NOT_FOUND );
|
SetLastError( ERROR_PATH_NOT_FOUND );
|
||||||
return INVALID_HANDLE_VALUE;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
attr.Length = sizeof(attr);
|
attr.Length = sizeof(attr);
|
||||||
|
@ -75,27 +75,28 @@ HANDLE WINAPI FindFirstChangeNotificationW( LPCWSTR lpPathName, BOOL bWatchSubtr
|
||||||
attr.SecurityDescriptor = NULL;
|
attr.SecurityDescriptor = NULL;
|
||||||
attr.SecurityQualityOfService = NULL;
|
attr.SecurityQualityOfService = NULL;
|
||||||
|
|
||||||
status = NtOpenFile( &file, 0, &attr, &io, 0,
|
status = NtOpenFile( &file, SYNCHRONIZE, &attr, &io,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
|
||||||
RtlFreeUnicodeString( &nt_name );
|
RtlFreeUnicodeString( &nt_name );
|
||||||
|
|
||||||
if (status != STATUS_SUCCESS)
|
if (status != STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
SetLastError( RtlNtStatusToDosError(status) );
|
SetLastError( RtlNtStatusToDosError(status) );
|
||||||
return INVALID_HANDLE_VALUE;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SERVER_START_REQ( create_change_notification )
|
SERVER_START_REQ( read_directory_changes )
|
||||||
{
|
{
|
||||||
req->access = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE;
|
|
||||||
req->attributes = 0;
|
|
||||||
req->handle = file;
|
req->handle = file;
|
||||||
req->subtree = bWatchSubtree;
|
req->event = NULL;
|
||||||
req->filter = dwNotifyFilter;
|
req->filter = dwNotifyFilter;
|
||||||
if (!wine_server_call_err( req )) ret = reply->handle;
|
status = wine_server_call( req );
|
||||||
|
if (status == STATUS_PENDING)
|
||||||
|
ret = file;
|
||||||
}
|
}
|
||||||
SERVER_END_REQ;
|
SERVER_END_REQ;
|
||||||
CloseHandle( file );
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,16 +105,28 @@ HANDLE WINAPI FindFirstChangeNotificationW( LPCWSTR lpPathName, BOOL bWatchSubtr
|
||||||
*/
|
*/
|
||||||
BOOL WINAPI FindNextChangeNotification( HANDLE handle )
|
BOOL WINAPI FindNextChangeNotification( HANDLE handle )
|
||||||
{
|
{
|
||||||
BOOL ret;
|
BOOL ret = FALSE;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
TRACE("%p\n",handle);
|
TRACE("%p\n",handle);
|
||||||
|
|
||||||
SERVER_START_REQ( next_change_notification )
|
if (!handle)
|
||||||
{
|
{
|
||||||
req->handle = handle;
|
SetLastError( ERROR_INVALID_HANDLE );
|
||||||
ret = !wine_server_call_err( req );
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVER_START_REQ( read_directory_changes )
|
||||||
|
{
|
||||||
|
req->handle = handle;
|
||||||
|
req->event = NULL;
|
||||||
|
req->filter = FILE_NOTIFY_CHANGE_SIZE; /* valid but ignored */
|
||||||
|
status = wine_server_call( req );
|
||||||
|
if (status == STATUS_PENDING)
|
||||||
|
ret = TRUE;
|
||||||
}
|
}
|
||||||
SERVER_END_REQ;
|
SERVER_END_REQ;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1778,6 +1778,16 @@ done:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FILE_NOTIFY_ALL ( \
|
||||||
|
FILE_NOTIFY_CHANGE_FILE_NAME | \
|
||||||
|
FILE_NOTIFY_CHANGE_DIR_NAME | \
|
||||||
|
FILE_NOTIFY_CHANGE_ATTRIBUTES | \
|
||||||
|
FILE_NOTIFY_CHANGE_SIZE | \
|
||||||
|
FILE_NOTIFY_CHANGE_LAST_WRITE | \
|
||||||
|
FILE_NOTIFY_CHANGE_LAST_ACCESS | \
|
||||||
|
FILE_NOTIFY_CHANGE_CREATION | \
|
||||||
|
FILE_NOTIFY_CHANGE_SECURITY )
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* NtNotifyChangeDirectoryFile [NTDLL.@]
|
* NtNotifyChangeDirectoryFile [NTDLL.@]
|
||||||
*/
|
*/
|
||||||
|
@ -1787,8 +1797,30 @@ 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 )
|
||||||
{
|
{
|
||||||
FIXME("%p %p %p %p %p %p %lu %lu %d\n",
|
NTSTATUS status;
|
||||||
|
|
||||||
|
TRACE("%p %p %p %p %p %p %lu %lu %d\n",
|
||||||
FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
|
FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
|
||||||
Buffer, BufferSize, CompletionFilter, WatchTree );
|
Buffer, BufferSize, CompletionFilter, WatchTree );
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
|
||||||
|
if (!IoStatusBlock)
|
||||||
|
return STATUS_ACCESS_VIOLATION;
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
SERVER_START_REQ( read_directory_changes )
|
||||||
|
{
|
||||||
|
req->handle = FileHandle;
|
||||||
|
req->event = Event;
|
||||||
|
req->filter = CompletionFilter;
|
||||||
|
status = wine_server_call( req );
|
||||||
|
}
|
||||||
|
SERVER_END_REQ;
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,8 @@ static void test_ntncdf(void)
|
||||||
r = CreateDirectoryW(path, NULL);
|
r = CreateDirectoryW(path, NULL);
|
||||||
ok( r == TRUE, "failed to create directory\n");
|
ok( r == TRUE, "failed to create directory\n");
|
||||||
|
|
||||||
todo_wine {
|
|
||||||
r = pNtNotifyChangeDirectoryFile(NULL,NULL,NULL,NULL,NULL,NULL,0,0,0);
|
r = pNtNotifyChangeDirectoryFile(NULL,NULL,NULL,NULL,NULL,NULL,0,0,0);
|
||||||
ok(r==STATUS_ACCESS_VIOLATION, "should return access violation\n");
|
ok(r==STATUS_ACCESS_VIOLATION, "should return access violation\n");
|
||||||
}
|
|
||||||
|
|
||||||
fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
|
fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
|
||||||
hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE, FILE_SHARE_READ, NULL,
|
hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE, FILE_SHARE_READ, NULL,
|
||||||
|
@ -76,7 +74,6 @@ static void test_ntncdf(void)
|
||||||
|
|
||||||
hEvent = CreateEvent( NULL, 0, 0, NULL );
|
hEvent = CreateEvent( NULL, 0, 0, NULL );
|
||||||
|
|
||||||
todo_wine {
|
|
||||||
r = pNtNotifyChangeDirectoryFile(hdir,NULL,NULL,NULL,&iosb,NULL,0,0,0);
|
r = pNtNotifyChangeDirectoryFile(hdir,NULL,NULL,NULL,&iosb,NULL,0,0,0);
|
||||||
ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
|
ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
|
||||||
|
|
||||||
|
@ -93,31 +90,28 @@ static void test_ntncdf(void)
|
||||||
filter |= FILE_NOTIFY_CHANGE_SECURITY;
|
filter |= FILE_NOTIFY_CHANGE_SECURITY;
|
||||||
|
|
||||||
r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
|
r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
|
||||||
ok(r==STATUS_PENDING, "should return invalid parameter\n");
|
ok(r==STATUS_PENDING, "should return status pending\n");
|
||||||
}
|
|
||||||
|
|
||||||
r = WaitForSingleObject( hEvent, 0 );
|
r = WaitForSingleObject( hEvent, 0 );
|
||||||
ok( r == STATUS_TIMEOUT, "event ready\n" );
|
ok( r == STATUS_TIMEOUT, "event shouldn't be ready\n" );
|
||||||
|
|
||||||
r = CreateDirectoryW( subdir, NULL );
|
r = CreateDirectoryW( subdir, NULL );
|
||||||
ok( r == TRUE, "failed to create directory\n");
|
ok( r == TRUE, "failed to create directory\n");
|
||||||
|
|
||||||
todo_wine {
|
|
||||||
r = WaitForSingleObject( hEvent, 0 );
|
r = WaitForSingleObject( hEvent, 0 );
|
||||||
ok( r == WAIT_OBJECT_0, "event ready\n" );
|
ok( r == WAIT_OBJECT_0, "event wasn't ready\n" );
|
||||||
|
|
||||||
r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0);
|
r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0);
|
||||||
ok(r==STATUS_PENDING, "should return invalid parameter\n");
|
ok(r==STATUS_PENDING, "should return status pending\n");
|
||||||
|
|
||||||
r = WaitForSingleObject( hdir, 0 );
|
r = WaitForSingleObject( hdir, 0 );
|
||||||
ok( r == STATUS_TIMEOUT, "event ready\n" );
|
ok( r == STATUS_TIMEOUT, "event shouldn't be ready\n" );
|
||||||
}
|
|
||||||
|
|
||||||
r = RemoveDirectoryW( subdir );
|
r = RemoveDirectoryW( subdir );
|
||||||
ok( r == TRUE, "failed to remove directory\n");
|
ok( r == TRUE, "failed to remove directory\n");
|
||||||
|
|
||||||
r = WaitForSingleObject( hdir, 0 );
|
r = WaitForSingleObject( hdir, 0 );
|
||||||
ok( r == WAIT_OBJECT_0, "event ready\n" );
|
ok( r == WAIT_OBJECT_0, "event wasn't ready\n" );
|
||||||
|
|
||||||
todo_wine {
|
todo_wine {
|
||||||
r = RemoveDirectoryW( path );
|
r = RemoveDirectoryW( path );
|
||||||
|
|
|
@ -1396,34 +1396,20 @@ struct send_console_signal_reply
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct create_change_notification_request
|
struct read_directory_changes_request
|
||||||
{
|
{
|
||||||
struct request_header __header;
|
struct request_header __header;
|
||||||
unsigned int access;
|
|
||||||
unsigned int attributes;
|
|
||||||
obj_handle_t handle;
|
obj_handle_t handle;
|
||||||
int subtree;
|
obj_handle_t event;
|
||||||
unsigned int filter;
|
unsigned int filter;
|
||||||
};
|
};
|
||||||
struct create_change_notification_reply
|
struct read_directory_changes_reply
|
||||||
{
|
|
||||||
struct reply_header __header;
|
|
||||||
obj_handle_t handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct next_change_notification_request
|
|
||||||
{
|
|
||||||
struct request_header __header;
|
|
||||||
obj_handle_t handle;
|
|
||||||
};
|
|
||||||
struct next_change_notification_reply
|
|
||||||
{
|
{
|
||||||
struct reply_header __header;
|
struct reply_header __header;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct create_mapping_request
|
struct create_mapping_request
|
||||||
{
|
{
|
||||||
struct request_header __header;
|
struct request_header __header;
|
||||||
|
@ -3782,8 +3768,7 @@ enum request
|
||||||
REQ_read_console_output,
|
REQ_read_console_output,
|
||||||
REQ_move_console_output,
|
REQ_move_console_output,
|
||||||
REQ_send_console_signal,
|
REQ_send_console_signal,
|
||||||
REQ_create_change_notification,
|
REQ_read_directory_changes,
|
||||||
REQ_next_change_notification,
|
|
||||||
REQ_create_mapping,
|
REQ_create_mapping,
|
||||||
REQ_open_mapping,
|
REQ_open_mapping,
|
||||||
REQ_get_mapping_info,
|
REQ_get_mapping_info,
|
||||||
|
@ -4001,8 +3986,7 @@ union generic_request
|
||||||
struct read_console_output_request read_console_output_request;
|
struct read_console_output_request read_console_output_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 create_change_notification_request create_change_notification_request;
|
struct read_directory_changes_request read_directory_changes_request;
|
||||||
struct next_change_notification_request next_change_notification_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;
|
||||||
|
@ -4218,8 +4202,7 @@ union generic_reply
|
||||||
struct read_console_output_reply read_console_output_reply;
|
struct read_console_output_reply read_console_output_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 create_change_notification_reply create_change_notification_reply;
|
struct read_directory_changes_reply read_directory_changes_reply;
|
||||||
struct next_change_notification_reply next_change_notification_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;
|
||||||
|
@ -4362,6 +4345,6 @@ union generic_reply
|
||||||
struct query_symlink_reply query_symlink_reply;
|
struct query_symlink_reply query_symlink_reply;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SERVER_PROTOCOL_VERSION 221
|
#define SERVER_PROTOCOL_VERSION 222
|
||||||
|
|
||||||
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
|
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
|
||||||
|
|
245
server/change.c
245
server/change.c
|
@ -2,6 +2,7 @@
|
||||||
* Server-side change notification management
|
* Server-side change notification management
|
||||||
*
|
*
|
||||||
* Copyright (C) 1998 Alexandre Julliard
|
* Copyright (C) 1998 Alexandre Julliard
|
||||||
|
* Copyright (C) 2006 Mike McCormack
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -51,35 +52,50 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct change
|
struct dir
|
||||||
{
|
{
|
||||||
struct object obj; /* object header */
|
struct object obj; /* object header */
|
||||||
struct fd *fd; /* file descriptor to the directory */
|
struct fd *fd; /* file descriptor to the directory */
|
||||||
struct list entry; /* entry in global change notifications list */
|
struct list entry; /* entry in global change notifications list */
|
||||||
int subtree; /* watch all the subtree */
|
struct event *event;
|
||||||
unsigned int filter; /* notification filter */
|
unsigned int filter; /* notification filter */
|
||||||
int notified; /* SIGIO counter */
|
int notified; /* SIGIO counter */
|
||||||
long signaled; /* the file changed */
|
long signaled; /* the file changed */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void change_dump( struct object *obj, int verbose );
|
static struct fd *dir_get_fd( struct object *obj );
|
||||||
static int change_signaled( struct object *obj, struct thread *thread );
|
static unsigned int dir_map_access( struct object *obj, unsigned int access );
|
||||||
static void change_destroy( struct object *obj );
|
static void dir_dump( struct object *obj, int verbose );
|
||||||
|
static void dir_destroy( struct object *obj );
|
||||||
|
static int dir_signaled( struct object *obj, struct thread *thread );
|
||||||
|
|
||||||
static const struct object_ops change_ops =
|
static const struct object_ops dir_ops =
|
||||||
{
|
{
|
||||||
sizeof(struct change), /* size */
|
sizeof(struct dir), /* size */
|
||||||
change_dump, /* dump */
|
dir_dump, /* dump */
|
||||||
add_queue, /* add_queue */
|
add_queue, /* add_queue */
|
||||||
remove_queue, /* remove_queue */
|
remove_queue, /* remove_queue */
|
||||||
change_signaled, /* signaled */
|
dir_signaled, /* signaled */
|
||||||
no_satisfied, /* satisfied */
|
no_satisfied, /* satisfied */
|
||||||
no_signal, /* signal */
|
no_signal, /* signal */
|
||||||
no_get_fd, /* get_fd */
|
dir_get_fd, /* get_fd */
|
||||||
no_map_access, /* map_access */
|
dir_map_access, /* map_access */
|
||||||
no_lookup_name, /* lookup_name */
|
no_lookup_name, /* lookup_name */
|
||||||
no_close_handle, /* close_handle */
|
no_close_handle, /* close_handle */
|
||||||
change_destroy /* destroy */
|
dir_destroy /* destroy */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dir_get_poll_events( struct fd *fd );
|
||||||
|
static int dir_get_info( struct fd *fd );
|
||||||
|
|
||||||
|
static const struct fd_ops dir_fd_ops =
|
||||||
|
{
|
||||||
|
dir_get_poll_events, /* get_poll_events */
|
||||||
|
default_poll_event, /* poll_event */
|
||||||
|
no_flush, /* flush */
|
||||||
|
dir_get_info, /* get_file_info */
|
||||||
|
default_fd_queue_async, /* queue_async */
|
||||||
|
default_fd_cancel_async /* cancel_async */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct list change_list = LIST_INIT(change_list);
|
static struct list change_list = LIST_INIT(change_list);
|
||||||
|
@ -113,77 +129,61 @@ static void adjust_changes( int fd, unsigned int filter )
|
||||||
}
|
}
|
||||||
|
|
||||||
/* insert change in the global list */
|
/* insert change in the global list */
|
||||||
static inline void insert_change( struct change *change )
|
static inline void insert_change( struct dir *dir )
|
||||||
{
|
{
|
||||||
sigset_t sigset;
|
sigset_t sigset;
|
||||||
|
|
||||||
sigemptyset( &sigset );
|
sigemptyset( &sigset );
|
||||||
sigaddset( &sigset, SIGIO );
|
sigaddset( &sigset, SIGIO );
|
||||||
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
||||||
list_add_head( &change_list, &change->entry );
|
list_add_head( &change_list, &dir->entry );
|
||||||
sigprocmask( SIG_UNBLOCK, &sigset, NULL );
|
sigprocmask( SIG_UNBLOCK, &sigset, NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove change from the global list */
|
/* remove change from the global list */
|
||||||
static inline void remove_change( struct change *change )
|
static inline void remove_change( struct dir *dir )
|
||||||
{
|
{
|
||||||
sigset_t sigset;
|
sigset_t sigset;
|
||||||
|
|
||||||
sigemptyset( &sigset );
|
sigemptyset( &sigset );
|
||||||
sigaddset( &sigset, SIGIO );
|
sigaddset( &sigset, SIGIO );
|
||||||
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
||||||
list_remove( &change->entry );
|
list_remove( &dir->entry );
|
||||||
sigprocmask( SIG_UNBLOCK, &sigset, NULL );
|
sigprocmask( SIG_UNBLOCK, &sigset, NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct change *create_change_notification( struct fd *fd, int subtree, unsigned int filter )
|
struct object *create_dir_obj( struct fd *fd )
|
||||||
{
|
{
|
||||||
struct change *change;
|
struct dir *dir;
|
||||||
struct stat st;
|
|
||||||
int unix_fd = get_unix_fd( fd );
|
|
||||||
|
|
||||||
if (unix_fd == -1) return NULL;
|
dir = alloc_object( &dir_ops );
|
||||||
|
if (!dir)
|
||||||
if (fstat( unix_fd, &st ) == -1 || !S_ISDIR(st.st_mode))
|
|
||||||
{
|
|
||||||
set_error( STATUS_NOT_A_DIRECTORY );
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
dir->event = NULL;
|
||||||
|
dir->filter = 0;
|
||||||
|
dir->notified = 0;
|
||||||
|
dir->signaled = 0;
|
||||||
|
grab_object( fd );
|
||||||
|
dir->fd = fd;
|
||||||
|
set_fd_user( fd, &dir_fd_ops, &dir->obj );
|
||||||
|
|
||||||
if ((change = alloc_object( &change_ops )))
|
return &dir->obj;
|
||||||
{
|
|
||||||
change->fd = (struct fd *)grab_object( fd );
|
|
||||||
change->subtree = subtree;
|
|
||||||
change->filter = filter;
|
|
||||||
change->notified = 0;
|
|
||||||
change->signaled = 0;
|
|
||||||
insert_change( change );
|
|
||||||
adjust_changes( unix_fd, filter );
|
|
||||||
}
|
|
||||||
return change;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void change_dump( struct object *obj, int verbose )
|
static void dir_dump( struct object *obj, int verbose )
|
||||||
{
|
{
|
||||||
struct change *change = (struct change *)obj;
|
struct dir *dir = (struct dir *)obj;
|
||||||
assert( obj->ops == &change_ops );
|
assert( obj->ops == &dir_ops );
|
||||||
fprintf( stderr, "Change notification fd=%p sub=%d filter=%08x\n",
|
fprintf( stderr, "Dirfile fd=%p event=%p filter=%08x\n",
|
||||||
change->fd, change->subtree, change->filter );
|
dir->fd, dir->event, dir->filter );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int change_signaled( struct object *obj, struct thread *thread )
|
static int dir_signaled( struct object *obj, struct thread *thread )
|
||||||
{
|
{
|
||||||
struct change *change = (struct change *)obj;
|
struct dir *dir = (struct dir *)obj;
|
||||||
|
assert (obj->ops == &dir_ops);
|
||||||
return change->signaled != 0;
|
return (dir->event == NULL) && dir->signaled;
|
||||||
}
|
|
||||||
|
|
||||||
static void change_destroy( struct object *obj )
|
|
||||||
{
|
|
||||||
struct change *change = (struct change *)obj;
|
|
||||||
|
|
||||||
release_object( change->fd );
|
|
||||||
remove_change( change );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enter here directly from SIGIO signal handler */
|
/* enter here directly from SIGIO signal handler */
|
||||||
|
@ -194,9 +194,9 @@ void do_change_notify( int unix_fd )
|
||||||
/* FIXME: this is O(n) ... probably can be improved */
|
/* FIXME: this is O(n) ... probably can be improved */
|
||||||
LIST_FOR_EACH( ptr, &change_list )
|
LIST_FOR_EACH( ptr, &change_list )
|
||||||
{
|
{
|
||||||
struct change *change = LIST_ENTRY( ptr, struct change, entry );
|
struct dir *dir = LIST_ENTRY( ptr, struct dir, entry );
|
||||||
if (get_unix_fd( change->fd ) != unix_fd) continue;
|
if (get_unix_fd( dir->fd ) != unix_fd) continue;
|
||||||
interlocked_xchg_add( &change->notified, 1 );
|
interlocked_xchg_add( &dir->notified, 1 );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,46 +208,113 @@ void sigio_callback(void)
|
||||||
|
|
||||||
LIST_FOR_EACH( ptr, &change_list )
|
LIST_FOR_EACH( ptr, &change_list )
|
||||||
{
|
{
|
||||||
struct change *change = LIST_ENTRY( ptr, struct change, entry );
|
struct dir *dir = LIST_ENTRY( ptr, struct dir, entry );
|
||||||
long count = interlocked_xchg( &change->notified, 0 );
|
long count = interlocked_xchg( &dir->notified, 0 );
|
||||||
if (count)
|
if (count)
|
||||||
{
|
{
|
||||||
change->signaled += count;
|
dir->signaled += count;
|
||||||
if (change->signaled == count) /* was it 0? */
|
if (dir->signaled == count) /* was it 0? */
|
||||||
wake_up( &change->obj, 0 );
|
{
|
||||||
|
if (dir->event)
|
||||||
|
set_event( dir->event );
|
||||||
|
else
|
||||||
|
wake_up( &dir->obj, 0 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create a change notification */
|
static struct fd *dir_get_fd( struct object *obj )
|
||||||
DECL_HANDLER(create_change_notification)
|
|
||||||
{
|
{
|
||||||
struct change *change;
|
struct dir *dir = (struct dir *)obj;
|
||||||
struct file *file;
|
assert( obj->ops == &dir_ops );
|
||||||
struct fd *fd;
|
return (struct fd *)grab_object( dir->fd );
|
||||||
|
|
||||||
if (!(file = get_file_obj( current->process, req->handle, 0 ))) return;
|
|
||||||
fd = get_obj_fd( (struct object *)file );
|
|
||||||
release_object( file );
|
|
||||||
if (!fd) return;
|
|
||||||
|
|
||||||
if ((change = create_change_notification( fd, req->subtree, req->filter )))
|
|
||||||
{
|
|
||||||
reply->handle = alloc_handle( current->process, change, req->access, req->attributes );
|
|
||||||
release_object( change );
|
|
||||||
}
|
|
||||||
release_object( fd );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* move to the next change notification */
|
static unsigned int dir_map_access( struct object *obj, unsigned int access )
|
||||||
DECL_HANDLER(next_change_notification)
|
|
||||||
{
|
{
|
||||||
struct change *change;
|
if (access & GENERIC_READ) access |= FILE_GENERIC_READ;
|
||||||
|
if (access & GENERIC_WRITE) access |= FILE_GENERIC_WRITE;
|
||||||
if ((change = (struct change *)get_handle_obj( current->process, req->handle,
|
if (access & GENERIC_EXECUTE) access |= FILE_GENERIC_EXECUTE;
|
||||||
0, &change_ops )))
|
if (access & GENERIC_ALL) access |= FILE_ALL_ACCESS;
|
||||||
{
|
return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
|
||||||
if (change->signaled > 0) change->signaled--;
|
}
|
||||||
release_object( change );
|
|
||||||
}
|
static void dir_destroy( struct object *obj )
|
||||||
|
{
|
||||||
|
struct dir *dir = (struct dir *)obj;
|
||||||
|
assert (obj->ops == &dir_ops);
|
||||||
|
|
||||||
|
if (dir->filter)
|
||||||
|
remove_change( dir );
|
||||||
|
|
||||||
|
if (dir->event)
|
||||||
|
{
|
||||||
|
set_event( dir->event );
|
||||||
|
release_object( dir->event );
|
||||||
|
}
|
||||||
|
release_object( dir->fd );
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dir *
|
||||||
|
get_dir_obj( struct process *process, obj_handle_t handle, unsigned int access )
|
||||||
|
{
|
||||||
|
return (struct dir *)get_handle_obj( process, handle, access, &dir_ops );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dir_get_poll_events( struct fd *fd )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dir_get_info( struct fd *fd )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable change notifications for a directory */
|
||||||
|
DECL_HANDLER(read_directory_changes)
|
||||||
|
{
|
||||||
|
struct event *event = NULL;
|
||||||
|
struct dir *dir;
|
||||||
|
|
||||||
|
if (!req->filter)
|
||||||
|
{
|
||||||
|
set_error(STATUS_INVALID_PARAMETER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = get_dir_obj( current->process, req->handle, 0 );
|
||||||
|
if (!dir)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* possibly send changes through an event flag */
|
||||||
|
if (req->event)
|
||||||
|
{
|
||||||
|
event = get_event_obj( current->process, req->event, EVENT_MODIFY_STATE );
|
||||||
|
if (!event)
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* discard the current data, and move onto the next event */
|
||||||
|
if (dir->event) release_object( dir->event );
|
||||||
|
dir->event = event;
|
||||||
|
|
||||||
|
/* assign it once */
|
||||||
|
if (!dir->filter)
|
||||||
|
{
|
||||||
|
insert_change( dir );
|
||||||
|
dir->filter = req->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove any notifications */
|
||||||
|
if (dir->signaled>0)
|
||||||
|
dir->signaled--;
|
||||||
|
|
||||||
|
adjust_changes( get_unix_fd( dir->fd ), dir->filter );
|
||||||
|
|
||||||
|
set_error(STATUS_PENDING);
|
||||||
|
|
||||||
|
end:
|
||||||
|
release_object( dir );
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,9 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
||||||
fd = open_fd( name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options );
|
fd = open_fd( name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options );
|
||||||
if (!fd) goto done;
|
if (!fd) goto done;
|
||||||
|
|
||||||
if (S_ISCHR(mode) && is_serial_fd( fd ))
|
if (S_ISDIR(mode))
|
||||||
|
obj = create_dir_obj( fd );
|
||||||
|
else if (S_ISCHR(mode) && is_serial_fd( fd ))
|
||||||
obj = create_serial( fd, options );
|
obj = create_serial( fd, options );
|
||||||
else
|
else
|
||||||
obj = create_file_obj( fd, access, options );
|
obj = create_file_obj( fd, access, options );
|
||||||
|
|
|
@ -109,6 +109,7 @@ extern void file_set_error(void);
|
||||||
|
|
||||||
extern void do_change_notify( int unix_fd );
|
extern void do_change_notify( int unix_fd );
|
||||||
extern void sigio_callback(void);
|
extern void sigio_callback(void);
|
||||||
|
extern struct object *create_dir_obj( struct fd *fd );
|
||||||
|
|
||||||
/* serial port functions */
|
/* serial port functions */
|
||||||
|
|
||||||
|
|
|
@ -1041,23 +1041,14 @@ enum char_info_mode
|
||||||
@END
|
@END
|
||||||
|
|
||||||
|
|
||||||
/* Create a change notification */
|
/* enable directory change notifications */
|
||||||
@REQ(create_change_notification)
|
@REQ(read_directory_changes)
|
||||||
unsigned int access; /* wanted access rights */
|
|
||||||
unsigned int attributes; /* object attributes */
|
|
||||||
obj_handle_t handle; /* handle to the directory */
|
obj_handle_t handle; /* handle to the directory */
|
||||||
int subtree; /* watch all the subtree */
|
obj_handle_t event; /* handle to the event */
|
||||||
unsigned int filter; /* notification filter */
|
unsigned int filter; /* notification filter */
|
||||||
@REPLY
|
|
||||||
obj_handle_t handle; /* handle to the change notification */
|
|
||||||
@END
|
@END
|
||||||
|
|
||||||
|
|
||||||
/* Move to the next change notification */
|
|
||||||
@REQ(next_change_notification)
|
|
||||||
obj_handle_t handle; /* handle to the change notification */
|
|
||||||
@END
|
|
||||||
|
|
||||||
/* Create a file mapping */
|
/* Create a file mapping */
|
||||||
@REQ(create_mapping)
|
@REQ(create_mapping)
|
||||||
unsigned int access; /* wanted access rights */
|
unsigned int access; /* wanted access rights */
|
||||||
|
|
|
@ -180,8 +180,7 @@ DECL_HANDLER(fill_console_output);
|
||||||
DECL_HANDLER(read_console_output);
|
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(create_change_notification);
|
DECL_HANDLER(read_directory_changes);
|
||||||
DECL_HANDLER(next_change_notification);
|
|
||||||
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,8 +397,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
|
||||||
(req_handler)req_read_console_output,
|
(req_handler)req_read_console_output,
|
||||||
(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_create_change_notification,
|
(req_handler)req_read_directory_changes,
|
||||||
(req_handler)req_next_change_notification,
|
|
||||||
(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,
|
||||||
|
|
|
@ -1445,25 +1445,13 @@ static void dump_send_console_signal_request( const struct send_console_signal_r
|
||||||
fprintf( stderr, " group_id=%04x", req->group_id );
|
fprintf( stderr, " group_id=%04x", req->group_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_create_change_notification_request( const struct create_change_notification_request *req )
|
static void dump_read_directory_changes_request( const struct read_directory_changes_request *req )
|
||||||
{
|
{
|
||||||
fprintf( stderr, " access=%08x,", req->access );
|
|
||||||
fprintf( stderr, " attributes=%08x,", req->attributes );
|
|
||||||
fprintf( stderr, " handle=%p,", req->handle );
|
fprintf( stderr, " handle=%p,", req->handle );
|
||||||
fprintf( stderr, " subtree=%d,", req->subtree );
|
fprintf( stderr, " event=%p,", req->event );
|
||||||
fprintf( stderr, " filter=%08x", req->filter );
|
fprintf( stderr, " filter=%08x", req->filter );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_create_change_notification_reply( const struct create_change_notification_reply *req )
|
|
||||||
{
|
|
||||||
fprintf( stderr, " handle=%p", req->handle );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dump_next_change_notification_request( const struct next_change_notification_request *req )
|
|
||||||
{
|
|
||||||
fprintf( stderr, " handle=%p", req->handle );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dump_create_mapping_request( const struct create_mapping_request *req )
|
static void dump_create_mapping_request( const struct create_mapping_request *req )
|
||||||
{
|
{
|
||||||
fprintf( stderr, " access=%08x,", req->access );
|
fprintf( stderr, " access=%08x,", req->access );
|
||||||
|
@ -3286,8 +3274,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
|
||||||
(dump_func)dump_read_console_output_request,
|
(dump_func)dump_read_console_output_request,
|
||||||
(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_create_change_notification_request,
|
(dump_func)dump_read_directory_changes_request,
|
||||||
(dump_func)dump_next_change_notification_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,
|
||||||
|
@ -3501,7 +3488,6 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
||||||
(dump_func)dump_read_console_output_reply,
|
(dump_func)dump_read_console_output_reply,
|
||||||
(dump_func)0,
|
(dump_func)0,
|
||||||
(dump_func)0,
|
(dump_func)0,
|
||||||
(dump_func)dump_create_change_notification_reply,
|
|
||||||
(dump_func)0,
|
(dump_func)0,
|
||||||
(dump_func)dump_create_mapping_reply,
|
(dump_func)dump_create_mapping_reply,
|
||||||
(dump_func)dump_open_mapping_reply,
|
(dump_func)dump_open_mapping_reply,
|
||||||
|
@ -3716,8 +3702,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
|
||||||
"read_console_output",
|
"read_console_output",
|
||||||
"move_console_output",
|
"move_console_output",
|
||||||
"send_console_signal",
|
"send_console_signal",
|
||||||
"create_change_notification",
|
"read_directory_changes",
|
||||||
"next_change_notification",
|
|
||||||
"create_mapping",
|
"create_mapping",
|
||||||
"open_mapping",
|
"open_mapping",
|
||||||
"get_mapping_info",
|
"get_mapping_info",
|
||||||
|
|
Loading…
Reference in New Issue