Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v2: fix a test failure with Windows XP
dlls/kernel32/tests/file.c | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 110082a9c4f..1f704337877 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -117,6 +117,13 @@ static void InitFunctionPointers(void) pFindFirstStreamW = (void *)GetProcAddress(hkernel32, "FindFirstStreamW"); }
+static void create_file( const char *path ) +{ + FILE *f = fopen( path, "wb" ); + fputs( path, f ); + fclose( f ); +} + static void test__hread( void ) { HFILE filehandle; @@ -5510,6 +5517,80 @@ static void test_SetFileTime(void) CloseHandle(hfile); }
+static void test_hard_link(void) +{ + char cwd[MAX_PATH], temp_dir[MAX_PATH], name_buffer[200], buffer[20]; + FILE_NAME_INFORMATION *name_info = (FILE_NAME_INFORMATION *)name_buffer; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE file; + DWORD size; + BOOL ret; + + GetCurrentDirectoryA( sizeof(cwd), cwd ); + GetTempPathA( sizeof(temp_dir), temp_dir ); + SetCurrentDirectoryA( temp_dir ); + + ret = CreateDirectoryA( "winetest_dir1", NULL ); + ok(ret, "failed to create directory, error %u\n", GetLastError()); + ret = CreateDirectoryA( "winetest_dir2", NULL ); + ok(ret, "failed to create directory, error %u\n", GetLastError()); + create_file( "winetest_file1" ); + create_file( "winetest_file2" ); + + ret = CreateHardLinkA( "winetest_file3", "winetest_file1", NULL ); + ok(ret, "got error %u\n", GetLastError()); + + file = CreateFileA( "winetest_file3", FILE_READ_DATA, 0, NULL, OPEN_EXISTING, 0, NULL ); + ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError()); + + status = NtQueryInformationFile( file, &io, name_buffer, sizeof(name_buffer), FileNameInformation ); + ok(!status, "got status %#x\n", status); + ok(!wcsncmp(name_info->FileName + (name_info->FileNameLength / sizeof(WCHAR)) - wcslen(L"\winetest_file3"), + L"\winetest_file3", wcslen(L"\winetest_file3")), "got name %s\n", + debugstr_wn(name_info->FileName, name_info->FileNameLength / sizeof(WCHAR))); + + ret = ReadFile( file, buffer, sizeof(buffer), &size, NULL ); + ok(ret, "got error %u\n", GetLastError()); + ok(!memcmp( buffer, "winetest_file1", size ), "got file contents %s\n", debugstr_an( buffer, size )); + + CloseHandle( file ); + + ret = DeleteFileA( "winetest_file3" ); + ok(ret, "failed to delete file, error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateHardLinkA( "winetest_file2", "winetest_file1", NULL ); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateHardLinkA( "winetest_file3", "winetest_dir1", NULL ); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "got error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateHardLinkA( "winetest_dir2", "winetest_dir1", NULL ); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED + || GetLastError() == ERROR_ALREADY_EXISTS /* XP */, "got error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = CreateHardLinkA( "winetest_dir1", "winetest_file1", NULL ); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_ALREADY_EXISTS, "got error %u\n", GetLastError()); + + ret = RemoveDirectoryA( "winetest_dir1" ); + ok(ret, "failed to remove directory, error %u\n", GetLastError()); + ret = RemoveDirectoryA( "winetest_dir2" ); + ok(ret, "failed to remove directory, error %u\n", GetLastError()); + ret = DeleteFileA( "winetest_file1" ); + ok(ret, "failed to delete file, error %u\n", GetLastError()); + ret = DeleteFileA( "winetest_file2" ); + ok(ret, "failed to delete file, error %u\n", GetLastError()); + SetCurrentDirectoryA( cwd ); +} + START_TEST(file) { char temp_path[MAX_PATH]; @@ -5584,4 +5665,5 @@ START_TEST(file) test_find_file_stream(); test_SetFileTime(); test_ReOpenFile(); + test_hard_link(); }
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/kernel32/path.c | 46 ++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 25 deletions(-)
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index c50b631d7c7..617b14a7178 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -663,10 +663,13 @@ BOOL WINAPI MoveFileA( LPCSTR source, LPCSTR dest ) BOOL WINAPI CreateHardLinkW(LPCWSTR lpFileName, LPCWSTR lpExistingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) { - NTSTATUS status; UNICODE_STRING ntDest, ntSource; - ANSI_STRING unixDest, unixSource; + FILE_LINK_INFORMATION *info = NULL; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; BOOL ret = FALSE; + HANDLE file; + ULONG size;
TRACE("(%s, %s, %p)\n", debugstr_w(lpFileName), debugstr_w(lpExistingFileName), lpSecurityAttributes); @@ -679,37 +682,30 @@ BOOL WINAPI CreateHardLinkW(LPCWSTR lpFileName, LPCWSTR lpExistingFileName, goto err; }
- unixSource.Buffer = unixDest.Buffer = NULL; - status = wine_nt_to_unix_file_name( &ntSource, &unixSource, FILE_OPEN, FALSE ); - if (!status) + size = offsetof( FILE_LINK_INFORMATION, FileName ) + ntDest.Length; + if (!(info = HeapAlloc( GetProcessHeap(), 0, size ))) { - status = wine_nt_to_unix_file_name( &ntDest, &unixDest, FILE_CREATE, FALSE ); - if (!status) /* destination must not exist */ - { - status = STATUS_OBJECT_NAME_EXISTS; - } else if (status == STATUS_NO_SUCH_FILE) - { - status = STATUS_SUCCESS; - } + SetLastError( ERROR_OUTOFMEMORY ); + goto err; }
- if (status) - SetLastError( RtlNtStatusToDosError(status) ); - else if (!link( unixSource.Buffer, unixDest.Buffer )) - { - TRACE("Hardlinked '%s' to '%s'\n", debugstr_a( unixDest.Buffer ), - debugstr_a( unixSource.Buffer )); - ret = TRUE; - } - else - FILE_SetDosError(); + InitializeObjectAttributes( &attr, &ntSource, OBJ_CASE_INSENSITIVE, 0, NULL ); + if (!(ret = set_ntstatus( NtOpenFile( &file, SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT ) ))) + goto err; + + info->ReplaceIfExists = FALSE; + info->RootDirectory = NULL; + info->FileNameLength = ntDest.Length; + memcpy( info->FileName, ntDest.Buffer, ntDest.Length ); + ret = set_ntstatus( NtSetInformationFile( file, &io, info, size, FileLinkInformation ) );
- RtlFreeAnsiString( &unixSource ); - RtlFreeAnsiString( &unixDest ); + NtClose( file );
err: RtlFreeUnicodeString( &ntSource ); RtlFreeUnicodeString( &ntDest ); + HeapFree( GetProcessHeap(), 0, info ); return ret; }