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`.
-- v2: 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
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- include/winternl.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)
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 ff1618f31fd..3e930f1ac2f 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4754,7 +4754,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 3e930f1ac2f..8352cd6722b 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4773,8 +4773,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 8352cd6722b..90760588e63 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4771,8 +4771,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 90760588e63..b2219645478 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4767,8 +4767,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); }
On Tue Jun 20 20:24:49 2023 +0000, **** wrote:
Marvin replied on the mailing list:
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details: The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=133988 Your paranoid android. === w7u_2qxl (32 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w7u_adm (32 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w7u_el (32 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w8 (32 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w8adm (32 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w864 (32 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w1064v1507 (32 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w7pro64 (64 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w864 (64 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted === w1064v1507 (64 bit report) === ntdll: file.c:3099: Test failed: File should have been deleted
Fixed in v2
This merge request was approved by Zebediah Figura.
Thank you @zfigura . We're getting there; one step at a time!