-- v2: ntdll: Add support for FILE_{RENAME,LINK}_POSIX_SEMANTICS. ntdll: Factored out get_inode_open_sharing ntdll/test: Add tests for FILE_LINK_POSIX_SEMANTICS. ntdll/test: Add tests for FILE_RENAME_POSIX_SEMANTICS. ntdll: Add support for FILE_{RENAME,LINK}_IGNORE_READONLY_ATTRIBUTE. server: Don't allow read-only files to be replaced by File{Rename,Link}Information{,Ex}. ntdll/test: Add tests for FILE_LINK_IGNORE_READONLY_ATTRIBUTE. ntdll/test: Add tests for FILE_RENAME_IGNORE_READONLY_ATTRIBUTE. ntdll/test: Use FileDispositionInformationEx to delete files and directories. ntdll: Initial implementation of FileLinkInformationEx. ntdll: Initial implementation of FileRenameInformationEx.
From: Joel Holdsworth joel@airwebreathe.org.uk
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54997 Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 48 +++------------------------------- dlls/ntdll/unix/file.c | 14 ++++++++-- include/wine/server_protocol.h | 4 +-- server/fd.c | 5 ++-- server/protocol.def | 2 +- server/request.h | 2 +- server/trace.c | 2 +- 7 files changed, 23 insertions(+), 54 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 1fcffe36a35..da4f817b1da 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -1549,29 +1549,24 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
if (class == FileRenameInformationEx && (res == STATUS_NOT_IMPLEMENTED || res == STATUS_INVALID_INFO_CLASS)) { - todo_wine win_skip( "FileRenameInformationEx not supported\n" ); + win_skip( "FileRenameInformationEx not supported\n" ); CloseHandle( handle ); HeapFree( GetProcessHeap(), 0, fri ); delete_object( oldpath ); return; }
- todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( !fileDeleted, "file should exist\n" );
fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - todo_wine_if( class == FileRenameInformationEx ) ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -1600,7 +1595,6 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1630,12 +1624,9 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1667,7 +1658,6 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1702,7 +1692,6 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1737,22 +1726,17 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( !fileDeleted, "file should exist\n" );
fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - todo_wine_if( class == FileRenameInformationEx ) ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -1792,11 +1776,9 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class) todo_wine ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class != FileRenameInformationEx ) - ok( !fileDeleted, "file should exist\n" ); + todo_wine ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class != FileRenameInformationEx ) - ok( fileDeleted, "file should not exist\n" ); + todo_wine ok( fileDeleted, "file should not exist\n" );
CloseHandle( handle ); CloseHandle( handle2 ); @@ -1831,9 +1813,7 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_OBJECT_NAME_COLLISION, "io.Status got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1869,9 +1849,7 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_OBJECT_NAME_COLLISION, "io.Status got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1905,12 +1883,9 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1944,9 +1919,7 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -1983,9 +1956,7 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_OBJECT_NAME_COLLISION, "io.Status got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2021,9 +1992,7 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2062,9 +2031,7 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_ACCESS_DENIED, "io.Status got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2099,7 +2066,6 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2133,7 +2099,6 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2167,22 +2132,17 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileRenameInformationEx ) ok( !fileDeleted, "file should exist\n" );
fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); res = pNtQueryInformationFile( handle, &io, fni, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR), FileNameInformation ); ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fni->FileName[ fni->FileNameLength / sizeof(WCHAR) ] = 0; - todo_wine_if( class == FileRenameInformationEx ) ok( !lstrcmpiW(fni->FileName, newpath + 2), "FileName expected %s, got %s\n", wine_dbgstr_w(newpath + 2), wine_dbgstr_w(fni->FileName) ); HeapFree( GetProcessHeap(), 0, fni ); @@ -2209,9 +2169,7 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, class ); - todo_wine_if( class == FileRenameInformationEx ) ok( io.Status == STATUS_SUCCESS, "got io status %#lx\n", io.Status ); - todo_wine_if( class == FileRenameInformationEx ) ok( res == STATUS_SUCCESS, "got status %lx\n", res ); ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" );
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 43bde14e5d8..8bfa70b6a62 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4783,13 +4783,23 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, break;
case FileRenameInformation: + case FileRenameInformationEx: if (len >= sizeof(FILE_RENAME_INFORMATION)) { FILE_RENAME_INFORMATION *info = ptr; + ULONG flags; UNICODE_STRING name_str, redir; OBJECT_ATTRIBUTES attr; char *unix_name;
+ if (class == FileRenameInformation) + flags = info->ReplaceIfExists ? FILE_RENAME_REPLACE_IF_EXISTS : 0; + else + flags = info->Flags; + + if (flags & ~FILE_RENAME_REPLACE_IF_EXISTS) + FIXME( "unsupported flags: %x\n", flags ); + name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR); @@ -4805,7 +4815,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->namelen = attr.ObjectName->Length; req->link = FALSE; - req->replace = info->ReplaceIfExists; + req->flags = flags; wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length ); wine_server_add_data( req, unix_name, strlen(unix_name) ); status = wine_server_call( req ); @@ -4842,7 +4852,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->namelen = attr.ObjectName->Length; req->link = TRUE; - req->replace = info->ReplaceIfExists; + req->flags = info->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0; wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length ); wine_server_add_data( req, unix_name, strlen(unix_name) ); status = wine_server_call( req ); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 6d9f8550647..9ad2bf9c3f9 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5307,7 +5307,7 @@ struct set_fd_name_info_request obj_handle_t rootdir; data_size_t namelen; int link; - int replace; + unsigned int flags; /* VARARG(name,unicode_str,namelen); */ /* VARARG(filename,string); */ }; @@ -6487,7 +6487,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 782 +#define SERVER_PROTOCOL_VERSION 783
/* ### protocol_version end ### */
diff --git a/server/fd.c b/server/fd.c index 7cdd329bcbb..5947dee3ae1 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2511,11 +2511,12 @@ static void set_fd_disposition( struct fd *fd, unsigned int flags )
/* set new name for the fd */ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, data_size_t len, - struct unicode_str nt_name, int create_link, int replace ) + struct unicode_str nt_name, int create_link, unsigned int flags ) { struct inode *inode; struct stat st, st2; char *name; + const unsigned int replace = flags & FILE_RENAME_REPLACE_IF_EXISTS;
if (!fd->inode || !fd->unix_name) { @@ -2967,7 +2968,7 @@ DECL_HANDLER(set_fd_name_info) if ((fd = get_handle_fd_obj( current->process, req->handle, 0 ))) { set_fd_name( fd, root_fd, (const char *)get_req_data() + req->namelen, - get_req_data_size() - req->namelen, nt_name, req->link, req->replace ); + get_req_data_size() - req->namelen, nt_name, req->link, req->flags ); release_object( fd ); } if (root_fd) release_object( root_fd ); diff --git a/server/protocol.def b/server/protocol.def index 1699dd37473..4d2a95d6324 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3692,7 +3692,7 @@ struct handle_info obj_handle_t rootdir; /* root directory */ data_size_t namelen; /* length of NT name in bytes */ int link; /* link instead of renaming */ - int replace; /* replace an existing file? */ + unsigned int flags; /* FILE_RENAME_* flags */ VARARG(name,unicode_str,namelen); /* NT name */ VARARG(filename,string); /* new file name */ @END diff --git a/server/request.h b/server/request.h index fe148f84f35..41d38e4347f 100644 --- a/server/request.h +++ b/server/request.h @@ -2241,7 +2241,7 @@ C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, rootdir) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, namelen) == 20 ); C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, link) == 24 ); -C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, replace) == 28 ); +C_ASSERT( FIELD_OFFSET(struct set_fd_name_info_request, flags) == 28 ); C_ASSERT( sizeof(struct set_fd_name_info_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct set_fd_eof_info_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_fd_eof_info_request, eof) == 16 ); diff --git a/server/trace.c b/server/trace.c index f9cb2207b79..b968b74034d 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4411,7 +4411,7 @@ static void dump_set_fd_name_info_request( const struct set_fd_name_info_request fprintf( stderr, ", rootdir=%04x", req->rootdir ); fprintf( stderr, ", namelen=%u", req->namelen ); fprintf( stderr, ", link=%d", req->link ); - fprintf( stderr, ", replace=%d", req->replace ); + fprintf( stderr, ", flags=%08x", req->flags ); dump_varargs_unicode_str( ", name=", min(cur_size,req->namelen) ); dump_varargs_string( ", filename=", cur_size ); }
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 41 +---------------------------------------- dlls/ntdll/unix/file.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 41 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index da4f817b1da..cf04ccc1264 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2216,21 +2216,18 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
if (class == FileLinkInformationEx && (res == STATUS_NOT_IMPLEMENTED || res == STATUS_INVALID_INFO_CLASS)) { - todo_wine win_skip( "FileLinkInformationEx not supported\n" ); + win_skip( "FileLinkInformationEx not supported\n" ); CloseHandle( handle ); HeapFree( GetProcessHeap(), 0, fli ); delete_object( oldpath ); return; }
- todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileLinkInformationEx ) ok( !fileDeleted, "file should exist\n" );
fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); @@ -2265,7 +2262,6 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2295,9 +2291,7 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2328,9 +2322,7 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2373,7 +2365,6 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2408,7 +2399,6 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2443,10 +2433,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY , "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2493,10 +2481,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2530,10 +2516,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION || res == STATUS_FILE_IS_A_DIRECTORY /* > Win XP */, "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2570,10 +2554,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION || res == STATUS_FILE_IS_A_DIRECTORY /* > Win XP */, "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2608,10 +2590,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2647,10 +2627,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2687,10 +2665,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION || res == STATUS_FILE_IS_A_DIRECTORY /* > Win XP */, "res expected STATUS_OBJECT_NAME_COLLISION or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2727,10 +2703,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2769,10 +2743,8 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == 0xdeadbeef || io.Status == STATUS_FILE_IS_A_DIRECTORY, "io.Status expected 0xdeadbeef or STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2807,7 +2779,6 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "res expected STATUS_OBJECT_NAME_COLLISION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2841,7 +2812,6 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -2875,14 +2845,11 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine_if( class == FileLinkInformationEx ) ok( !fileDeleted, "file should exist\n" );
fni = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR) ); @@ -2916,15 +2883,12 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "got io status %#lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "got status %lx\n", res );
fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == STATUS_SUCCESS, "got io status %#lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_SUCCESS, "got status %lx\n", res ); ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" );
@@ -2950,15 +2914,12 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); todo_wine ok( io.Status == 0xdeadbeef, "got io status %#lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_OBJECT_NAME_COLLISION, "got status %lx\n", res );
fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, class ); - todo_wine_if( class == FileLinkInformationEx ) ok( io.Status == STATUS_SUCCESS, "got io status %#lx\n", io.Status ); - todo_wine_if( class == FileLinkInformationEx ) ok( res == STATUS_SUCCESS, "got status %lx\n", res ); ok( GetFileAttributesW( oldpath ) != INVALID_FILE_ATTRIBUTES, "file should exist\n" );
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 8bfa70b6a62..b0255f8aaad 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4830,13 +4830,23 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, break;
case FileLinkInformation: + case FileLinkInformationEx: if (len >= sizeof(FILE_LINK_INFORMATION)) { FILE_LINK_INFORMATION *info = ptr; + ULONG flags; UNICODE_STRING name_str, redir; OBJECT_ATTRIBUTES attr; char *unix_name;
+ if (class == FileLinkInformation) + flags = info->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0; + else + flags = info->Flags; + + if (flags & ~FILE_LINK_REPLACE_IF_EXISTS) + FIXME( "unsupported flags: %x\n", flags ); + name_str.Buffer = info->FileName; name_str.Length = info->FileNameLength; name_str.MaximumLength = info->FileNameLength + sizeof(WCHAR); @@ -4852,7 +4862,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, req->rootdir = wine_server_obj_handle( attr.RootDirectory ); req->namelen = attr.ObjectName->Length; req->link = TRUE; - req->flags = info->ReplaceIfExists ? FILE_LINK_REPLACE_IF_EXISTS : 0; + req->flags = flags; wine_server_add_data( req, attr.ObjectName->Buffer, attr.ObjectName->Length ); wine_server_add_data( req, unix_name, strlen(unix_name) ); status = wine_server_call( req );
From: Joel Holdsworth joel@airwebreathe.org.uk
This change has the benefit that a file or directory can be deleted using a single combined operation. It can also be later adapted to include FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE for deletion of read-only test files.
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index cf04ccc1264..a1841c5936c 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -1502,13 +1502,27 @@ static void test_file_all_information(void)
static void delete_object( WCHAR *path ) { - BOOL ret = DeleteFileW( path ); - ok( ret || GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_ACCESS_DENIED, - "DeleteFileW failed with %lu\n", GetLastError() ); - if (!ret && GetLastError() == ERROR_ACCESS_DENIED) - { - ret = RemoveDirectoryW( path ); - ok( ret, "RemoveDirectoryW failed with %lu\n", GetLastError() ); + UNICODE_STRING str; + OBJECT_ATTRIBUTES attr; + HANDLE handle; + FILE_DISPOSITION_INFORMATION_EX fdie; + IO_STATUS_BLOCK io; + NTSTATUS status; + + pRtlDosPathNameToNtPathName_U( path, &str, NULL, NULL ); + InitializeObjectAttributes( &attr, &str, 0, 0, NULL ); + io.Status = 0xdeadbeef; + status = pNtCreateFile( &handle, DELETE, &attr, &io, NULL, 0, 0, FILE_OPEN, 0, NULL, 0 ); + pRtlFreeUnicodeString( &str ); + ok(status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND, + "expected STATUS_SUCESS or STATUS_OBJECT_NAME_NOT_FOUND, got: %lx\n", io.Status); + if (status == STATUS_SUCCESS) { + fdie.Flags = FILE_DISPOSITION_DELETE; + io.Status = 0xdeadbeef; + status = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); + ok ( status == STATUS_SUCCESS, "can't set file disposition, status: %lx\n", status ); + ok ( io.Status == STATUS_SUCCESS, "can't set file disposition, io.Status: %lx\n", io.Status ); + CloseHandle( handle ); } }
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 85 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index a1841c5936c..e19d8bd29b5 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -1517,7 +1517,7 @@ static void delete_object( WCHAR *path ) ok(status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND, "expected STATUS_SUCESS or STATUS_OBJECT_NAME_NOT_FOUND, got: %lx\n", io.Status); if (status == STATUS_SUCCESS) { - fdie.Flags = FILE_DISPOSITION_DELETE; + fdie.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE; io.Status = 0xdeadbeef; status = pNtSetInformationFile( handle, &io, &fdie, sizeof fdie, FileDispositionInformationEx ); ok ( status == STATUS_SUCCESS, "can't set file disposition, status: %lx\n", status ); @@ -2192,6 +2192,88 @@ static void test_file_rename_information(FILE_INFORMATION_CLASS class) delete_object( oldpath ); }
+static void test_file_rename_information_ex(void) +{ + static const WCHAR fooW[] = {'f','o','o',0}; + WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16]; + FILE_RENAME_INFORMATION *fri; + BOOL fileDeleted; + UNICODE_STRING name_str; + HANDLE handle, handle2; + IO_STATUS_BLOCK io; + NTSTATUS res; + + GetTempPathW( MAX_PATH, tmp_path ); + + /* oldpath is a file, newpath is a read-only file, with FILE_RENAME_REPLACE_IF_EXISTS */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a read-only file, with FILE_RENAME_REPLACE_IF_EXISTS and FILE_RENAME_IGNORE_READONLY_ATTRIBUTE */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( fileDeleted, "file should not exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); +} + static void test_file_link_information(FILE_INFORMATION_CLASS class) { static const WCHAR foo_txtW[] = {'\','f','o','o','.','t','x','t',0}; @@ -5713,6 +5795,7 @@ START_TEST(file) test_file_all_name_information(); test_file_rename_information(FileRenameInformation); test_file_rename_information(FileRenameInformationEx); + test_file_rename_information_ex(); test_file_link_information(FileLinkInformation); test_file_link_information(FileLinkInformationEx); test_file_disposition_information();
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index e19d8bd29b5..6990ef39b42 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3033,6 +3033,88 @@ static void test_file_link_information(FILE_INFORMATION_CLASS class) delete_object( oldpath ); }
+static void test_file_link_information_ex(void) +{ + static const WCHAR fooW[] = {'f','o','o',0}; + WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16]; + FILE_LINK_INFORMATION *fli; + BOOL fileDeleted; + UNICODE_STRING name_str; + HANDLE handle, handle2; + IO_STATUS_BLOCK io; + NTSTATUS res; + + GetTempPathW( MAX_PATH, tmp_path ); + + /* oldpath is a file, newpath is a read-only file, with FILE_LINK_REPLACE_IF_EXISTS */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a read-only file, with FILE_LINK_REPLACE_IF_EXISTS and FILE_LINK_IGNORE_READONLY_ATTRIBUTE */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + handle2 = CreateFileW( newpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + CloseHandle( handle2 ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_IGNORE_READONLY_ATTRIBUTE; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); +} + static void test_file_both_information(void) { IO_STATUS_BLOCK io; @@ -5798,6 +5880,7 @@ START_TEST(file) test_file_rename_information_ex(); test_file_link_information(FileLinkInformation); test_file_link_information(FileLinkInformationEx); + test_file_link_information_ex(); test_file_disposition_information(); test_file_completion_information(); test_file_id_information();
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 16 ++++++++-------- server/fd.c | 7 +++++++ 2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 6990ef39b42..45ee937ca02 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2228,9 +2228,9 @@ static void test_file_rename_information_ex(void) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( !fileDeleted, "file should exist\n" ); + ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" );
@@ -2261,10 +2261,10 @@ static void test_file_rename_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); - ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - ok( fileDeleted, "file should not exist\n" ); + todo_wine ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" );
@@ -3068,7 +3068,7 @@ static void test_file_link_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); - todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); @@ -3102,8 +3102,8 @@ static void test_file_link_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); - ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; diff --git a/server/fd.c b/server/fd.c index 5947dee3ae1..3426a39682a 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2579,6 +2579,13 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da goto failed; }
+ /* read-only files cannot be replaced */ + if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + { + set_error( STATUS_ACCESS_DENIED ); + goto failed; + } + /* can't replace an opened file */ if ((inode = get_inode( st.st_dev, st.st_ino, -1 ))) {
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 14 +++++++------- dlls/ntdll/unix/file.c | 4 ++-- server/fd.c | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 45ee937ca02..7ff05dbb937 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2261,10 +2261,10 @@ static void test_file_rename_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); - todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( fileDeleted, "file should not exist\n" ); + ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" );
@@ -3068,8 +3068,8 @@ static void test_file_link_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); - ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_ACCESS_DENIED, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -3102,8 +3102,8 @@ static void test_file_link_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); - todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index b0255f8aaad..6283539c962 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4797,7 +4797,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, else flags = info->Flags;
- if (flags & ~FILE_RENAME_REPLACE_IF_EXISTS) + if (flags & ~(FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE)) FIXME( "unsupported flags: %x\n", flags );
name_str.Buffer = info->FileName; @@ -4844,7 +4844,7 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, else flags = info->Flags;
- if (flags & ~FILE_LINK_REPLACE_IF_EXISTS) + if (flags & ~(FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_IGNORE_READONLY_ATTRIBUTE)) FIXME( "unsupported flags: %x\n", flags );
name_str.Buffer = info->FileName; diff --git a/server/fd.c b/server/fd.c index 3426a39682a..06793c3f281 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2580,7 +2580,8 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da }
/* read-only files cannot be replaced */ - if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) + if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && + !(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE)) { set_error( STATUS_ACCESS_DENIED ); goto failed;
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 189 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 7ff05dbb937..8c7eaa670e6 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2197,7 +2197,7 @@ static void test_file_rename_information_ex(void) static const WCHAR fooW[] = {'f','o','o',0}; WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16]; FILE_RENAME_INFORMATION *fri; - BOOL fileDeleted; + BOOL success, fileDeleted; UNICODE_STRING name_str; HANDLE handle, handle2; IO_STATUS_BLOCK io; @@ -2272,6 +2272,193 @@ static void test_file_rename_information_ex(void) HeapFree( GetProcessHeap(), 0, fri ); delete_object( oldpath ); delete_object( newpath ); + + /* oldpath is a file, newpath is a file, with FILE_RENAME_REPLACE_IF_EXISTS, with + * FILE_RENAME_POSIX_SEMANTICS, target file opened without sharing mode */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_POSIX_SEMANTICS; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SHARING_VIOLATION, "res expected STATUS_SHARING_VIOLATION, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a file, with FILE_RENAME_REPLACE_IF_EXISTS, with + * FILE_RENAME_POSIX_SEMANTICS, target file opened with sharing mode */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, + NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_POSIX_SEMANTICS; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine ok( fileDeleted, "file should not exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a directory, newpath is a file, with FILE_RENAME_REPLACE_IF_EXISTS, with + * FILE_RENAME_POSIX_SEMANTICS, target file opened */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( oldpath ); + success = CreateDirectoryW( oldpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, + NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_POSIX_SEMANTICS; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_NOT_A_DIRECTORY, "res expected STATUS_NOT_A_DIRECTORY, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a directory, newpath is a directory, with FILE_RENAME_REPLACE_IF_EXISTS, with + * FILE_RENAME_POSIX_SEMANTICS, target file opened */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( oldpath ); + success = CreateDirectoryW( oldpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + success = CreateDirectoryW( newpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_POSIX_SEMANTICS; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + todo_wine ok( fileDeleted, "file should not exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a directory, with FILE_RENAME_REPLACE_IF_EXISTS, with + * FILE_RENAME_POSIX_SEMANTICS */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + success = CreateDirectoryW( newpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fri = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_RENAME_INFORMATION) + name_str.Length ); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_POSIX_SEMANTICS; + fri->RootDirectory = NULL; + fri->FileNameLength = name_str.Length; + memcpy( fri->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fri ); + delete_object( oldpath ); + delete_object( newpath ); }
static void test_file_link_information(FILE_INFORMATION_CLASS class)
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 195 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 8c7eaa670e6..f7dbf97e11c 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3225,7 +3225,7 @@ static void test_file_link_information_ex(void) static const WCHAR fooW[] = {'f','o','o',0}; WCHAR tmp_path[MAX_PATH], oldpath[MAX_PATH + 16], newpath[MAX_PATH + 16]; FILE_LINK_INFORMATION *fli; - BOOL fileDeleted; + BOOL success, fileDeleted; UNICODE_STRING name_str; HANDLE handle, handle2; IO_STATUS_BLOCK io; @@ -3300,6 +3300,199 @@ static void test_file_link_information_ex(void) HeapFree( GetProcessHeap(), 0, fli ); delete_object( oldpath ); delete_object( newpath ); + + /* oldpath is a file, newpath is a file, with FILE_LINK_REPLACE_IF_EXISTS, with + * FILE_LINK_POSIX_SEMANTICS, target file opened without sharing mode */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, + NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, 0, + NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_POSIX_SEMANTICS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SHARING_VIOLATION, "res expected STATUS_SHARING_VIOLATION, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a file, with FILE_LINK_REPLACE_IF_EXISTS, with + * FILE_LINK_POSIX_SEMANTICS, target file opened with sharing mode */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, + NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, + NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_POSIX_SEMANTICS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a directory, newpath is a file, with FILE_LINK_REPLACE_IF_EXISTS, with + * FILE_LINK_POSIX_SEMANTICS, target file opened */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( oldpath ); + success = CreateDirectoryW( oldpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, + NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_POSIX_SEMANTICS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + ok( io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a directory, newpath is a directory, with FILE_LINK_REPLACE_IF_EXISTS, with + * FILE_LINK_POSIX_SEMANTICS, target file opened */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( oldpath ); + success = CreateDirectoryW( oldpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + success = CreateDirectoryW( newpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + handle2 = CreateFileW( newpath, GENERIC_WRITE | DELETE, FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ); + ok( handle2 != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_POSIX_SEMANTICS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + ok( io.Status == STATUS_FILE_IS_A_DIRECTORY, + "io.Status expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + CloseHandle( handle2 ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); + + /* oldpath is a file, newpath is a directory, with FILE_LINK_REPLACE_IF_EXISTS, with + * FILE_LINK_POSIX_SEMANTICS */ + res = GetTempFileNameW( tmp_path, fooW, 0, oldpath ); + ok( res != 0, "failed to create temp file\n" ); + handle = CreateFileW( oldpath, GENERIC_WRITE | DELETE, 0, NULL, CREATE_ALWAYS, 0, 0 ); + ok( handle != INVALID_HANDLE_VALUE, "CreateFileW failed\n" ); + + res = GetTempFileNameW( tmp_path, fooW, 0, newpath ); + ok( res != 0, "failed to create temp file\n" ); + DeleteFileW( newpath ); + success = CreateDirectoryW( newpath, NULL ); + ok( success != 0, "failed to create temp directory\n" ); + pRtlDosPathNameToNtPathName_U( newpath, &name_str, NULL, NULL ); + fli = HeapAlloc( GetProcessHeap(), 0, sizeof(FILE_LINK_INFORMATION) + name_str.Length ); + fli->Flags = FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_POSIX_SEMANTICS; + fli->RootDirectory = NULL; + fli->FileNameLength = name_str.Length; + memcpy( fli->FileName, name_str.Buffer, name_str.Length ); + pRtlFreeUnicodeString( &name_str ); + + io.Status = 0xdeadbeef; + res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + ok( !fileDeleted, "file should exist\n" ); + + CloseHandle( handle ); + HeapFree( GetProcessHeap(), 0, fli ); + delete_object( oldpath ); + delete_object( newpath ); }
static void test_file_both_information(void)
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- server/fd.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/server/fd.c b/server/fd.c index 06793c3f281..35ea893e187 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1564,6 +1564,31 @@ static void fd_destroy( struct object *obj ) } }
+static void get_inode_open_sharing(struct fd *fd, struct inode *inode, + unsigned int *sharing, unsigned int *access ) +{ + static const unsigned int all_access = FILE_READ_DATA | FILE_EXECUTE | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE; + + struct list *ptr; + + *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + *access = 0; + + if (!inode) + inode = fd->inode; + + LIST_FOR_EACH( ptr, &inode->open ) + { + struct fd *fd_ptr = LIST_ENTRY( ptr, struct fd, inode_entry ); + if (fd_ptr != fd) + { + /* if access mode is 0, sharing mode is ignored */ + if (fd_ptr->access & all_access) *sharing &= fd_ptr->sharing; + *access |= fd_ptr->access; + } + } +} + /* check if the desired access is possible without violating */ /* the sharing mode of other opens of the same file */ static unsigned int check_sharing( struct fd *fd, unsigned int access, unsigned int sharing, @@ -1576,21 +1601,11 @@ static unsigned int check_sharing( struct fd *fd, unsigned int access, unsigned
unsigned int existing_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; unsigned int existing_access = 0; - struct list *ptr;
fd->access = access; fd->sharing = sharing;
- LIST_FOR_EACH( ptr, &fd->inode->open ) - { - struct fd *fd_ptr = LIST_ENTRY( ptr, struct fd, inode_entry ); - if (fd_ptr != fd) - { - /* if access mode is 0, sharing mode is ignored */ - if (fd_ptr->access & all_access) existing_sharing &= fd_ptr->sharing; - existing_access |= fd_ptr->access; - } - } + get_inode_open_sharing(fd, NULL, &existing_sharing, &existing_access);
if (((access & read_access) && !(existing_sharing & FILE_SHARE_READ)) || ((access & write_access) && !(existing_sharing & FILE_SHARE_WRITE)) ||
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 20 +++++++-------- dlls/ntdll/unix/file.c | 8 ++++-- server/fd.c | 56 ++++++++++++++++++++++++++++++++--------- 3 files changed, 60 insertions(+), 24 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index f7dbf97e11c..140faa33577 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2296,7 +2296,7 @@ static void test_file_rename_information_ex(void) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SHARING_VIOLATION, "res expected STATUS_SHARING_VIOLATION, got %lx\n", res ); + ok( res == STATUS_SHARING_VIOLATION, "res expected STATUS_SHARING_VIOLATION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2331,10 +2331,10 @@ static void test_file_rename_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); - todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_SUCCESS, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; - todo_wine ok( fileDeleted, "file should not exist\n" ); + ok( fileDeleted, "file should not exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" );
@@ -2371,7 +2371,7 @@ static void test_file_rename_information_ex(void) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_NOT_A_DIRECTORY, "res expected STATUS_NOT_A_DIRECTORY, got %lx\n", res ); + ok( res == STATUS_NOT_A_DIRECTORY, "res expected STATUS_NOT_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -2449,7 +2449,7 @@ static void test_file_rename_information_ex(void) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -3326,7 +3326,7 @@ static void test_file_link_information_ex(void) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SHARING_VIOLATION, "res expected STATUS_SHARING_VIOLATION, got %lx\n", res ); + ok( res == STATUS_SHARING_VIOLATION, "res expected STATUS_SHARING_VIOLATION, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -3362,8 +3362,8 @@ static void test_file_link_information_ex(void)
io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); - todo_wine ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SUCCESS, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); + ok( io.Status == STATUS_SUCCESS, "io.Status expected STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS, "res expected STATUS_ACCESS_DENIED, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; @@ -3483,7 +3483,7 @@ static void test_file_link_information_ex(void) io.Status = 0xdeadbeef; res = pNtSetInformationFile( handle, &io, fli, sizeof(FILE_LINK_INFORMATION) + fli->FileNameLength, FileLinkInformationEx ); todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); + ok( res == STATUS_FILE_IS_A_DIRECTORY, "res expected STATUS_FILE_IS_A_DIRECTORY, got %lx\n", res ); fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; ok( !fileDeleted, "file should exist\n" ); fileDeleted = GetFileAttributesW( newpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 6283539c962..149a5913982 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4797,7 +4797,9 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, else flags = info->Flags;
- if (flags & ~(FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE)) + if (flags & ~(FILE_RENAME_REPLACE_IF_EXISTS | + FILE_RENAME_IGNORE_READONLY_ATTRIBUTE | + FILE_RENAME_POSIX_SEMANTICS)) FIXME( "unsupported flags: %x\n", flags );
name_str.Buffer = info->FileName; @@ -4844,7 +4846,9 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, else flags = info->Flags;
- if (flags & ~(FILE_LINK_REPLACE_IF_EXISTS | FILE_LINK_IGNORE_READONLY_ATTRIBUTE)) + if (flags & ~(FILE_LINK_REPLACE_IF_EXISTS | + FILE_LINK_IGNORE_READONLY_ATTRIBUTE | + FILE_LINK_POSIX_SEMANTICS)) FIXME( "unsupported flags: %x\n", flags );
name_str.Buffer = info->FileName; diff --git a/server/fd.c b/server/fd.c index 35ea893e187..607922e9ec4 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2587,13 +2587,6 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da goto failed; }
- /* can't replace directories or special files */ - if (!S_ISREG( st.st_mode )) - { - set_error( STATUS_ACCESS_DENIED ); - goto failed; - } - /* read-only files cannot be replaced */ if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) && !(flags & FILE_RENAME_IGNORE_READONLY_ATTRIBUTE)) @@ -2602,16 +2595,55 @@ static void set_fd_name( struct fd *fd, struct fd *root, const char *nameptr, da goto failed; }
- /* can't replace an opened file */ - if ((inode = get_inode( st.st_dev, st.st_ino, -1 ))) + if (flags & FILE_RENAME_POSIX_SEMANTICS) { - int is_empty = list_empty( &inode->open ); - release_object( inode ); - if (!is_empty) + /* can't replace a file with a directory with FILE_RENAME_POSIX_SEMANTICS */ + if (S_ISDIR( st2.st_mode ) && !S_ISDIR( st.st_mode )) + { + set_error( STATUS_NOT_A_DIRECTORY ); + goto failed; + } + + /* can't replace a directory with a file with FILE_RENAME_POSIX_SEMANTICS */ + if (!S_ISDIR( st2.st_mode ) && S_ISDIR( st.st_mode )) + { + set_error( STATUS_FILE_IS_A_DIRECTORY ); + goto failed; + } + + /* can't replace an open file that was not opened with FILE_SHARE_DELETE */ + if ((inode = get_inode( st.st_dev, st.st_ino, -1 ))) + { + unsigned int sharing, access; + get_inode_open_sharing( NULL, inode, &sharing, &access ); + release_object( inode ); + + if (!(sharing & FILE_SHARE_DELETE)) { + set_error( STATUS_SHARING_VIOLATION ); + goto failed; + } + } + } + else + { + /* can't replace directories or special files */ + if (!S_ISREG( st.st_mode )) { set_error( STATUS_ACCESS_DENIED ); goto failed; } + + /* can't replace an opened file without FILE_RENAME_POSIX_SEMANTICS */ + if ((inode = get_inode( st.st_dev, st.st_ino, -1 ))) + { + int is_empty = list_empty( &inode->open ); + release_object( inode ); + if (!is_empty) + { + set_error( STATUS_ACCESS_DENIED ); + goto failed; + } + } }
/* link() expects that the target doesn't exist */
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 full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=137990
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/ntdll/tests/file.c:1549 error: patch failed: dlls/ntdll/tests/file.c:2216 Task: Patch failed to apply
=== debian11 (build log) ===
error: patch failed: dlls/ntdll/tests/file.c:1549 error: patch failed: dlls/ntdll/tests/file.c:2216 Task: Patch failed to apply
=== debian11b (build log) ===
error: patch failed: dlls/ntdll/tests/file.c:1549 error: patch failed: dlls/ntdll/tests/file.c:2216 Task: Patch failed to apply