server: Add support for maintaining a client-side directory cache.
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
fcf0829b60
commit
80f3fda934
|
@ -1452,6 +1452,21 @@ enum server_fd_type
|
|||
|
||||
|
||||
|
||||
struct get_directory_cache_entry_request
|
||||
{
|
||||
struct request_header __header;
|
||||
obj_handle_t handle;
|
||||
};
|
||||
struct get_directory_cache_entry_reply
|
||||
{
|
||||
struct reply_header __header;
|
||||
int entry;
|
||||
/* VARARG(free,ints); */
|
||||
char __pad_12[4];
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct flush_request
|
||||
{
|
||||
struct request_header __header;
|
||||
|
@ -5401,6 +5416,7 @@ enum request
|
|||
REQ_alloc_file_handle,
|
||||
REQ_get_handle_unix_name,
|
||||
REQ_get_handle_fd,
|
||||
REQ_get_directory_cache_entry,
|
||||
REQ_flush,
|
||||
REQ_lock_file,
|
||||
REQ_unlock_file,
|
||||
|
@ -5679,6 +5695,7 @@ union generic_request
|
|||
struct alloc_file_handle_request alloc_file_handle_request;
|
||||
struct get_handle_unix_name_request get_handle_unix_name_request;
|
||||
struct get_handle_fd_request get_handle_fd_request;
|
||||
struct get_directory_cache_entry_request get_directory_cache_entry_request;
|
||||
struct flush_request flush_request;
|
||||
struct lock_file_request lock_file_request;
|
||||
struct unlock_file_request unlock_file_request;
|
||||
|
@ -5955,6 +5972,7 @@ union generic_reply
|
|||
struct alloc_file_handle_reply alloc_file_handle_reply;
|
||||
struct get_handle_unix_name_reply get_handle_unix_name_reply;
|
||||
struct get_handle_fd_reply get_handle_fd_reply;
|
||||
struct get_directory_cache_entry_reply get_directory_cache_entry_reply;
|
||||
struct flush_reply flush_reply;
|
||||
struct lock_file_reply lock_file_reply;
|
||||
struct unlock_file_reply unlock_file_reply;
|
||||
|
@ -6184,6 +6202,6 @@ union generic_reply
|
|||
struct terminate_job_reply terminate_job_reply;
|
||||
};
|
||||
|
||||
#define SERVER_PROTOCOL_VERSION 502
|
||||
#define SERVER_PROTOCOL_VERSION 503
|
||||
|
||||
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
|
||||
|
|
122
server/change.c
122
server/change.c
|
@ -141,6 +141,8 @@ struct dir
|
|||
struct list change_records; /* data for the change */
|
||||
struct list in_entry; /* entry in the inode dirs list */
|
||||
struct inode *inode; /* inode of the associated directory */
|
||||
struct process *client_process; /* client process that has a cache for this directory */
|
||||
int client_entry; /* entry in client process cache */
|
||||
};
|
||||
|
||||
static struct fd *dir_get_fd( struct object *obj );
|
||||
|
@ -149,6 +151,7 @@ static int dir_set_sd( struct object *obj, const struct security_descriptor *sd,
|
|||
unsigned int set_info );
|
||||
static void dir_dump( struct object *obj, int verbose );
|
||||
static struct object_type *dir_get_type( struct object *obj );
|
||||
static int dir_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
|
||||
static void dir_destroy( struct object *obj );
|
||||
|
||||
static const struct object_ops dir_ops =
|
||||
|
@ -169,7 +172,7 @@ static const struct object_ops dir_ops =
|
|||
no_link_name, /* link_name */
|
||||
NULL, /* unlink_name */
|
||||
no_open_file, /* open_file */
|
||||
fd_close_handle, /* close_handle */
|
||||
dir_close_handle, /* close_handle */
|
||||
dir_destroy /* destroy */
|
||||
};
|
||||
|
||||
|
@ -192,6 +195,86 @@ static const struct fd_ops dir_fd_ops =
|
|||
|
||||
static struct list change_list = LIST_INIT(change_list);
|
||||
|
||||
/* per-process structure to keep track of cache entries on the client size */
|
||||
struct dir_cache
|
||||
{
|
||||
unsigned int size;
|
||||
unsigned int count;
|
||||
unsigned char state[1];
|
||||
};
|
||||
|
||||
enum dir_cache_state
|
||||
{
|
||||
DIR_CACHE_STATE_FREE,
|
||||
DIR_CACHE_STATE_INUSE,
|
||||
DIR_CACHE_STATE_RELEASED
|
||||
};
|
||||
|
||||
/* return an array of cache entries that can be freed on the client side */
|
||||
static int *get_free_dir_cache_entries( struct process *process, data_size_t *size )
|
||||
{
|
||||
int *ret;
|
||||
struct dir_cache *cache = process->dir_cache;
|
||||
unsigned int i, j, count;
|
||||
|
||||
if (!cache) return NULL;
|
||||
for (i = count = 0; i < cache->count && count < *size / sizeof(*ret); i++)
|
||||
if (cache->state[i] == DIR_CACHE_STATE_RELEASED) count++;
|
||||
if (!count) return NULL;
|
||||
|
||||
if ((ret = malloc( count * sizeof(*ret) )))
|
||||
{
|
||||
for (i = j = 0; j < count; i++)
|
||||
{
|
||||
if (cache->state[i] != DIR_CACHE_STATE_RELEASED) continue;
|
||||
cache->state[i] = DIR_CACHE_STATE_FREE;
|
||||
ret[j++] = i;
|
||||
}
|
||||
*size = count * sizeof(*ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* allocate a new client-side directory cache entry */
|
||||
static int alloc_dir_cache_entry( struct dir *dir, struct process *process )
|
||||
{
|
||||
unsigned int i = 0;
|
||||
struct dir_cache *cache = process->dir_cache;
|
||||
|
||||
if (cache)
|
||||
for (i = 0; i < cache->count; i++)
|
||||
if (cache->state[i] == DIR_CACHE_STATE_FREE) goto found;
|
||||
|
||||
if (!cache || cache->count == cache->size)
|
||||
{
|
||||
unsigned int size = cache ? cache->size * 2 : 256;
|
||||
if (!(cache = realloc( cache, offsetof( struct dir_cache, state[size] ))))
|
||||
{
|
||||
set_error( STATUS_NO_MEMORY );
|
||||
return -1;
|
||||
}
|
||||
process->dir_cache = cache;
|
||||
cache->size = size;
|
||||
}
|
||||
cache->count = i + 1;
|
||||
|
||||
found:
|
||||
cache->state[i] = DIR_CACHE_STATE_INUSE;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* release a directory cache entry; it will be freed on the client side on the next cache request */
|
||||
static void release_dir_cache_entry( struct dir *dir )
|
||||
{
|
||||
struct dir_cache *cache;
|
||||
|
||||
if (!dir->client_process) return;
|
||||
cache = dir->client_process->dir_cache;
|
||||
cache->state[dir->client_entry] = DIR_CACHE_STATE_RELEASED;
|
||||
release_object( dir->client_process );
|
||||
dir->client_process = NULL;
|
||||
}
|
||||
|
||||
static void dnotify_adjust_changes( struct dir *dir )
|
||||
{
|
||||
#if defined(F_SETSIG) && defined(F_NOTIFY)
|
||||
|
@ -385,6 +468,15 @@ static struct change_record *get_first_change_record( struct dir *dir )
|
|||
return LIST_ENTRY( ptr, struct change_record, entry );
|
||||
}
|
||||
|
||||
static int dir_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
|
||||
{
|
||||
struct dir *dir = (struct dir *)obj;
|
||||
|
||||
if (!fd_close_handle( obj, process, handle )) return 0;
|
||||
if (obj->handle_count == 1) release_dir_cache_entry( dir ); /* closing last handle, release cache */
|
||||
return 1; /* ok to close */
|
||||
}
|
||||
|
||||
static void dir_destroy( struct object *obj )
|
||||
{
|
||||
struct change_record *record;
|
||||
|
@ -402,6 +494,7 @@ static void dir_destroy( struct object *obj )
|
|||
|
||||
while ((record = get_first_change_record( dir ))) free( record );
|
||||
|
||||
release_dir_cache_entry( dir );
|
||||
release_object( dir->fd );
|
||||
|
||||
if (inotify_fd && list_empty( &change_list ))
|
||||
|
@ -1104,6 +1197,7 @@ struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode )
|
|||
dir->fd = fd;
|
||||
dir->mode = mode;
|
||||
dir->uid = ~(uid_t)0;
|
||||
dir->client_process = NULL;
|
||||
set_fd_user( fd, &dir_fd_ops, &dir->obj );
|
||||
|
||||
dir_add_to_existing_notify( dir );
|
||||
|
@ -1111,6 +1205,32 @@ struct object *create_dir_obj( struct fd *fd, unsigned int access, mode_t mode )
|
|||
return &dir->obj;
|
||||
}
|
||||
|
||||
/* retrieve (or allocate) the client-side directory cache entry */
|
||||
DECL_HANDLER(get_directory_cache_entry)
|
||||
{
|
||||
struct dir *dir;
|
||||
int *free_entries;
|
||||
data_size_t free_size;
|
||||
|
||||
if (!(dir = get_dir_obj( current->process, req->handle, 0 ))) return;
|
||||
|
||||
if (!dir->client_process)
|
||||
{
|
||||
if ((dir->client_entry = alloc_dir_cache_entry( dir, current->process )) == -1) goto done;
|
||||
dir->client_process = (struct process *)grab_object( current->process );
|
||||
}
|
||||
|
||||
if (dir->client_process == current->process) reply->entry = dir->client_entry;
|
||||
else set_error( STATUS_SHARING_VIOLATION );
|
||||
|
||||
done: /* allow freeing entries even on failure */
|
||||
free_size = get_reply_max_size();
|
||||
free_entries = get_free_dir_cache_entries( current->process, &free_size );
|
||||
if (free_entries) set_reply_data_ptr( free_entries, free_size );
|
||||
|
||||
release_object( dir );
|
||||
}
|
||||
|
||||
/* enable change notifications for a directory */
|
||||
DECL_HANDLER(read_directory_changes)
|
||||
{
|
||||
|
|
|
@ -526,6 +526,7 @@ struct thread *create_process( int fd, struct thread *parent_thread, int inherit
|
|||
process->idle_event = NULL;
|
||||
process->peb = 0;
|
||||
process->ldt_copy = 0;
|
||||
process->dir_cache = NULL;
|
||||
process->winstation = 0;
|
||||
process->desktop = 0;
|
||||
process->token = NULL;
|
||||
|
@ -630,6 +631,7 @@ static void process_destroy( struct object *obj )
|
|||
if (process->idle_event) release_object( process->idle_event );
|
||||
if (process->id) free_ptid( process->id );
|
||||
if (process->token) release_object( process->token );
|
||||
free( process->dir_cache );
|
||||
}
|
||||
|
||||
/* dump a process on stdout for debugging purposes */
|
||||
|
|
|
@ -90,6 +90,7 @@ struct process
|
|||
struct list dlls; /* list of loaded dlls */
|
||||
client_ptr_t peb; /* PEB address in client address space */
|
||||
client_ptr_t ldt_copy; /* pointer to LDT copy in client addr space */
|
||||
struct dir_cache *dir_cache; /* map of client-side directory cache */
|
||||
unsigned int trace_data; /* opaque data used by the process tracing mechanism */
|
||||
struct list rawinput_devices;/* list of registered rawinput devices */
|
||||
const struct rawinput_device *rawinput_mouse; /* rawinput mouse device, if any */
|
||||
|
|
|
@ -1200,6 +1200,15 @@ enum server_fd_type
|
|||
};
|
||||
|
||||
|
||||
/* Retrieve (or allocate) the client-side directory cache entry */
|
||||
@REQ(get_directory_cache_entry)
|
||||
obj_handle_t handle; /* handle to the directory */
|
||||
@REPLY
|
||||
int entry; /* cache entry on the client side */
|
||||
VARARG(free,ints); /* entries that can be freed */
|
||||
@END
|
||||
|
||||
|
||||
/* Flush a file buffers */
|
||||
@REQ(flush)
|
||||
int blocking; /* whether it's a blocking flush */
|
||||
|
|
|
@ -156,6 +156,7 @@ DECL_HANDLER(open_file_object);
|
|||
DECL_HANDLER(alloc_file_handle);
|
||||
DECL_HANDLER(get_handle_unix_name);
|
||||
DECL_HANDLER(get_handle_fd);
|
||||
DECL_HANDLER(get_directory_cache_entry);
|
||||
DECL_HANDLER(flush);
|
||||
DECL_HANDLER(lock_file);
|
||||
DECL_HANDLER(unlock_file);
|
||||
|
@ -433,6 +434,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
|
|||
(req_handler)req_alloc_file_handle,
|
||||
(req_handler)req_get_handle_unix_name,
|
||||
(req_handler)req_get_handle_fd,
|
||||
(req_handler)req_get_directory_cache_entry,
|
||||
(req_handler)req_flush,
|
||||
(req_handler)req_lock_file,
|
||||
(req_handler)req_unlock_file,
|
||||
|
@ -975,6 +977,10 @@ C_ASSERT( FIELD_OFFSET(struct get_handle_fd_reply, cacheable) == 12 );
|
|||
C_ASSERT( FIELD_OFFSET(struct get_handle_fd_reply, access) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct get_handle_fd_reply, options) == 20 );
|
||||
C_ASSERT( sizeof(struct get_handle_fd_reply) == 24 );
|
||||
C_ASSERT( FIELD_OFFSET(struct get_directory_cache_entry_request, handle) == 12 );
|
||||
C_ASSERT( sizeof(struct get_directory_cache_entry_request) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct get_directory_cache_entry_reply, entry) == 8 );
|
||||
C_ASSERT( sizeof(struct get_directory_cache_entry_reply) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct flush_request, blocking) == 12 );
|
||||
C_ASSERT( FIELD_OFFSET(struct flush_request, async) == 16 );
|
||||
C_ASSERT( sizeof(struct flush_request) == 56 );
|
||||
|
|
|
@ -1717,6 +1717,17 @@ static void dump_get_handle_fd_reply( const struct get_handle_fd_reply *req )
|
|||
fprintf( stderr, ", options=%08x", req->options );
|
||||
}
|
||||
|
||||
static void dump_get_directory_cache_entry_request( const struct get_directory_cache_entry_request *req )
|
||||
{
|
||||
fprintf( stderr, " handle=%04x", req->handle );
|
||||
}
|
||||
|
||||
static void dump_get_directory_cache_entry_reply( const struct get_directory_cache_entry_reply *req )
|
||||
{
|
||||
fprintf( stderr, " entry=%d", req->entry );
|
||||
dump_varargs_ints( ", free=", cur_size );
|
||||
}
|
||||
|
||||
static void dump_flush_request( const struct flush_request *req )
|
||||
{
|
||||
fprintf( stderr, " blocking=%d", req->blocking );
|
||||
|
@ -4344,6 +4355,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
|
|||
(dump_func)dump_alloc_file_handle_request,
|
||||
(dump_func)dump_get_handle_unix_name_request,
|
||||
(dump_func)dump_get_handle_fd_request,
|
||||
(dump_func)dump_get_directory_cache_entry_request,
|
||||
(dump_func)dump_flush_request,
|
||||
(dump_func)dump_lock_file_request,
|
||||
(dump_func)dump_unlock_file_request,
|
||||
|
@ -4618,6 +4630,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
|||
(dump_func)dump_alloc_file_handle_reply,
|
||||
(dump_func)dump_get_handle_unix_name_reply,
|
||||
(dump_func)dump_get_handle_fd_reply,
|
||||
(dump_func)dump_get_directory_cache_entry_reply,
|
||||
(dump_func)dump_flush_reply,
|
||||
(dump_func)dump_lock_file_reply,
|
||||
NULL,
|
||||
|
@ -4892,6 +4905,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
|
|||
"alloc_file_handle",
|
||||
"get_handle_unix_name",
|
||||
"get_handle_fd",
|
||||
"get_directory_cache_entry",
|
||||
"flush",
|
||||
"lock_file",
|
||||
"unlock_file",
|
||||
|
|
Loading…
Reference in New Issue