Implemented file change notifications, based on a patch by Mike
McCormack.
This commit is contained in:
parent
473a88661e
commit
3e588e3a57
|
@ -21,6 +21,7 @@ SPEC_SRCS16 = \
|
|||
|
||||
C_SRCS = \
|
||||
$(TOPOBJDIR)/ole/ole2nls.c \
|
||||
change.c \
|
||||
comm.c \
|
||||
computername.c \
|
||||
console.c \
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
/*
|
||||
* Win32 file change notification functions
|
||||
*
|
||||
* FIXME: this is VERY difficult to implement with proper Unix support
|
||||
* at the wineserver side.
|
||||
* (Unix doesn't really support this)
|
||||
* See http://x57.deja.com/getdoc.xp?AN=575483053 for possible solutions.
|
||||
*
|
||||
* Copyright 1998 Ulrich Weigand
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -25,13 +20,9 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "winbase.h"
|
||||
#include "winerror.h"
|
||||
#include "wine/server.h"
|
||||
|
@ -45,38 +36,49 @@ WINE_DEFAULT_DEBUG_CHANNEL(file);
|
|||
HANDLE WINAPI FindFirstChangeNotificationA( LPCSTR lpPathName, BOOL bWatchSubtree,
|
||||
DWORD dwNotifyFilter )
|
||||
{
|
||||
HANDLE ret = INVALID_HANDLE_VALUE;
|
||||
HANDLE file, ret = INVALID_HANDLE_VALUE;
|
||||
|
||||
FIXME("this is not supported yet (non-trivial).\n");
|
||||
TRACE( "%s %d %lx\n", debugstr_a(lpPathName), bWatchSubtree, dwNotifyFilter );
|
||||
|
||||
if ((file = CreateFileA( lpPathName, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 )) == INVALID_HANDLE_VALUE)
|
||||
return INVALID_HANDLE_VALUE;
|
||||
|
||||
SERVER_START_REQ( create_change_notification )
|
||||
{
|
||||
req->handle = file;
|
||||
req->subtree = bWatchSubtree;
|
||||
req->filter = dwNotifyFilter;
|
||||
if (!wine_server_call_err( req )) ret = reply->handle;
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
CloseHandle( file );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* FindFirstChangeNotificationW (KERNEL32.@)
|
||||
*/
|
||||
HANDLE WINAPI FindFirstChangeNotificationW( LPCWSTR lpPathName,
|
||||
BOOL bWatchSubtree,
|
||||
DWORD dwNotifyFilter)
|
||||
HANDLE WINAPI FindFirstChangeNotificationW( LPCWSTR lpPathName, BOOL bWatchSubtree,
|
||||
DWORD dwNotifyFilter)
|
||||
{
|
||||
HANDLE ret = INVALID_HANDLE_VALUE;
|
||||
HANDLE file, ret = INVALID_HANDLE_VALUE;
|
||||
|
||||
FIXME("this is not supported yet (non-trivial).\n");
|
||||
TRACE( "%s %d %lx\n", debugstr_w(lpPathName), bWatchSubtree, dwNotifyFilter );
|
||||
|
||||
if ((file = CreateFileW( lpPathName, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 )) == INVALID_HANDLE_VALUE)
|
||||
return INVALID_HANDLE_VALUE;
|
||||
|
||||
SERVER_START_REQ( create_change_notification )
|
||||
{
|
||||
req->handle = file;
|
||||
req->subtree = bWatchSubtree;
|
||||
req->filter = dwNotifyFilter;
|
||||
if (!wine_server_call_err( req )) ret = reply->handle;
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
CloseHandle( file );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -85,15 +87,23 @@ HANDLE WINAPI FindFirstChangeNotificationW( LPCWSTR lpPathName,
|
|||
*/
|
||||
BOOL WINAPI FindNextChangeNotification( HANDLE handle )
|
||||
{
|
||||
/* FIXME: do something */
|
||||
return TRUE;
|
||||
BOOL ret;
|
||||
|
||||
TRACE("%p\n",handle);
|
||||
|
||||
SERVER_START_REQ( next_change_notification )
|
||||
{
|
||||
req->handle = handle;
|
||||
ret = !wine_server_call_err( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* FindCloseChangeNotification (KERNEL32.@)
|
||||
*/
|
||||
BOOL WINAPI FindCloseChangeNotification( HANDLE handle)
|
||||
BOOL WINAPI FindCloseChangeNotification( HANDLE handle )
|
||||
{
|
||||
return CloseHandle( handle );
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ MODULE = ntdll.dll
|
|||
EXTRALIBS = $(LIBUNICODE)
|
||||
|
||||
C_SRCS = \
|
||||
$(TOPOBJDIR)/files/change.c \
|
||||
$(TOPOBJDIR)/files/directory.c \
|
||||
$(TOPOBJDIR)/files/dos_fs.c \
|
||||
$(TOPOBJDIR)/files/drive.c \
|
||||
|
|
|
@ -1429,8 +1429,9 @@ struct send_console_signal_reply
|
|||
struct create_change_notification_request
|
||||
{
|
||||
struct request_header __header;
|
||||
obj_handle_t handle;
|
||||
int subtree;
|
||||
int filter;
|
||||
unsigned int filter;
|
||||
};
|
||||
struct create_change_notification_reply
|
||||
{
|
||||
|
@ -1440,6 +1441,17 @@ struct create_change_notification_reply
|
|||
|
||||
|
||||
|
||||
struct next_change_notification_request
|
||||
{
|
||||
struct request_header __header;
|
||||
obj_handle_t handle;
|
||||
};
|
||||
struct next_change_notification_reply
|
||||
{
|
||||
struct reply_header __header;
|
||||
};
|
||||
|
||||
|
||||
struct create_mapping_request
|
||||
{
|
||||
struct request_header __header;
|
||||
|
@ -3095,6 +3107,7 @@ enum request
|
|||
REQ_move_console_output,
|
||||
REQ_send_console_signal,
|
||||
REQ_create_change_notification,
|
||||
REQ_next_change_notification,
|
||||
REQ_create_mapping,
|
||||
REQ_open_mapping,
|
||||
REQ_get_mapping_info,
|
||||
|
@ -3276,6 +3289,7 @@ union generic_request
|
|||
struct move_console_output_request move_console_output_request;
|
||||
struct send_console_signal_request send_console_signal_request;
|
||||
struct create_change_notification_request create_change_notification_request;
|
||||
struct next_change_notification_request next_change_notification_request;
|
||||
struct create_mapping_request create_mapping_request;
|
||||
struct open_mapping_request open_mapping_request;
|
||||
struct get_mapping_info_request get_mapping_info_request;
|
||||
|
@ -3455,6 +3469,7 @@ union generic_reply
|
|||
struct move_console_output_reply move_console_output_reply;
|
||||
struct send_console_signal_reply send_console_signal_reply;
|
||||
struct create_change_notification_reply create_change_notification_reply;
|
||||
struct next_change_notification_reply next_change_notification_reply;
|
||||
struct create_mapping_reply create_mapping_reply;
|
||||
struct open_mapping_reply open_mapping_reply;
|
||||
struct get_mapping_info_reply get_mapping_info_reply;
|
||||
|
@ -3557,6 +3572,6 @@ union generic_reply
|
|||
struct get_next_hook_reply get_next_hook_reply;
|
||||
};
|
||||
|
||||
#define SERVER_PROTOCOL_VERSION 102
|
||||
#define SERVER_PROTOCOL_VERSION 103
|
||||
|
||||
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
|
||||
|
|
152
server/change.c
152
server/change.c
|
@ -18,25 +18,37 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "windef.h"
|
||||
|
||||
#include "file.h"
|
||||
#include "handle.h"
|
||||
#include "thread.h"
|
||||
#include "request.h"
|
||||
#include "list.h"
|
||||
|
||||
struct change
|
||||
{
|
||||
struct object obj; /* object header */
|
||||
struct fd *fd; /* file descriptor to the directory */
|
||||
struct list entry; /* entry in global change notifications list */
|
||||
int subtree; /* watch all the subtree */
|
||||
int filter; /* notification filter */
|
||||
unsigned int filter; /* notification filter */
|
||||
long notified; /* SIGIO counter */
|
||||
long signaled; /* the file changed */
|
||||
};
|
||||
|
||||
static void change_dump( struct object *obj, int verbose );
|
||||
static int change_signaled( struct object *obj, struct thread *thread );
|
||||
static void change_destroy( struct object *obj );
|
||||
|
||||
static const struct object_ops change_ops =
|
||||
{
|
||||
|
@ -47,17 +59,72 @@ static const struct object_ops change_ops =
|
|||
change_signaled, /* signaled */
|
||||
no_satisfied, /* satisfied */
|
||||
no_get_fd, /* get_fd */
|
||||
no_destroy /* destroy */
|
||||
change_destroy /* destroy */
|
||||
};
|
||||
|
||||
static struct list change_list = LIST_INIT(change_list);
|
||||
|
||||
static struct change *create_change_notification( int subtree, int filter )
|
||||
static void adjust_changes( int fd, unsigned int filter )
|
||||
{
|
||||
#ifdef F_NOTIFY
|
||||
unsigned int val;
|
||||
if ( 0 > fcntl( fd, F_SETSIG, SIGIO) )
|
||||
return;
|
||||
|
||||
val = DN_MULTISHOT;
|
||||
if( filter & FILE_NOTIFY_CHANGE_FILE_NAME )
|
||||
val |= DN_RENAME | DN_DELETE | DN_CREATE;
|
||||
if( filter & FILE_NOTIFY_CHANGE_DIR_NAME )
|
||||
val |= DN_RENAME | DN_DELETE | DN_CREATE;
|
||||
if( filter & FILE_NOTIFY_CHANGE_ATTRIBUTES )
|
||||
val |= DN_ATTRIB;
|
||||
if( filter & FILE_NOTIFY_CHANGE_SIZE )
|
||||
val |= DN_MODIFY;
|
||||
if( filter & FILE_NOTIFY_CHANGE_LAST_WRITE )
|
||||
val |= DN_MODIFY;
|
||||
if( filter & FILE_NOTIFY_CHANGE_SECURITY )
|
||||
val |= DN_ATTRIB;
|
||||
fcntl( fd, F_NOTIFY, val );
|
||||
#endif
|
||||
}
|
||||
|
||||
/* insert change in the global list */
|
||||
static inline void insert_change( struct change *change )
|
||||
{
|
||||
sigset_t sigset;
|
||||
|
||||
sigemptyset( &sigset );
|
||||
sigaddset( &sigset, SIGIO );
|
||||
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
||||
list_add_head( &change_list, &change->entry );
|
||||
sigprocmask( SIG_UNBLOCK, &sigset, NULL );
|
||||
}
|
||||
|
||||
/* remove change from the global list */
|
||||
static inline void remove_change( struct change *change )
|
||||
{
|
||||
sigset_t sigset;
|
||||
|
||||
sigemptyset( &sigset );
|
||||
sigaddset( &sigset, SIGIO );
|
||||
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
||||
list_remove( &change->entry );
|
||||
sigprocmask( SIG_UNBLOCK, &sigset, NULL );
|
||||
}
|
||||
|
||||
static struct change *create_change_notification( struct fd *fd, int subtree, unsigned int filter )
|
||||
{
|
||||
struct change *change;
|
||||
|
||||
if ((change = alloc_object( &change_ops )))
|
||||
{
|
||||
change->subtree = subtree;
|
||||
change->filter = filter;
|
||||
change->fd = (struct fd *)grab_object( fd );
|
||||
change->subtree = subtree;
|
||||
change->filter = filter;
|
||||
change->notified = 0;
|
||||
change->signaled = 0;
|
||||
insert_change( change );
|
||||
adjust_changes( get_unix_fd(fd), filter );
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
@ -66,27 +133,88 @@ static void change_dump( struct object *obj, int verbose )
|
|||
{
|
||||
struct change *change = (struct change *)obj;
|
||||
assert( obj->ops == &change_ops );
|
||||
fprintf( stderr, "Change notification sub=%d filter=%08x\n",
|
||||
change->subtree, change->filter );
|
||||
fprintf( stderr, "Change notification fd=%p sub=%d filter=%08x\n",
|
||||
change->fd, change->subtree, change->filter );
|
||||
}
|
||||
|
||||
static int change_signaled( struct object *obj, struct thread *thread )
|
||||
{
|
||||
/* struct change *change = (struct change *)obj;*/
|
||||
assert( obj->ops == &change_ops );
|
||||
return 0; /* never signaled for now */
|
||||
struct change *change = (struct change *)obj;
|
||||
|
||||
return change->signaled != 0;
|
||||
}
|
||||
|
||||
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 */
|
||||
void do_change_notify( int unix_fd )
|
||||
{
|
||||
struct list *ptr;
|
||||
|
||||
/* FIXME: this is O(n) ... probably can be improved */
|
||||
LIST_FOR_EACH( ptr, &change_list )
|
||||
{
|
||||
struct change *change = LIST_ENTRY( ptr, struct change, entry );
|
||||
if (get_unix_fd( change->fd ) != unix_fd) continue;
|
||||
interlocked_xchg_add( &change->notified, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* SIGIO callback, called synchronously with the poll loop */
|
||||
void sigio_callback(void)
|
||||
{
|
||||
struct list *ptr;
|
||||
|
||||
LIST_FOR_EACH( ptr, &change_list )
|
||||
{
|
||||
struct change *change = LIST_ENTRY( ptr, struct change, entry );
|
||||
long count = interlocked_xchg( &change->notified, 0 );
|
||||
if (count)
|
||||
{
|
||||
change->signaled += count;
|
||||
if (change->signaled == count) /* was it 0? */
|
||||
wake_up( &change->obj, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* create a change notification */
|
||||
DECL_HANDLER(create_change_notification)
|
||||
{
|
||||
struct change *change;
|
||||
struct file *file;
|
||||
struct fd *fd;
|
||||
|
||||
reply->handle = 0;
|
||||
if ((change = create_change_notification( req->subtree, req->filter )))
|
||||
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,
|
||||
STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE, 0 );
|
||||
release_object( change );
|
||||
}
|
||||
release_object( fd );
|
||||
}
|
||||
|
||||
/* move to the next change notification */
|
||||
DECL_HANDLER(next_change_notification)
|
||||
{
|
||||
struct change *change;
|
||||
|
||||
if ((change = (struct change *)get_handle_obj( current->process, req->handle,
|
||||
0, &change_ops )))
|
||||
{
|
||||
if (change->signaled > 0) change->signaled--;
|
||||
release_object( change );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,7 +221,7 @@ static struct file *create_file( const char *nameptr, size_t len, unsigned int a
|
|||
return NULL;
|
||||
}
|
||||
/* refuse to open a directory */
|
||||
if (S_ISDIR(mode))
|
||||
if (S_ISDIR(mode) && !(file->flags & FILE_FLAG_BACKUP_SEMANTICS))
|
||||
{
|
||||
set_error( STATUS_ACCESS_DENIED );
|
||||
release_object( file );
|
||||
|
|
|
@ -95,4 +95,9 @@ extern int grow_file( struct file *file, int size_high, int size_low );
|
|||
extern struct file *create_temp_file( int access );
|
||||
extern void file_set_error(void);
|
||||
|
||||
/* change notification functions */
|
||||
|
||||
extern void do_change_notify( int unix_fd );
|
||||
extern void sigio_callback(void);
|
||||
|
||||
#endif /* __WINE_SERVER_FILE_H */
|
||||
|
|
|
@ -1055,13 +1055,19 @@ enum char_info_mode
|
|||
|
||||
/* Create a change notification */
|
||||
@REQ(create_change_notification)
|
||||
obj_handle_t handle; /* handle to the directory */
|
||||
int subtree; /* watch all the subtree */
|
||||
int filter; /* notification filter */
|
||||
unsigned int filter; /* notification filter */
|
||||
@REPLY
|
||||
obj_handle_t handle; /* handle to the change notification */
|
||||
@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 */
|
||||
@REQ(create_mapping)
|
||||
int size_high; /* mapping size */
|
||||
|
|
|
@ -177,6 +177,7 @@ DECL_HANDLER(read_console_output);
|
|||
DECL_HANDLER(move_console_output);
|
||||
DECL_HANDLER(send_console_signal);
|
||||
DECL_HANDLER(create_change_notification);
|
||||
DECL_HANDLER(next_change_notification);
|
||||
DECL_HANDLER(create_mapping);
|
||||
DECL_HANDLER(open_mapping);
|
||||
DECL_HANDLER(get_mapping_info);
|
||||
|
@ -357,6 +358,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_create_change_notification,
|
||||
(req_handler)req_next_change_notification,
|
||||
(req_handler)req_create_mapping,
|
||||
(req_handler)req_open_mapping,
|
||||
(req_handler)req_get_mapping_info,
|
||||
|
|
|
@ -170,12 +170,6 @@ static void sigint_callback(void)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
/* SIGIO callback */
|
||||
static void sigio_callback(void)
|
||||
{
|
||||
/* nothing here yet */
|
||||
}
|
||||
|
||||
/* SIGHUP handler */
|
||||
static void do_sighup()
|
||||
{
|
||||
|
@ -203,8 +197,8 @@ static void do_sigchld()
|
|||
/* SIGIO handler */
|
||||
static void do_sigio( int signum, siginfo_t *si, void *x )
|
||||
{
|
||||
/* do_change_notify( si->si_fd ); */
|
||||
do_signal( handler_sigio );
|
||||
do_change_notify( si->si_fd );
|
||||
}
|
||||
|
||||
void init_signals(void)
|
||||
|
|
|
@ -1241,8 +1241,9 @@ static void dump_send_console_signal_request( const struct send_console_signal_r
|
|||
|
||||
static void dump_create_change_notification_request( const struct create_change_notification_request *req )
|
||||
{
|
||||
fprintf( stderr, " handle=%p,", req->handle );
|
||||
fprintf( stderr, " subtree=%d,", req->subtree );
|
||||
fprintf( stderr, " filter=%d", req->filter );
|
||||
fprintf( stderr, " filter=%08x", req->filter );
|
||||
}
|
||||
|
||||
static void dump_create_change_notification_reply( const struct create_change_notification_reply *req )
|
||||
|
@ -1250,6 +1251,11 @@ static void dump_create_change_notification_reply( const struct create_change_no
|
|||
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 )
|
||||
{
|
||||
fprintf( stderr, " size_high=%d,", req->size_high );
|
||||
|
@ -2495,6 +2501,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_create_change_notification_request,
|
||||
(dump_func)dump_next_change_notification_request,
|
||||
(dump_func)dump_create_mapping_request,
|
||||
(dump_func)dump_open_mapping_request,
|
||||
(dump_func)dump_get_mapping_info_request,
|
||||
|
@ -2672,6 +2679,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
|||
(dump_func)0,
|
||||
(dump_func)0,
|
||||
(dump_func)dump_create_change_notification_reply,
|
||||
(dump_func)0,
|
||||
(dump_func)dump_create_mapping_reply,
|
||||
(dump_func)dump_open_mapping_reply,
|
||||
(dump_func)dump_get_mapping_info_reply,
|
||||
|
@ -2849,6 +2857,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
|
|||
"move_console_output",
|
||||
"send_console_signal",
|
||||
"create_change_notification",
|
||||
"next_change_notification",
|
||||
"create_mapping",
|
||||
"open_mapping",
|
||||
"get_mapping_info",
|
||||
|
|
Loading…
Reference in New Issue