Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk
-- v2: ntdll: Add support for FILE_{RENAME,LINK}_POSIX_SEMANTICS. ntdll: Factor out get_inode_open_sharing. ntdll/test: Add tests for FILE_LINK_POSIX_SEMANTICS. ntdll/test: Add tests for FILE_RENAME_POSIX_SEMANTICS.
From: Joel Holdsworth joel@airwebreathe.org.uk
Signed-off-by: Joel Holdsworth joel@airwebreathe.org.uk --- dlls/ntdll/tests/file.c | 204 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 9dd49c925b7..02050918cac 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2185,7 +2185,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; @@ -2276,6 +2276,208 @@ 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 ); + ok( res == STATUS_SHARING_VIOLATION || res == STATUS_ACCESS_DENIED, + "res expected STATUS_SHARING_VIOLATION or 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, 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 == STATUS_SUCCESS || io.Status == 0xdeadbeef, + "io.Status expected STATUS_SUCCESS or 0xdeadbeef, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SUCCESS || res == STATUS_NOT_A_DIRECTORY, + "res expected STATUS_SUCCESS or STATUS_NOT_A_DIRECTORY, got %lx\n", res ); + + fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; + if (res == STATUS_SUCCESS) + ok( fileDeleted, "file should not exist\n" ); + else if (res == STATUS_NOT_A_DIRECTORY) + 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 == 0xdeadbeef, + "io.Status expected STATUS_SUCCESS or 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS || res == STATUS_ACCESS_DENIED, + "res expected STATUS_SUCCESS or STATUS_ACCESS_DENIED, got %lx\n", res ); + + if (res == STATUS_SUCCESS) { + 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 ); + 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 ); + ok( res == STATUS_FILE_IS_A_DIRECTORY || res == STATUS_ACCESS_DENIED, + "res expected STATUS_FILE_IS_A_DIRECTORY or 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, 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 | 199 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 02050918cac..9ad438cc39a 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3244,7 +3244,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; @@ -3329,6 +3329,203 @@ 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 == STATUS_NOT_SUPPORTED, + "res expected STATUS_SHARING_VIOLATION or STATUS_NOT_SUPPORTED, 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 == 0xdeadbeef || io.Status == STATUS_SUCCESS, + "io.Status expected 0xdeadbeef or STATUS_SUCCESS, got %lx\n", io.Status ); + todo_wine ok( res == STATUS_SUCCESS || res == STATUS_NOT_SUPPORTED, + "res expected STATUS_SUCCESS or STATUS_NOT_SUPPORTED, 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 ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY || res == STATUS_NOT_SUPPORTED, + "res expected STATUS_FILE_IS_A_DIRECTORY or STATUS_NOT_SUPPORTED, 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 ); + todo_wine ok( io.Status == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); + ok( res == STATUS_FILE_IS_A_DIRECTORY || res == STATUS_NOT_SUPPORTED, + "res expected STATUS_FILE_IS_A_DIRECTORY or STATUS_NOT_SUPPORTED, 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 == STATUS_NOT_SUPPORTED, + "res expected STATUS_FILE_IS_A_DIRECTORY or STATUS_NOT_SUPPORTED, 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 | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-)
diff --git a/server/fd.c b/server/fd.c index 8576882aaa9..0d35dda5ebc 100644 --- a/server/fd.c +++ b/server/fd.c @@ -1567,6 +1567,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, @@ -1577,23 +1602,12 @@ static unsigned int check_sharing( struct fd *fd, unsigned int access, unsigned const unsigned int write_access = FILE_WRITE_DATA | FILE_APPEND_DATA; const unsigned int all_access = read_access | write_access | DELETE;
- unsigned int existing_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - unsigned int existing_access = 0; - struct list *ptr; + unsigned int existing_sharing, existing_access;
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 | 30 ++++++++--------- dlls/ntdll/unix/file.c | 8 +++-- server/fd.c | 74 +++++++++++++++++++++++++++++++---------- 3 files changed, 77 insertions(+), 35 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 9ad438cc39a..e757fbef380 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -2336,10 +2336,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" );
@@ -2377,8 +2377,8 @@ static void test_file_rename_information_ex(void) res = pNtSetInformationFile( handle, &io, fri, sizeof(FILE_RENAME_INFORMATION) + fri->FileNameLength, FileRenameInformationEx ); todo_wine ok( io.Status == STATUS_SUCCESS || io.Status == 0xdeadbeef, "io.Status expected STATUS_SUCCESS or 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SUCCESS || res == STATUS_NOT_A_DIRECTORY, - "res expected STATUS_SUCCESS or STATUS_NOT_A_DIRECTORY, got %lx\n", res ); + ok( res == STATUS_SUCCESS || res == STATUS_NOT_A_DIRECTORY, + "res expected STATUS_SUCCESS or STATUS_NOT_A_DIRECTORY, got %lx\n", res );
fileDeleted = GetFileAttributesW( oldpath ) == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_FILE_NOT_FOUND; if (res == STATUS_SUCCESS) @@ -2425,8 +2425,8 @@ 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 == 0xdeadbeef, - "io.Status expected STATUS_SUCCESS or 0xdeadbeef, got %lx\n", io.Status ); + ok( io.Status == STATUS_SUCCESS || io.Status == 0xdeadbeef, + "io.Status expected STATUS_SUCCESS or 0xdeadbeef, got %lx\n", io.Status ); ok( res == STATUS_SUCCESS || res == STATUS_ACCESS_DENIED, "res expected STATUS_SUCCESS or STATUS_ACCESS_DENIED, got %lx\n", res );
@@ -3355,8 +3355,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 == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SHARING_VIOLATION || res == STATUS_NOT_SUPPORTED, - "res expected STATUS_SHARING_VIOLATION or STATUS_NOT_SUPPORTED, got %lx\n", res ); + ok( res == STATUS_SHARING_VIOLATION || res == STATUS_NOT_SUPPORTED, + "res expected STATUS_SHARING_VIOLATION or STATUS_NOT_SUPPORTED, 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; @@ -3392,10 +3392,10 @@ 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 == STATUS_SUCCESS, - "io.Status expected 0xdeadbeef or STATUS_SUCCESS, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_SUCCESS || res == STATUS_NOT_SUPPORTED, - "res expected STATUS_SUCCESS or STATUS_NOT_SUPPORTED, got %lx\n", res ); + ok( io.Status == 0xdeadbeef || io.Status == STATUS_SUCCESS, + "io.Status expected 0xdeadbeef or STATUS_SUCCESS, got %lx\n", io.Status ); + ok( res == STATUS_SUCCESS || res == STATUS_NOT_SUPPORTED, + "res expected STATUS_SUCCESS or STATUS_NOT_SUPPORTED, 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; @@ -3515,8 +3515,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 == 0xdeadbeef, "io.Status expected 0xdeadbeef, got %lx\n", io.Status ); - todo_wine ok( res == STATUS_FILE_IS_A_DIRECTORY || res == STATUS_NOT_SUPPORTED, - "res expected STATUS_FILE_IS_A_DIRECTORY or STATUS_NOT_SUPPORTED, got %lx\n", res ); + ok( res == STATUS_FILE_IS_A_DIRECTORY || res == STATUS_NOT_SUPPORTED, + "res expected STATUS_FILE_IS_A_DIRECTORY or STATUS_NOT_SUPPORTED, 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 d292d934efa..7cd12600662 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4831,7 +4831,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; @@ -4878,7 +4880,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 0d35dda5ebc..7e4a0071dd0 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2607,13 +2607,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)) @@ -2622,23 +2615,68 @@ 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_ACCESS_DENIED ); + set_error( STATUS_NOT_A_DIRECTORY ); goto failed; } - }
- /* link() expects that the target doesn't exist */ - /* rename() cannot replace files with directories */ - if (create_link || S_ISDIR( st2.st_mode )) + /* 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; + } + } + + /* link() expects that the target doesn't exist */ + /* rename() cannot replace files with directories */ + if (S_ISDIR( st2.st_mode ) && S_ISDIR( st.st_mode )) + { + if (rmdir( name )) + { + file_set_error(); + goto failed; + } + } + else if ((create_link || S_ISDIR( st2.st_mode )) && unlink(name)) + { + file_set_error(); + goto failed; + } + } + else { - if (unlink( name )) + /* 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 */ + /* rename() cannot replace files with directories */ + if ((create_link || S_ISDIR( st2.st_mode )) && unlink( name )) { file_set_error(); goto failed;
Just rebased on master.
Sorry, can you please remind me what needed this?
On Wed Mar 13 15:27:06 2024 +0000, Zebediah Figura wrote:
Sorry, can you please remind me what needed this?
Sorry for the delay in getting back to you. Thanks for having a look.
Sorry, can you please remind me what needed this?
`FILE_DISPOSITION_POSIX_SEMANTICS` is used by msys2 in their implementation of `unlink`:
https://github.com/msys2/msys2-runtime/blob/msys2-3.4.10/winsup/cygwin/sysca...
Without this implementation, it makes a very noisy fixme.
On Wed Mar 13 15:28:01 2024 +0000, Joel Holdsworth wrote:
Sorry for the delay in getting back to you. Thanks for having a look.
Sorry, can you please remind me what needed this?
`FILE_DISPOSITION_POSIX_SEMANTICS` is used by msys2 in their implementation of `unlink`: https://github.com/msys2/msys2-runtime/blob/msys2-3.4.10/winsup/cygwin/sysca... Without this implementation, it makes a very noisy fixme.
Yeah, but we already have FILE_DISPOSITION_POSIX_SEMANTICS, don't we? This is something slightly different.
On Wed Mar 13 17:56:35 2024 +0000, Zebediah Figura wrote:
Yeah, but we already have FILE_DISPOSITION_POSIX_SEMANTICS, don't we? This is something slightly different.
Sorry - you're right. It's been a while since I worked on this. IIRC, I produced this patch to bring the implementation of `FileRenameInformationEx` closer to completeness. It also improves the correctness of the non-POSIX rename/link case.
Currently I don't have an app that requires this.
On Wed Mar 13 18:51:48 2024 +0000, Joel Holdsworth wrote:
Sorry - you're right. It's been a while since I worked on this. IIRC, I produced this patch to bring the implementation of `FileRenameInformationEx` closer to completeness. It also improves the correctness of the non-POSIX rename/link case. Currently I don't have an app that requires this.
msys2 is important, shame this wasnt merged :disappointed: