From 0fabfe7d6b30cac1f15c1f70786da6ebe2bffe2e Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Wed, 19 Aug 2015 11:18:01 +0200 Subject: [PATCH] server: Add support for setting file disposition information. Based on a patch by Dmitry Timoshkov. --- dlls/kernel32/tests/file.c | 1 - dlls/ntdll/file.c | 16 +++++++++++++ dlls/ntdll/tests/file.c | 13 ++-------- include/wine/server_protocol.h | 19 ++++++++++++++- server/fd.c | 44 ++++++++++++++++++++++++++++++++++ server/protocol.def | 7 ++++++ server/request.h | 5 ++++ server/trace.c | 9 +++++++ 8 files changed, 101 insertions(+), 13 deletions(-) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 53d225ca124..dd64c910d4a 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -4645,7 +4645,6 @@ todo_wine dispinfo.DeleteFile = TRUE; ret = pSetFileInformationByHandle(file, FileDispositionInfo, &dispinfo, sizeof(dispinfo)); -todo_wine ok(ret, "setting FileDispositionInfo failed, error %d\n", GetLastError()); CloseHandle(file); diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index d081750bdc8..314c233a4d7 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -2765,6 +2765,22 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io, else io->u.Status = STATUS_INVALID_PARAMETER_3; break; + case FileDispositionInformation: + if (len >= sizeof(FILE_DISPOSITION_INFORMATION)) + { + FILE_DISPOSITION_INFORMATION *info = ptr; + + SERVER_START_REQ( set_fd_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->unlink = info->DoDeleteFile; + io->u.Status = wine_server_call( req ); + } + SERVER_END_REQ; + } else + io->u.Status = STATUS_INVALID_PARAMETER_3; + break; + default: FIXME("Unsupported class (%d)\n", class); io->u.Status = STATUS_NOT_IMPLEMENTED; diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 86a4516c0d5..57ae15c038a 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -1473,7 +1473,6 @@ static void test_file_disposition_information(void) ok( res == STATUS_INFO_LENGTH_MISMATCH, "expected STATUS_INFO_LENGTH_MISMATCH, got %x\n", res ); fdi2 = 0x100; res = pNtSetInformationFile( handle, &io, &fdi2, sizeof(fdi2), FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %x\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1488,7 +1487,6 @@ static void test_file_disposition_information(void) ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_NOT_IMPLEMENTED, "Unexpected NtQueryInformationFile result (expected STATUS_INVALID_INFO_CLASS, got %x)\n", res ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_ACCESS_DENIED, "unexpected FileDispositionInformation result (expected STATUS_ACCESS_DENIED, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1501,11 +1499,9 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( fileDeleted, "File should have been deleted\n" ); DeleteFileA( buffer ); @@ -1519,6 +1515,7 @@ static void test_file_disposition_information(void) ok( res == STATUS_CANNOT_DELETE, "unexpected FileDispositionInformation result (expected STATUS_CANNOT_DELETE, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine ok( !fileDeleted, "File shouldn't have been deleted\n" ); SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); DeleteFileA( buffer ); @@ -1529,11 +1526,9 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1546,7 +1541,6 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1561,7 +1555,6 @@ static void test_file_disposition_information(void) CloseHandle( handle ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle2, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); CloseHandle( handle2 ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1576,11 +1569,9 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to open a directory\n" ); fdi.DoDeleteFile = TRUE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( fileDeleted, "Directory should have been deleted\n" ); RemoveDirectoryA( buffer ); @@ -1593,7 +1584,6 @@ static void test_file_disposition_information(void) RemoveDirectoryA( buffer ); fdi.DoDeleteFile = FALSE; res = pNtSetInformationFile( handle, &io, &fdi, sizeof fdi, FileDispositionInformation ); - todo_wine ok( res == STATUS_SUCCESS, "unexpected FileDispositionInformation result (expected STATUS_SUCCESS, got %x)\n", res ); CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -1618,6 +1608,7 @@ static void test_file_disposition_information(void) buffer[dirpos] = '\0'; CloseHandle( handle ); fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine ok( !fileDeleted, "Directory shouldn't have been deleted\n" ); RemoveDirectoryA( buffer ); } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 36f63786b40..d796c674dc1 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5080,6 +5080,20 @@ struct add_fd_completion_reply +struct set_fd_info_request +{ + struct request_header __header; + obj_handle_t handle; + int unlink; + char __pad_20[4]; +}; +struct set_fd_info_reply +{ + struct reply_header __header; +}; + + + struct get_window_layered_info_request { struct request_header __header; @@ -5552,6 +5566,7 @@ enum request REQ_query_completion, REQ_set_completion_info, REQ_add_fd_completion, + REQ_set_fd_info, REQ_get_window_layered_info, REQ_set_window_layered_info, REQ_alloc_user_handle, @@ -5826,6 +5841,7 @@ union generic_request struct query_completion_request query_completion_request; struct set_completion_info_request set_completion_info_request; struct add_fd_completion_request add_fd_completion_request; + struct set_fd_info_request set_fd_info_request; struct get_window_layered_info_request get_window_layered_info_request; struct set_window_layered_info_request set_window_layered_info_request; struct alloc_user_handle_request alloc_user_handle_request; @@ -6098,6 +6114,7 @@ union generic_reply struct query_completion_reply query_completion_reply; struct set_completion_info_reply set_completion_info_reply; struct add_fd_completion_reply add_fd_completion_reply; + struct set_fd_info_reply set_fd_info_reply; struct get_window_layered_info_reply get_window_layered_info_reply; struct set_window_layered_info_reply set_window_layered_info_reply; struct alloc_user_handle_reply alloc_user_handle_reply; @@ -6114,6 +6131,6 @@ union generic_reply struct terminate_job_reply terminate_job_reply; }; -#define SERVER_PROTOCOL_VERSION 484 +#define SERVER_PROTOCOL_VERSION 485 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/fd.c b/server/fd.c index 6b95d6cc351..f607261dbca 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2214,6 +2214,39 @@ static struct fd *get_handle_fd_obj( struct process *process, obj_handle_t handl return fd; } +/* set disposition for the fd */ +static void set_fd_disposition( struct fd *fd, int unlink ) +{ + struct stat st; + + if (!fd->inode) + { + set_error( STATUS_OBJECT_TYPE_MISMATCH ); + return; + } + + if (fd->unix_fd == -1) + { + set_error( fd->no_fd_status ); + return; + } + + if (fstat( fd->unix_fd, &st ) == -1) + { + file_set_error(); + return; + } + + /* can't unlink special files */ + if (unlink && !S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + fd->closed->unlink = unlink || (fd->options & FILE_DELETE_ON_CLOSE); +} + struct completion *fd_get_completion( struct fd *fd, apc_param_t *p_key ) { *p_key = fd->comp_key; @@ -2409,3 +2442,14 @@ DECL_HANDLER(add_fd_completion) release_object( fd ); } } + +/* set fd information */ +DECL_HANDLER(set_fd_info) +{ + struct fd *fd = get_handle_fd_obj( current->process, req->handle, DELETE ); + if (fd) + { + set_fd_disposition( fd, req->unlink ); + release_object( fd ); + } +} diff --git a/server/protocol.def b/server/protocol.def index ffee0c002e4..afb26a530b9 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3525,6 +3525,13 @@ enum coords_relative @END +/* set fd information */ +@REQ(set_fd_info) + obj_handle_t handle; /* handle to a file or directory */ + int unlink; /* whether to unlink file on close */ +@END + + /* Retrieve layered info for a window */ @REQ(get_window_layered_info) user_handle_t handle; /* handle to the window */ diff --git a/server/request.h b/server/request.h index 10e4c9b65e5..1cef7f38222 100644 --- a/server/request.h +++ b/server/request.h @@ -359,6 +359,7 @@ DECL_HANDLER(remove_completion); DECL_HANDLER(query_completion); DECL_HANDLER(set_completion_info); DECL_HANDLER(add_fd_completion); +DECL_HANDLER(set_fd_info); DECL_HANDLER(get_window_layered_info); DECL_HANDLER(set_window_layered_info); DECL_HANDLER(alloc_user_handle); @@ -632,6 +633,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_query_completion, (req_handler)req_set_completion_info, (req_handler)req_add_fd_completion, + (req_handler)req_set_fd_info, (req_handler)req_get_window_layered_info, (req_handler)req_set_window_layered_info, (req_handler)req_alloc_user_handle, @@ -2221,6 +2223,9 @@ C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, cvalue) == 16 ); C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, information) == 24 ); C_ASSERT( FIELD_OFFSET(struct add_fd_completion_request, status) == 32 ); C_ASSERT( sizeof(struct add_fd_completion_request) == 40 ); +C_ASSERT( FIELD_OFFSET(struct set_fd_info_request, handle) == 12 ); +C_ASSERT( FIELD_OFFSET(struct set_fd_info_request, unlink) == 16 ); +C_ASSERT( sizeof(struct set_fd_info_request) == 24 ); C_ASSERT( FIELD_OFFSET(struct get_window_layered_info_request, handle) == 12 ); C_ASSERT( sizeof(struct get_window_layered_info_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_window_layered_info_reply, color_key) == 8 ); diff --git a/server/trace.c b/server/trace.c index 3eb8583db9b..488b6a1ed18 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4136,6 +4136,12 @@ static void dump_add_fd_completion_request( const struct add_fd_completion_reque fprintf( stderr, ", status=%08x", req->status ); } +static void dump_set_fd_info_request( const struct set_fd_info_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", unlink=%d", req->unlink ); +} + static void dump_get_window_layered_info_request( const struct get_window_layered_info_request *req ) { fprintf( stderr, " handle=%08x", req->handle ); @@ -4509,6 +4515,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_query_completion_request, (dump_func)dump_set_completion_info_request, (dump_func)dump_add_fd_completion_request, + (dump_func)dump_set_fd_info_request, (dump_func)dump_get_window_layered_info_request, (dump_func)dump_set_window_layered_info_request, (dump_func)dump_alloc_user_handle_request, @@ -4779,6 +4786,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_query_completion_reply, NULL, NULL, + NULL, (dump_func)dump_get_window_layered_info_reply, NULL, (dump_func)dump_alloc_user_handle_reply, @@ -5049,6 +5057,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "query_completion", "set_completion_info", "add_fd_completion", + "set_fd_info", "get_window_layered_info", "set_window_layered_info", "alloc_user_handle",