This MR supercedes !1895 and !3073 as a solution to bug [#50771](https://bugs.winehq.org/show_bug.cgi?id=50771). Rather than fixing the problem of Wine's inability to modify the attributes of read-only files, this patch set instead implements `FileDispositionInformationEx` which the Msys2 and Cygwin runtime libriries can use to avoid needing to modify attributes.
All the flags of `FileDispositionInformationEx` are implemented with the exception of `FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK`.
This MR improves upon !3073 by implementing `FILE_DISPOSITION_POSIX_SEMANTICS` and `FILE_DISPOSITION_ON_CLOSE` where the previous MR only implemented `FILE_DISPOSITION_DELETE` and `FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE`.
-- v3: ntdll: Implement FILE_DISPOSITION_POSIX_SEMANTICS server: Replace unlink member of closed_fd with disp_flags ntdll/tests: Add tests for FILE_DISPOSITION_POSIX_SEMANTICS ntdll: Implement FILE_DISPOSITION_ON_CLOSE ntdll/tests: Add tests for FILE_DISPOSITION_ON_CLOSE ntdll: Implement FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE ntdll/tests: Add tests for FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE ntdll: Initial implementation of FileDispositionInformationEx include: Define FILE_DISPOSITION_INFORMATION_EX and friends
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/unix/file.c | 1 + include/winternl.h | 12 ++++++++++++ 2 files changed, 13 insertions(+)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index ff1618f31fd..aba4e1c694b 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4336,6 +4336,7 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, 0, /* FileReplaceCompletionInformation */ 0, /* FileHardLinkFullIdInformation */ 0, /* FileIdExtdBothDirectoryInformation */ + 0, /* FileDispositionInformationEx */ };
struct stat st; diff --git a/include/winternl.h b/include/winternl.h index 455e199f3e1..1549f5b85f4 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -1255,6 +1255,7 @@ typedef enum _FILE_INFORMATION_CLASS { FileReplaceCompletionInformation, FileHardLinkFullIdInformation, FileIdExtdBothDirectoryInformation, + FileDispositionInformationEx, FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
@@ -1424,6 +1425,17 @@ typedef struct _FILE_DISPOSITION_INFORMATION { BOOLEAN DoDeleteFile; } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
+typedef struct _FILE_DISPOSITION_INFORMATION_EX { + ULONG Flags; +} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX; + +#define FILE_DISPOSITION_DO_NOT_DELETE 0x00000000 +#define FILE_DISPOSITION_DELETE 0x00000001 +#define FILE_DISPOSITION_POSIX_SEMANTICS 0x00000002 +#define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x00000004 +#define FILE_DISPOSITION_ON_CLOSE 0x00000008 +#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x00000010 + typedef struct _FILE_POSITION_INFORMATION { LARGE_INTEGER CurrentByteOffset; } FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
From: Joel Holdsworth joel@airwebreathe.org.uk
This is required by Msys2 when running gpg-agent.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54996 Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/unix/file.c | 27 ++++++++++++++++++++++++++- include/wine/server_protocol.h | 4 ++-- server/fd.c | 8 ++++---- server/protocol.def | 2 +- server/request.h | 2 +- server/trace.c | 2 +- 6 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index aba4e1c694b..56b59413327 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4755,7 +4755,32 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, SERVER_START_REQ( set_fd_disp_info ) { req->handle = wine_server_obj_handle( handle ); - req->unlink = info->DoDeleteFile; + req->flags = info->DoDeleteFile ? FILE_DISPOSITION_DELETE : FILE_DISPOSITION_DO_NOT_DELETE; + status = wine_server_call( req ); + } + SERVER_END_REQ; + } + else status = STATUS_INVALID_PARAMETER_3; + break; + + case FileDispositionInformationEx: + if (len >= sizeof(FILE_DISPOSITION_INFORMATION_EX)) + { + FILE_DISPOSITION_INFORMATION_EX *info = ptr; + + if (info->Flags & FILE_DISPOSITION_POSIX_SEMANTICS) + FIXME( "FILE_DISPOSITION_POSIX_SEMANTICS not supported\n" ); + if (info->Flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) + FIXME( "FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK not supported\n" ); + if (info->Flags & FILE_DISPOSITION_ON_CLOSE) + FIXME( "FILE_DISPOSITION_ON_CLOSE not supported\n" ); + if (info->Flags & FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE) + FIXME( "FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE not supported\n" ); + + SERVER_START_REQ( set_fd_disp_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->flags = info->Flags; status = wine_server_call( req ); } SERVER_END_REQ; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index d765697277b..1701a3e0420 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5230,7 +5230,7 @@ struct set_fd_disp_info_request { struct request_header __header; obj_handle_t handle; - int unlink; + unsigned int flags; char __pad_20[4]; }; struct set_fd_disp_info_reply @@ -6418,7 +6418,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 777 +#define SERVER_PROTOCOL_VERSION 778
/* ### protocol_version end ### */
diff --git a/server/fd.c b/server/fd.c index eaebe044f37..9388d36dcfd 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2463,7 +2463,7 @@ static int is_dir_empty( int fd ) }
/* set disposition for the fd */ -static void set_fd_disposition( struct fd *fd, int unlink ) +static void set_fd_disposition( struct fd *fd, unsigned int flags ) { struct stat st;
@@ -2479,7 +2479,7 @@ static void set_fd_disposition( struct fd *fd, int unlink ) return; }
- if (unlink) + if (flags & FILE_DISPOSITION_DELETE) { struct fd *fd_ptr;
@@ -2524,7 +2524,7 @@ static void set_fd_disposition( struct fd *fd, int unlink ) } }
- fd->closed->unlink = unlink ? 1 : 0; + fd->closed->unlink = (flags & FILE_DISPOSITION_DELETE) ? 1 : 0; if (fd->options & FILE_DELETE_ON_CLOSE) fd->closed->unlink = -1; } @@ -2955,7 +2955,7 @@ DECL_HANDLER(set_fd_disp_info) struct fd *fd = get_handle_fd_obj( current->process, req->handle, DELETE ); if (fd) { - set_fd_disposition( fd, req->unlink ); + set_fd_disposition( fd, req->flags ); release_object( fd ); } } diff --git a/server/protocol.def b/server/protocol.def index 54059b6b580..b690817ef1d 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3641,7 +3641,7 @@ struct handle_info /* set fd disposition information */ @REQ(set_fd_disp_info) obj_handle_t handle; /* handle to a file or directory */ - int unlink; /* whether to unlink file on close */ + unsigned int flags; /* what actions should be taken when deleting a file */ @END
diff --git a/server/request.h b/server/request.h index 20e27ebdce1..0e54dc30b85 100644 --- a/server/request.h +++ b/server/request.h @@ -2207,7 +2207,7 @@ C_ASSERT( FIELD_OFFSET(struct set_fd_completion_mode_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_fd_completion_mode_request, flags) == 16 ); C_ASSERT( sizeof(struct set_fd_completion_mode_request) == 24 ); C_ASSERT( FIELD_OFFSET(struct set_fd_disp_info_request, handle) == 12 ); -C_ASSERT( FIELD_OFFSET(struct set_fd_disp_info_request, unlink) == 16 ); +C_ASSERT( FIELD_OFFSET(struct set_fd_disp_info_request, flags) == 16 ); C_ASSERT( sizeof(struct set_fd_disp_info_request) == 24 ); C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, rootdir) == 16 ); diff --git a/server/trace.c b/server/trace.c index 4dbc5de352f..4242e70a835 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4360,7 +4360,7 @@ static void dump_set_fd_completion_mode_request( const struct set_fd_completion_ static void dump_set_fd_disp_info_request( const struct set_fd_disp_info_request *req ) { fprintf( stderr, " handle=%04x", req->handle ); - fprintf( stderr, ", unlink=%d", req->unlink ); + fprintf( stderr, ", flags=%08x", req->flags ); }
static void dump_set_fd_name_info_request( const struct set_fd_name_info_request *req )
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index b96b2e5b072..6e3f9250a12 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2957,6 +2957,7 @@ static void test_file_disposition_information(void) NTSTATUS res; IO_STATUS_BLOCK io; FILE_DISPOSITION_INFORMATION fdi; + FILE_DISPOSITION_INFORMATION_EX fdie; FILE_STANDARD_INFORMATION fsi; BOOL fileDeleted; DWORD fdi2, size; @@ -3089,6 +3090,27 @@ static void test_file_disposition_information(void) SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); DeleteFileA( buffer );
+ /* set disposition on readonly file ignoring readonly attribute */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + DeleteFileA( buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + todo_wine + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine + ok( fileDeleted, "File should have been deleted\n" ); + } + SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); + DeleteFileA( buffer ); + /* can set disposition on file and then reset it */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0);
From: Joel Holdsworth joel@airwebreathe.org.uk
Both the Msys2 and Cygwin runtimes make use of FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE in their implementations of the unlink() system call. This enables these routines to delete a read-only file without first modifying the attributes.
https://github.com/msys2/msys2-runtime/blob/msys2-3.4.3/winsup/cygwin/syscal... https://www.cygwin.com/git/?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/sysca...
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50771 Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 2 -- dlls/ntdll/unix/file.c | 2 -- server/fd.c | 3 ++- 3 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 6e3f9250a12..813ef0a3cbf 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3098,14 +3098,12 @@ static void test_file_disposition_information(void) ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE; res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); - todo_wine ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); CloseHandle( handle ); if ( res == STATUS_SUCCESS ) { fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( fileDeleted, "File should have been deleted\n" ); } SetFileAttributesA( buffer, FILE_ATTRIBUTE_NORMAL ); diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 56b59413327..22046e63a21 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4774,8 +4774,6 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, FIXME( "FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK not supported\n" ); if (info->Flags & FILE_DISPOSITION_ON_CLOSE) FIXME( "FILE_DISPOSITION_ON_CLOSE not supported\n" ); - if (info->Flags & FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE) - FIXME( "FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE not supported\n" );
SERVER_START_REQ( set_fd_disp_info ) { diff --git a/server/fd.c b/server/fd.c index 9388d36dcfd..942585b06ae 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2499,7 +2499,8 @@ static void set_fd_disposition( struct fd *fd, unsigned int flags ) } if (S_ISREG( st.st_mode )) /* can't unlink files we don't have permission to write */ { - if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + if (!(flags & FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE) && + !(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) { set_error( STATUS_CANNOT_DELETE ); return;
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 813ef0a3cbf..06281f70a3d 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3150,6 +3150,24 @@ static void test_file_disposition_information(void) fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "File should have been deleted\n" );
+ /* can reset delete-on-close flag through FileDispositionInformationEx */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + fdie.Flags = FILE_DISPOSITION_ON_CLOSE; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + DeleteFileA( buffer ); + } + /* DeleteFile fails for wrong sharing mode */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0);
From: Joel Holdsworth joel@airwebreathe.org.uk
The FILE_DELEETE_ON_CLOSE can be state with the FILE_DISPOSITION_ON_CLOSE flag of FileDispositionInformationEx.
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 1 - dlls/ntdll/unix/file.c | 2 -- server/fd.c | 3 +++ 3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 06281f70a3d..6883e31ebb5 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3163,7 +3163,6 @@ static void test_file_disposition_information(void) if ( res == STATUS_SUCCESS ) { fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( !fileDeleted, "File shouldn't have been deleted\n" ); DeleteFileA( buffer ); } diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 22046e63a21..91ba408bc95 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4772,8 +4772,6 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, FIXME( "FILE_DISPOSITION_POSIX_SEMANTICS not supported\n" ); if (info->Flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) FIXME( "FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK not supported\n" ); - if (info->Flags & FILE_DISPOSITION_ON_CLOSE) - FIXME( "FILE_DISPOSITION_ON_CLOSE not supported\n" );
SERVER_START_REQ( set_fd_disp_info ) { diff --git a/server/fd.c b/server/fd.c index 942585b06ae..dc35999c3f3 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2525,6 +2525,9 @@ static void set_fd_disposition( struct fd *fd, unsigned int flags ) } }
+ if (flags & FILE_DISPOSITION_ON_CLOSE) + fd->options &= ~FILE_DELETE_ON_CLOSE; + fd->closed->unlink = (flags & FILE_DISPOSITION_DELETE) ? 1 : 0; if (fd->options & FILE_DELETE_ON_CLOSE) fd->closed->unlink = -1;
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 6883e31ebb5..0da10e4a3da 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3061,6 +3061,47 @@ static void test_file_disposition_information(void) fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( fileDeleted, "File should have been deleted\n" );
+ /* file is deleted after handle with FILE_DISPOSITION_POSIX_SEMANTICS is closed */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open temp file\n" ); + fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine + ok( fileDeleted, "File should have been deleted\n" ); + } + CloseHandle( handle2 ); + + /* file is deleted after handle with FILE_DISPOSITION_POSIX_SEMANTICS is closed */ + /* FileDispositionInformationEx is only supported on Windows 10 build 1809 and later */ + GetTempFileNameA( tmp_path, "dis", 0, buffer ); + handle = CreateFileA(buffer, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0); + ok( handle != INVALID_HANDLE_VALUE, "failed to create temp file\n" ); + handle2 = CreateFileA(buffer, DELETE, FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); + ok( handle2 != INVALID_HANDLE_VALUE, "failed to open temp file\n" ); + fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS; + res = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok( res == STATUS_INVALID_INFO_CLASS || res == STATUS_SUCCESS, + "unexpected FileDispositionInformationEx result (expected STATUS_SUCCESS or SSTATUS_INVALID_INFO_CLASS, got %lx)\n", res ); + CloseHandle( handle2 ); + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "File shouldn't have been deleted\n" ); + CloseHandle( handle ); + if ( res == STATUS_SUCCESS ) + { + fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "File should have been deleted\n" ); + } + /* cannot set disposition on readonly file */ GetTempFileNameA( tmp_path, "dis", 0, buffer ); DeleteFileA( buffer );
From: Joel Holdsworth joel@airwebreathe.org.uk
In order to implement FILE_DISPOSITION_POSIX_SEMANTICS, it will be necessary to add additional flags to closed_fd. In preparation for this, the unlink member variable has been replaced with disp_flags which directly reflects the flags defined in the FILE_DISPOSITION_INFORMATION_EX structure.
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- server/fd.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/server/fd.c b/server/fd.c index dc35999c3f3..6ce3343fb6f 100644 --- a/server/fd.c +++ b/server/fd.c @@ -160,10 +160,10 @@ static inline int epoll_wait( int epfd, struct epoll_event *events, int maxevent /* closed_fd is used to keep track of the unix fd belonging to a closed fd object */ struct closed_fd { - struct list entry; /* entry in inode closed list */ - int unix_fd; /* the unix file descriptor */ - int unlink; /* whether to unlink on close: -1 - implicit FILE_DELETE_ON_CLOSE, 1 - explicit disposition */ - char *unix_name; /* name to unlink on close, points to parent fd unix_name */ + struct list entry; /* entry in inode closed list */ + int unix_fd; /* the unix file descriptor */ + unsigned int disp_flags; /* the disposition flags */ + char *unix_name; /* name to unlink on close, points to parent fd unix_name */ };
struct fd @@ -1122,7 +1122,9 @@ static void inode_close_pending( struct inode *inode, int keep_unlinks ) close( fd->unix_fd ); fd->unix_fd = -1; } - if (!keep_unlinks || !fd->unlink) /* get rid of it unless there's an unlink pending on that file */ + + /* get rid of it unless there's an unlink pending on that file */ + if (!keep_unlinks || !(fd->disp_flags & FILE_DISPOSITION_DELETE)) { list_remove( ptr ); free( fd->unix_name ); @@ -1155,7 +1157,7 @@ static void inode_destroy( struct object *obj ) struct closed_fd *fd = LIST_ENTRY( ptr, struct closed_fd, entry ); list_remove( ptr ); if (fd->unix_fd != -1) close( fd->unix_fd ); - if (fd->unlink) + if (fd->disp_flags & FILE_DISPOSITION_DELETE) { /* make sure it is still the same file */ struct stat st; @@ -1211,8 +1213,9 @@ static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd ) { list_add_head( &inode->closed, &fd->entry ); } - else if (fd->unlink) /* close the fd but keep the structure around for unlink */ + else if (fd->disp_flags & FILE_DISPOSITION_ON_CLOSE) { + /* close the fd but keep the structure around for unlink */ if (fd->unix_fd != -1) close( fd->unix_fd ); fd->unix_fd = -1; list_add_head( &inode->closed, &fd->entry ); @@ -1560,7 +1563,7 @@ static void fd_dump( struct object *obj, int verbose ) { struct fd *fd = (struct fd *)obj; fprintf( stderr, "Fd unix_fd=%d user=%p options=%08x", fd->unix_fd, fd->user, fd->options ); - if (fd->inode) fprintf( stderr, " inode=%p unlink=%d", fd->inode, fd->closed->unlink ); + if (fd->inode) fprintf( stderr, " inode=%p disp_flags=%x", fd->inode, fd->closed->disp_flags ); fprintf( stderr, "\n" ); }
@@ -1673,7 +1676,7 @@ static inline void unmount_fd( struct fd *fd ) fd->unix_fd = -1; fd->no_fd_status = STATUS_VOLUME_DISMOUNTED; fd->closed->unix_fd = -1; - fd->closed->unlink = 0; + fd->closed->disp_flags = 0;
/* stop using Unix locks on this fd (existing locks have been removed by close) */ fd->fs_locks = 0; @@ -1783,7 +1786,7 @@ struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sha goto failed; } closed->unix_fd = fd->unix_fd; - closed->unlink = 0; + closed->disp_flags = 0; closed->unix_name = fd->unix_name; fd->closed = closed; fd->inode = (struct inode *)grab_object( orig->inode ); @@ -1977,7 +1980,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam }
closed_fd->unix_fd = fd->unix_fd; - closed_fd->unlink = 0; + closed_fd->disp_flags = 0; closed_fd->unix_name = fd->unix_name; fstat( fd->unix_fd, &st ); *mode = st.st_mode; @@ -2026,7 +2029,8 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam goto error; }
- fd->closed->unlink = (options & FILE_DELETE_ON_CLOSE) ? -1 : 0; + fd->closed->disp_flags = (options & FILE_DELETE_ON_CLOSE) ? + FILE_DISPOSITION_DELETE : 0; if (flags & O_TRUNC) { if (S_ISDIR(st.st_mode)) @@ -2528,9 +2532,8 @@ static void set_fd_disposition( struct fd *fd, unsigned int flags ) if (flags & FILE_DISPOSITION_ON_CLOSE) fd->options &= ~FILE_DELETE_ON_CLOSE;
- fd->closed->unlink = (flags & FILE_DISPOSITION_DELETE) ? 1 : 0; - if (fd->options & FILE_DELETE_ON_CLOSE) - fd->closed->unlink = -1; + fd->closed->disp_flags = (flags & FILE_DISPOSITION_DELETE) | + ((fd->options & FILE_DELETE_ON_CLOSE) ? FILE_DISPOSITION_DELETE : 0); }
/* set new name for the fd */
From: Joel Holdsworth joel@airwebreathe.org.uk
Both the Msys2 and Cygwin runtimes make use of FILE_DISPOSITON_POSIX_SEMANTICS in their implementations of the unlink() system call. This enables these routines to behave similarly to POSIX where are unlisted from the directory, if handles are still open.
https://github.com/msys2/msys2-runtime/blob/msys2-3.4.3/winsup/cygwin/syscal... https://www.cygwin.com/git/?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/sysca...
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 1 - dlls/ntdll/unix/file.c | 2 -- server/fd.c | 36 ++++++++++++++++++++++++------------ 3 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 0da10e4a3da..40793e2db05 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3076,7 +3076,6 @@ static void test_file_disposition_information(void) if ( res == STATUS_SUCCESS ) { fileDeleted = GetFileAttributesA( buffer ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( fileDeleted, "File should have been deleted\n" ); } CloseHandle( handle2 ); diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 91ba408bc95..eef21e12ceb 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4768,8 +4768,6 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, { FILE_DISPOSITION_INFORMATION_EX *info = ptr;
- if (info->Flags & FILE_DISPOSITION_POSIX_SEMANTICS) - FIXME( "FILE_DISPOSITION_POSIX_SEMANTICS not supported\n" ); if (info->Flags & FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK) FIXME( "FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK not supported\n" );
diff --git a/server/fd.c b/server/fd.c index 6ce3343fb6f..0b0e91ebfbb 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1103,10 +1103,20 @@ static void device_destroy( struct object *obj ) list_remove( &device->entry ); /* remove it from the hash table */ }
- /****************************************************************/ /* inode functions */
+static void unlink_closed_fd( struct inode *inode, struct closed_fd *fd ) +{ + /* make sure it is still the same file */ + struct stat st; + if (!stat( fd->unix_name, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino) + { + if (S_ISDIR(st.st_mode)) rmdir( fd->unix_name ); + else unlink( fd->unix_name ); + } +} + /* close all pending file descriptors in the closed list */ static void inode_close_pending( struct inode *inode, int keep_unlinks ) { @@ -1158,15 +1168,7 @@ static void inode_destroy( struct object *obj ) list_remove( ptr ); if (fd->unix_fd != -1) close( fd->unix_fd ); if (fd->disp_flags & FILE_DISPOSITION_DELETE) - { - /* make sure it is still the same file */ - struct stat st; - if (!stat( fd->unix_name, &st ) && st.st_dev == inode->device->dev && st.st_ino == inode->ino) - { - if (S_ISDIR(st.st_mode)) rmdir( fd->unix_name ); - else unlink( fd->unix_name ); - } - } + unlink_closed_fd( inode, fd ); free( fd->unix_name ); free( fd ); } @@ -1213,7 +1215,16 @@ static void inode_add_closed_fd( struct inode *inode, struct closed_fd *fd ) { list_add_head( &inode->closed, &fd->entry ); } - else if (fd->disp_flags & FILE_DISPOSITION_ON_CLOSE) + else if ((fd->disp_flags & FILE_DISPOSITION_DELETE) && + (fd->disp_flags & FILE_DISPOSITION_POSIX_SEMANTICS)) + { + /* close the fd and unlink it at once */ + if (fd->unix_fd != -1) close( fd->unix_fd ); + unlink_closed_fd( inode, fd ); + free( fd->unix_name ); + free( fd ); + } + else if (fd->disp_flags & FILE_DISPOSITION_DELETE) { /* close the fd but keep the structure around for unlink */ if (fd->unix_fd != -1) close( fd->unix_fd ); @@ -2532,7 +2543,8 @@ static void set_fd_disposition( struct fd *fd, unsigned int flags ) if (flags & FILE_DISPOSITION_ON_CLOSE) fd->options &= ~FILE_DELETE_ON_CLOSE;
- fd->closed->disp_flags = (flags & FILE_DISPOSITION_DELETE) | + fd->closed->disp_flags = + (flags & (FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS)) | ((fd->options & FILE_DELETE_ON_CLOSE) ? FILE_DISPOSITION_DELETE : 0); }
v3 version, now adds missing `FileDispositionInformationEx` case to the `info_sizes` table in `NtQueryInformationFile`