From 28418cc91900ae8e5497e69fac2392779b8f4bd5 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 2 Nov 2006 20:48:19 +0100 Subject: [PATCH] ntdll: Maintain a file descriptor cache on the client side. --- dlls/ntdll/file.c | 2 +- dlls/ntdll/ntdll_misc.h | 1 + dlls/ntdll/om.c | 7 +- dlls/ntdll/server.c | 119 +++++++++++++++++++++++++++------ include/wine/server_protocol.h | 8 +-- server/fd.c | 12 ++-- server/handle.c | 6 +- server/protocol.def | 6 +- server/trace.c | 7 +- 9 files changed, 122 insertions(+), 46 deletions(-) diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index a2157d3c75a..eecce673594 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -1093,7 +1093,7 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc { req->handle = handle; io->u.Status = wine_server_call(req); - if (!io->u.Status && reply->fd != -1) close(reply->fd); + if (!io->u.Status) server_remove_fd_from_cache( handle ); } SERVER_END_REQ; break; diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 03110a43d61..7ce4b02aac7 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -61,6 +61,7 @@ extern void DECLSPEC_NORETURN server_protocol_error( const char *err, ... ); extern void DECLSPEC_NORETURN server_protocol_perror( const char *err ); extern void DECLSPEC_NORETURN server_exit_thread( int status ); extern void DECLSPEC_NORETURN server_abort_thread( int status ); +extern int server_remove_fd_from_cache( obj_handle_t handle ); /* module handling */ extern NTSTATUS MODULE_DllThreadAttach( LPVOID lpReserved ); diff --git a/dlls/ntdll/om.c b/dlls/ntdll/om.c index 627cde4462e..5d2a7d678ce 100644 --- a/dlls/ntdll/om.c +++ b/dlls/ntdll/om.c @@ -312,7 +312,10 @@ NTSTATUS WINAPI NtDuplicateObject( HANDLE source_process, HANDLE source, if (!(ret = wine_server_call( req ))) { if (dest) *dest = reply->handle; - if (reply->fd != -1) close( reply->fd ); + if (reply->closed) + server_remove_fd_from_cache( source ); + else if (options & DUPLICATE_CLOSE_SOURCE) + WARN( "failed to close handle %p in process %p\n", source, source_process ); } } SERVER_END_REQ; @@ -338,7 +341,7 @@ NTSTATUS WINAPI NtClose( HANDLE Handle ) { req->handle = Handle; ret = wine_server_call( req ); - if (!ret && reply->fd != -1) close( reply->fd ); + if (!ret) server_remove_fd_from_cache( Handle ); } SERVER_END_REQ; return ret; diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c index 272a441afc8..a41fb69f4dd 100644 --- a/dlls/ntdll/server.c +++ b/dlls/ntdll/server.c @@ -438,6 +438,92 @@ static int receive_fd( obj_handle_t *handle ) } +inline static unsigned int handle_to_index( obj_handle_t handle ) +{ + return ((unsigned long)handle >> 2) - 1; +} + +static int *fd_cache; +static unsigned int fd_cache_size; + +/*********************************************************************** + * add_fd_to_cache + * + * Caller must hold fd_cache_section. + */ +static int add_fd_to_cache( obj_handle_t handle, int fd ) +{ + unsigned int idx = handle_to_index( handle ); + + if (idx >= fd_cache_size) + { + unsigned int i, size = max( 32, fd_cache_size * 2 ); + int *new_cache; + + if (size <= idx) size = idx + 1; + if (fd_cache) + new_cache = RtlReAllocateHeap( GetProcessHeap(), 0, fd_cache, size*sizeof(fd_cache[0]) ); + else + new_cache = RtlAllocateHeap( GetProcessHeap(), 0, size*sizeof(fd_cache[0]) ); + + if (new_cache) + { + for (i = fd_cache_size; i < size; i++) new_cache[i] = -1; + fd_cache = new_cache; + fd_cache_size = size; + } + } + if (idx < fd_cache_size) + { + assert( fd_cache[idx] == -1 ); + fd_cache[idx] = fd; + TRACE("added %p (%d) to cache\n", handle, fd ); + return 1; + } + return 0; +} + + +/*********************************************************************** + * get_cached_fd + * + * Caller must hold fd_cache_section. + */ +static inline int get_cached_fd( obj_handle_t handle ) +{ + unsigned int idx = handle_to_index( handle ); + int fd = -1; + + if (idx < fd_cache_size) fd = fd_cache[idx]; + return fd; +} + + +/*********************************************************************** + * server_remove_fd_from_cache + */ +int server_remove_fd_from_cache( obj_handle_t handle ) +{ + unsigned int idx = handle_to_index( handle ); + int fd = -1; + + RtlEnterCriticalSection( &fd_cache_section ); + if (idx < fd_cache_size) + { + fd = fd_cache[idx]; + fd_cache[idx] = -1; + } + RtlLeaveCriticalSection( &fd_cache_section ); + + if (fd != -1) + { + close( fd ); + TRACE("removed %p (%d) from cache\n", handle, fd ); + } + return fd; +} + + /*********************************************************************** * wine_server_fd_to_handle (NTDLL.@) * @@ -488,20 +574,27 @@ int wine_server_fd_to_handle( int fd, unsigned int access, unsigned int attribut int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *unix_fd, int *flags ) { obj_handle_t fd_handle; - int ret, removable = -1, fd = -1; + int ret = 0, removable = 0, fd = -1; RtlEnterCriticalSection( &fd_cache_section ); *unix_fd = -1; + fd = get_cached_fd( handle ); + if (fd != -1 && !flags) + { + if ((fd = dup(fd)) == -1) ret = FILE_GetNtStatus(); + goto done; + } + SERVER_START_REQ( get_handle_fd ) { req->handle = handle; req->access = access; + req->cached = (fd != -1); if (!(ret = wine_server_call( req ))) { - fd = reply->fd; - removable = reply->removable; + removable = reply->flags & FD_FLAG_REMOVABLE; if (flags) *flags = reply->flags; } } @@ -525,26 +618,10 @@ int wine_server_handle_to_fd( obj_handle_t handle, unsigned int access, int *uni if (removable) goto done; /* don't cache it */ - /* and store it back into the cache */ - SERVER_START_REQ( set_handle_fd ) + if (add_fd_to_cache( handle, fd )) { - req->handle = fd_handle; - req->fd = fd; - if (!(ret = wine_server_call( req ))) - { - if (reply->cur_fd != -1) /* it has been cached */ - { - if (reply->cur_fd != fd) close( fd ); /* someone was here before us */ - if ((fd = dup(reply->cur_fd)) == -1) ret = FILE_GetNtStatus(); - } - } - else - { - close( fd ); - fd = -1; - } + if ((fd = dup(fd)) == -1) ret = FILE_GetNtStatus(); } - SERVER_END_REQ; done: RtlLeaveCriticalSection( &fd_cache_section ); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index a707d8f45f6..b3926512b23 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -585,7 +585,7 @@ struct dup_handle_reply { struct reply_header __header; obj_handle_t handle; - int fd; + int closed; }; #define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE #define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS @@ -840,12 +840,11 @@ struct get_handle_fd_request struct request_header __header; obj_handle_t handle; unsigned int access; + int cached; }; struct get_handle_fd_reply { struct reply_header __header; - int fd; - int removable; int flags; }; #define FD_FLAG_OVERLAPPED 0x01 @@ -854,6 +853,7 @@ struct get_handle_fd_reply #define FD_FLAG_SEND_SHUTDOWN 0x08 #define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation, * only handle available data (don't wait) */ +#define FD_FLAG_REMOVABLE 0x20 struct set_handle_fd_request @@ -4425,6 +4425,6 @@ union generic_reply struct query_symlink_reply query_symlink_reply; }; -#define SERVER_PROTOCOL_VERSION 256 +#define SERVER_PROTOCOL_VERSION 257 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/fd.c b/server/fd.c index 03347238cae..25a2fe0ba1b 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1944,19 +1944,15 @@ DECL_HANDLER(get_handle_fd) { struct fd *fd; - reply->fd = -1; - if ((fd = get_handle_fd_obj( current->process, req->handle, req->access ))) { - int unix_fd = get_unix_fd( fd ); - if (unix_fd != -1) + if (!req->cached) { - int cached_fd = get_handle_unix_fd( current->process, req->handle, req->access ); - if (cached_fd != -1) reply->fd = cached_fd; - else if (!get_error()) send_client_fd( current->process, unix_fd, req->handle ); + int unix_fd = get_unix_fd( fd ); + if (unix_fd != -1) send_client_fd( current->process, unix_fd, req->handle ); } - if (fd->inode) reply->removable = fd->inode->device->removable; reply->flags = fd->fd_ops->get_file_info( fd ); + if (fd->inode && fd->inode->device->removable) reply->flags |= FD_FLAG_REMOVABLE; release_object( fd ); } } diff --git a/server/handle.c b/server/handle.c index d7cf12a4a8c..9736ad2dea1 100644 --- a/server/handle.c +++ b/server/handle.c @@ -562,7 +562,6 @@ DECL_HANDLER(dup_handle) struct process *src, *dst; reply->handle = 0; - reply->fd = -1; if ((src = get_process_from_handle( req->src_process, PROCESS_DUP_HANDLE ))) { if (req->options & DUP_HANDLE_MAKE_GLOBAL) @@ -579,8 +578,9 @@ DECL_HANDLER(dup_handle) /* close the handle no matter what happened */ if (req->options & DUP_HANDLE_CLOSE_SOURCE) { - if (src == current->process) close_handle( src, req->src_handle, &reply->fd ); - else close_handle( src, req->src_handle, NULL ); + unsigned int err = get_error(); /* don't overwrite error from the above calls */ + reply->closed = close_handle( src, req->src_handle, NULL ); + set_error( err ); } release_object( src ); } diff --git a/server/protocol.def b/server/protocol.def index 49c4c30575d..c1392491939 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -485,7 +485,7 @@ enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC_IO }; unsigned int options; /* duplicate options (see below) */ @REPLY obj_handle_t handle; /* duplicated handle in dst process */ - int fd; /* associated fd to close */ + int closed; /* whether the source handle has been closed */ @END #define DUP_HANDLE_CLOSE_SOURCE DUPLICATE_CLOSE_SOURCE #define DUP_HANDLE_SAME_ACCESS DUPLICATE_SAME_ACCESS @@ -661,9 +661,8 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; @REQ(get_handle_fd) obj_handle_t handle; /* handle to the file */ unsigned int access; /* wanted access rights */ + int cached; /* is it cached on the client already? */ @REPLY - int fd; /* file descriptor */ - int removable; /* is device removable? (-1 if unknown) */ int flags; /* file read/write flags (see below) */ @END #define FD_FLAG_OVERLAPPED 0x01 /* fd opened in overlapped mode */ @@ -672,6 +671,7 @@ enum event_op { PULSE_EVENT, SET_EVENT, RESET_EVENT }; #define FD_FLAG_SEND_SHUTDOWN 0x08 #define FD_FLAG_AVAILABLE 0x10 /* in overlap read/write operation, * only handle available data (don't wait) */ +#define FD_FLAG_REMOVABLE 0x20 /* is it on a removable device? */ /* Set the cached file descriptor of a handle */ @REQ(set_handle_fd) diff --git a/server/trace.c b/server/trace.c index a16310f24ec..6ed28909c47 100644 --- a/server/trace.c +++ b/server/trace.c @@ -901,7 +901,7 @@ static void dump_dup_handle_request( const struct dup_handle_request *req ) static void dump_dup_handle_reply( const struct dup_handle_reply *req ) { fprintf( stderr, " handle=%p,", req->handle ); - fprintf( stderr, " fd=%d", req->fd ); + fprintf( stderr, " closed=%d", req->closed ); } static void dump_open_process_request( const struct open_process_request *req ) @@ -1103,13 +1103,12 @@ static void dump_alloc_file_handle_reply( const struct alloc_file_handle_reply * static void dump_get_handle_fd_request( const struct get_handle_fd_request *req ) { fprintf( stderr, " handle=%p,", req->handle ); - fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, " access=%08x,", req->access ); + fprintf( stderr, " cached=%d", req->cached ); } static void dump_get_handle_fd_reply( const struct get_handle_fd_reply *req ) { - fprintf( stderr, " fd=%d,", req->fd ); - fprintf( stderr, " removable=%d,", req->removable ); fprintf( stderr, " flags=%d", req->flags ); }