From b6c94018823f0217dcc9e5ecf1f100ba9ef12b96 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 5 May 2020 13:10:03 +0200 Subject: [PATCH] kernel32: Move some file functions to kernelbase. Signed-off-by: Alexandre Julliard --- dlls/kernel32/file.c | 326 --------------- dlls/kernel32/kernel32.spec | 22 +- dlls/kernel32/path.c | 449 -------------------- dlls/kernelbase/file.c | 700 ++++++++++++++++++++++++++++++++ dlls/kernelbase/kernelbase.spec | 20 +- 5 files changed, 721 insertions(+), 796 deletions(-) diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c index 9bd5e0ec7b8..502a4983e8f 100644 --- a/dlls/kernel32/file.c +++ b/dlls/kernel32/file.c @@ -415,121 +415,6 @@ BOOL WINAPI KERNEL32_FlushFileBuffers( HANDLE file ) **************************************************************************/ -/************************************************************************** - * ReplaceFileW (KERNEL32.@) - * ReplaceFile (KERNEL32.@) - */ -BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileName, - LPCWSTR lpBackupFileName, DWORD dwReplaceFlags, - LPVOID lpExclude, LPVOID lpReserved) -{ - UNICODE_STRING nt_replaced_name, nt_replacement_name; - HANDLE hReplacement = NULL; - NTSTATUS status; - IO_STATUS_BLOCK io; - OBJECT_ATTRIBUTES attr; - FILE_BASIC_INFORMATION info; - - TRACE("%s %s %s 0x%08x %p %p\n", debugstr_w(lpReplacedFileName), - debugstr_w(lpReplacementFileName), debugstr_w(lpBackupFileName), - dwReplaceFlags, lpExclude, lpReserved); - - if (dwReplaceFlags) - FIXME("Ignoring flags %x\n", dwReplaceFlags); - - /* First two arguments are mandatory */ - if (!lpReplacedFileName || !lpReplacementFileName) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - attr.Length = sizeof(attr); - attr.RootDirectory = 0; - attr.Attributes = OBJ_CASE_INSENSITIVE; - attr.ObjectName = NULL; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - - /* Open the "replaced" file for reading */ - if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &nt_replaced_name, NULL, NULL))) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - return FALSE; - } - attr.ObjectName = &nt_replaced_name; - - /* Replacement should fail if replaced is READ_ONLY */ - status = NtQueryAttributesFile(&attr, &info); - RtlFreeUnicodeString(&nt_replaced_name); - if (status != STATUS_SUCCESS) - return set_ntstatus( status ); - - if (info.FileAttributes & FILE_ATTRIBUTE_READONLY) - { - SetLastError( ERROR_ACCESS_DENIED ); - return FALSE; - } - - /* - * Open the replacement file for reading, writing, and deleting - * (writing and deleting are needed when finished) - */ - if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &nt_replacement_name, NULL, NULL))) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - return FALSE; - } - attr.ObjectName = &nt_replacement_name; - status = NtOpenFile(&hReplacement, - GENERIC_READ|GENERIC_WRITE|DELETE|WRITE_DAC|SYNCHRONIZE, - &attr, &io, 0, - FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE); - RtlFreeUnicodeString(&nt_replacement_name); - if (status != STATUS_SUCCESS) - return set_ntstatus( status ); - NtClose( hReplacement ); - - /* If the user wants a backup then that needs to be performed first */ - if (lpBackupFileName) - { - if (!MoveFileExW( lpReplacedFileName, lpBackupFileName, MOVEFILE_REPLACE_EXISTING )) - return FALSE; - } - else - { - /* ReplaceFile() can replace an open target. To do this, we need to move - * it out of the way first. */ - static const WCHAR prefixW[] = {'r','f',0}; - WCHAR temp_path[MAX_PATH], temp_file[MAX_PATH]; - - lstrcpynW( temp_path, lpReplacedFileName, ARRAY_SIZE( temp_path ) ); - PathRemoveFileSpecW( temp_path ); - if (!GetTempFileNameW( temp_path, prefixW, 0, temp_file ) - || !MoveFileExW( lpReplacedFileName, temp_file, MOVEFILE_REPLACE_EXISTING )) - return FALSE; - - DeleteFileW( temp_file ); - } - - /* - * Now that the backup has been performed (if requested), copy the replacement - * into place - */ - if (!MoveFileExW( lpReplacementFileName, lpReplacedFileName, 0 )) - { - /* on failure we need to indicate whether a backup was made */ - if (!lpBackupFileName) - SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT ); - else - SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 ); - return FALSE; - } - - return TRUE; -} - - /************************************************************************** * ReplaceFileA (KERNEL32.@) */ @@ -865,214 +750,3 @@ DWORD WINAPI K32GetDeviceDriverFileNameW(void *image_base, LPWSTR file_name, DWO return 0; } - -/*********************************************************************** - * GetFinalPathNameByHandleW (KERNEL32.@) - */ -DWORD WINAPI GetFinalPathNameByHandleW(HANDLE file, LPWSTR path, DWORD charcount, DWORD flags) -{ - WCHAR buffer[sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH + 1]; - OBJECT_NAME_INFORMATION *info = (OBJECT_NAME_INFORMATION*)&buffer; - WCHAR drive_part[MAX_PATH]; - DWORD drive_part_len = 0; - NTSTATUS status; - DWORD result = 0; - ULONG dummy; - WCHAR *ptr; - - TRACE( "(%p,%p,%d,%x)\n", file, path, charcount, flags ); - - if (flags & ~(FILE_NAME_OPENED | VOLUME_NAME_GUID | VOLUME_NAME_NONE | VOLUME_NAME_NT)) - { - WARN("Unknown flags: %x\n", flags); - SetLastError( ERROR_INVALID_PARAMETER ); - return 0; - } - - /* get object name */ - status = NtQueryObject( file, ObjectNameInformation, &buffer, sizeof(buffer) - sizeof(WCHAR), &dummy ); - if (status != STATUS_SUCCESS) - { - SetLastError( RtlNtStatusToDosError( status ) ); - return 0; - } - if (!info->Name.Buffer) - { - SetLastError( ERROR_INVALID_HANDLE ); - return 0; - } - if (info->Name.Length < 4 * sizeof(WCHAR) || info->Name.Buffer[0] != '\\' || - info->Name.Buffer[1] != '?' || info->Name.Buffer[2] != '?' || info->Name.Buffer[3] != '\\' ) - { - FIXME("Unexpected object name: %s\n", debugstr_wn(info->Name.Buffer, info->Name.Length / sizeof(WCHAR))); - SetLastError( ERROR_GEN_FAILURE ); - return 0; - } - - /* add terminating null character, remove "\\??\\" */ - info->Name.Buffer[info->Name.Length / sizeof(WCHAR)] = 0; - info->Name.Length -= 4 * sizeof(WCHAR); - info->Name.Buffer += 4; - - /* FILE_NAME_OPENED is not supported yet, and would require Wineserver changes */ - if (flags & FILE_NAME_OPENED) - { - FIXME("FILE_NAME_OPENED not supported\n"); - flags &= ~FILE_NAME_OPENED; - } - - /* Get information required for VOLUME_NAME_NONE, VOLUME_NAME_GUID and VOLUME_NAME_NT */ - if (flags == VOLUME_NAME_NONE || flags == VOLUME_NAME_GUID || flags == VOLUME_NAME_NT) - { - if (!GetVolumePathNameW( info->Name.Buffer, drive_part, MAX_PATH )) - return 0; - - drive_part_len = strlenW(drive_part); - if (!drive_part_len || drive_part_len > strlenW(info->Name.Buffer) || - drive_part[drive_part_len-1] != '\\' || - strncmpiW( info->Name.Buffer, drive_part, drive_part_len )) - { - FIXME("Path %s returned by GetVolumePathNameW does not match file path %s\n", - debugstr_w(drive_part), debugstr_w(info->Name.Buffer)); - SetLastError( ERROR_GEN_FAILURE ); - return 0; - } - } - - if (flags == VOLUME_NAME_NONE) - { - ptr = info->Name.Buffer + drive_part_len - 1; - result = strlenW(ptr); - if (result < charcount) - memcpy(path, ptr, (result + 1) * sizeof(WCHAR)); - else result++; - } - else if (flags == VOLUME_NAME_GUID) - { - WCHAR volume_prefix[51]; - - /* GetVolumeNameForVolumeMountPointW sets error code on failure */ - if (!GetVolumeNameForVolumeMountPointW( drive_part, volume_prefix, 50 )) - return 0; - - ptr = info->Name.Buffer + drive_part_len; - result = strlenW(volume_prefix) + strlenW(ptr); - if (result < charcount) - { - path[0] = 0; - strcatW(path, volume_prefix); - strcatW(path, ptr); - } - else - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - result++; - } - } - else if (flags == VOLUME_NAME_NT) - { - WCHAR nt_prefix[MAX_PATH]; - - /* QueryDosDeviceW sets error code on failure */ - drive_part[drive_part_len - 1] = 0; - if (!QueryDosDeviceW( drive_part, nt_prefix, MAX_PATH )) - return 0; - - ptr = info->Name.Buffer + drive_part_len - 1; - result = strlenW(nt_prefix) + strlenW(ptr); - if (result < charcount) - { - path[0] = 0; - strcatW(path, nt_prefix); - strcatW(path, ptr); - } - else - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - result++; - } - } - else if (flags == VOLUME_NAME_DOS) - { - static const WCHAR dos_prefix[] = {'\\','\\','?','\\', '\0'}; - - result = strlenW(dos_prefix) + strlenW(info->Name.Buffer); - if (result < charcount) - { - path[0] = 0; - strcatW(path, dos_prefix); - strcatW(path, info->Name.Buffer); - } - else - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - result++; - } - } - else - { - /* Windows crashes here, but we prefer returning ERROR_INVALID_PARAMETER */ - WARN("Invalid combination of flags: %x\n", flags); - SetLastError( ERROR_INVALID_PARAMETER ); - } - - return result; -} - -/*********************************************************************** - * GetFinalPathNameByHandleA (KERNEL32.@) - */ -DWORD WINAPI GetFinalPathNameByHandleA(HANDLE file, LPSTR path, DWORD charcount, DWORD flags) -{ - WCHAR *str; - DWORD result, len, cp; - - TRACE( "(%p,%p,%d,%x)\n", file, path, charcount, flags); - - len = GetFinalPathNameByHandleW(file, NULL, 0, flags); - if (len == 0) - return 0; - - str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); - if (!str) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - result = GetFinalPathNameByHandleW(file, str, len, flags); - if (result != len - 1) - { - HeapFree(GetProcessHeap(), 0, str); - WARN("GetFinalPathNameByHandleW failed unexpectedly: %u\n", result); - return 0; - } - - cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP; - - len = WideCharToMultiByte(cp, 0, str, -1, NULL, 0, NULL, NULL); - if (!len) - { - HeapFree(GetProcessHeap(), 0, str); - WARN("Failed to get multibyte length\n"); - return 0; - } - - if (charcount < len) - { - HeapFree(GetProcessHeap(), 0, str); - return len - 1; - } - - len = WideCharToMultiByte(cp, 0, str, -1, path, charcount, NULL, NULL); - if (!len) - { - HeapFree(GetProcessHeap(), 0, str); - WARN("WideCharToMultiByte failed\n"); - return 0; - } - - HeapFree(GetProcessHeap(), 0, str); - - return len - 1; -} diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index cb3edf384cb..db9d93e52aa 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -258,10 +258,10 @@ # @ stub CopyContext @ stdcall CopyFileA(str str long) @ stdcall CopyFileExA (str str ptr ptr ptr long) -@ stdcall CopyFileExW (wstr wstr ptr ptr ptr long) +@ stdcall -import CopyFileExW(wstr wstr ptr ptr ptr long) # @ stub CopyFileTransactedA # @ stub CopyFileTransactedW -@ stdcall CopyFileW(wstr wstr long) +@ stdcall -import CopyFileW(wstr wstr long) @ stdcall CopyLZFile(long long) LZCopy @ stdcall CreateActCtxA(ptr) @ stdcall -import CreateActCtxW(ptr) @@ -287,10 +287,10 @@ @ stdcall -import CreateFileMappingNumaW(long ptr long long long wstr long) @ stdcall -import CreateFileMappingW(long ptr long long long wstr) @ stdcall -import CreateFileW(wstr long long ptr long long long) -@ stdcall CreateHardLinkA(str str ptr) +@ stdcall -import CreateHardLinkA(str str ptr) @ stdcall CreateHardLinkTransactedA(str str ptr ptr) @ stdcall CreateHardLinkTransactedW(wstr wstr ptr ptr) -@ stdcall CreateHardLinkW(wstr wstr ptr) +@ stdcall -import CreateHardLinkW(wstr wstr ptr) @ stdcall -import CreateIoCompletionPort(long long long long) @ stdcall CreateJobObjectA(ptr str) @ stdcall CreateJobObjectW(ptr wstr) @@ -326,7 +326,7 @@ @ stdcall CreateSymbolicLinkA(str str long) # @ stub CreateSymbolicLinkTransactedA # @ stub CreateSymbolicLinkTransactedW -@ stdcall CreateSymbolicLinkW(wstr wstr long) +@ stdcall -import CreateSymbolicLinkW(wstr wstr long) @ stdcall CreateTapePartition(long long long long) @ stdcall -import CreateThread(ptr long ptr long long ptr) @ stdcall -import CreateThreadpool(ptr) @@ -684,8 +684,8 @@ @ stdcall -import GetFileSizeEx(long ptr) @ stdcall -import GetFileTime(long ptr ptr ptr) @ stdcall -import GetFileType(long) -@ stdcall GetFinalPathNameByHandleA(long ptr long long) -@ stdcall GetFinalPathNameByHandleW(long ptr long long) +@ stdcall -import GetFinalPathNameByHandleA(long ptr long long) +@ stdcall -import GetFinalPathNameByHandleW(long ptr long long) @ stdcall GetFirmwareEnvironmentVariableA(str str ptr long) @ stdcall GetFirmwareEnvironmentVariableW(wstr wstr ptr long) @ stdcall -import GetFullPathNameA(str long ptr ptr) @@ -1091,12 +1091,12 @@ @ stdcall Module32NextW(long ptr) @ stdcall MoveFileA(str str) @ stdcall MoveFileExA(str str long) -@ stdcall MoveFileExW(wstr wstr long) +@ stdcall -import MoveFileExW(wstr wstr long) @ stdcall MoveFileTransactedA(str str ptr ptr long ptr) @ stdcall MoveFileTransactedW(wstr wstr ptr ptr long ptr) @ stdcall MoveFileW(wstr wstr) @ stdcall MoveFileWithProgressA(str str ptr ptr long) -@ stdcall MoveFileWithProgressW(wstr wstr ptr ptr long) +@ stdcall -import MoveFileWithProgressW(wstr wstr ptr ptr long) @ stdcall MulDiv(long long long) @ stdcall -import MultiByteToWideChar(long long str long ptr long) @ stdcall -import NeedCurrentDirectoryForExePathA(str) @@ -1273,9 +1273,9 @@ @ stdcall RemoveVectoredContinueHandler(ptr) ntdll.RtlRemoveVectoredContinueHandler @ stdcall RemoveVectoredExceptionHandler(ptr) ntdll.RtlRemoveVectoredExceptionHandler @ stdcall -import ReOpenFile(ptr long long long) ReOpenFile -@ stdcall ReplaceFile(wstr wstr wstr long ptr ptr) ReplaceFileW +@ stdcall -import ReplaceFile(wstr wstr wstr long ptr ptr) ReplaceFileW @ stdcall ReplaceFileA(str str str long ptr ptr) -@ stdcall ReplaceFileW(wstr wstr wstr long ptr ptr) +@ stdcall -import ReplaceFileW(wstr wstr wstr long ptr ptr) # @ stub RemoveDirectoryTransactedA # @ stub RemoveDirectoryTransactedW @ stdcall -import RemoveDllDirectory(ptr) diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index 54dbf5a4f6f..0f075d0af1c 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -72,153 +72,6 @@ static DWORD copy_filename_WtoA( LPCWSTR nameW, LPSTR buffer, DWORD len ) return ret; } -/*********************************************************************** - * add_boot_rename_entry - * - * Adds an entry to the registry that is loaded when windows boots and - * checks if there are some files to be removed or renamed/moved. - * has to be valid and may be NULL. If both pointers are - * non-NULL then the file is moved, otherwise it is deleted. The - * entry of the registry key is always appended with two zero - * terminated strings. If is NULL then the second entry is - * simply a single 0-byte. Otherwise the second filename goes - * there. The entries are prepended with \??\ before the path and the - * second filename gets also a '!' as the first character if - * MOVEFILE_REPLACE_EXISTING is set. After the final string another - * 0-byte follows to indicate the end of the strings. - * i.e.: - * \??\D:\test\file1[0] - * !\??\D:\test\file1_renamed[0] - * \??\D:\Test|delete[0] - * [0] <- file is to be deleted, second string empty - * \??\D:\test\file2[0] - * !\??\D:\test\file2_renamed[0] - * [0] <- indicates end of strings - * - * or: - * \??\D:\test\file1[0] - * !\??\D:\test\file1_renamed[0] - * \??\D:\Test|delete[0] - * [0] <- file is to be deleted, second string empty - * [0] <- indicates end of strings - * - */ -static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags ) -{ - static const WCHAR ValueName[] = {'P','e','n','d','i','n','g', - 'F','i','l','e','R','e','n','a','m','e', - 'O','p','e','r','a','t','i','o','n','s',0}; - static const WCHAR SessionW[] = {'\\','R','e','g','i','s','t','r','y','\\', - 'M','a','c','h','i','n','e','\\', - 'S','y','s','t','e','m','\\', - 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', - 'C','o','n','t','r','o','l','\\', - 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0}; - static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ); - - OBJECT_ATTRIBUTES attr; - UNICODE_STRING nameW, source_name, dest_name; - KEY_VALUE_PARTIAL_INFORMATION *info; - BOOL rc = FALSE; - HANDLE Reboot = 0; - DWORD len1, len2; - DWORD DataSize = 0; - BYTE *Buffer = NULL; - WCHAR *p; - - if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL )) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - return FALSE; - } - dest_name.Buffer = NULL; - if (dest && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL )) - { - RtlFreeUnicodeString( &source_name ); - SetLastError( ERROR_PATH_NOT_FOUND ); - return FALSE; - } - - attr.Length = sizeof(attr); - attr.RootDirectory = 0; - attr.ObjectName = &nameW; - attr.Attributes = 0; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - RtlInitUnicodeString( &nameW, SessionW ); - - if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) - { - WARN("Error creating key for reboot management [%s]\n", - "SYSTEM\\CurrentControlSet\\Control\\Session Manager"); - RtlFreeUnicodeString( &source_name ); - RtlFreeUnicodeString( &dest_name ); - return FALSE; - } - - len1 = source_name.Length + sizeof(WCHAR); - if (dest) - { - len2 = dest_name.Length + sizeof(WCHAR); - if (flags & MOVEFILE_REPLACE_EXISTING) - len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */ - } - else len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */ - - RtlInitUnicodeString( &nameW, ValueName ); - - /* First we check if the key exists and if so how many bytes it already contains. */ - if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation, - NULL, 0, &DataSize ) == STATUS_BUFFER_TOO_SMALL) - { - if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) - goto Quit; - if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation, - Buffer, DataSize, &DataSize )) goto Quit; - info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer; - if (info->Type != REG_MULTI_SZ) goto Quit; - if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */ - } - else - { - DataSize = info_size; - if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) ))) - goto Quit; - } - - memcpy( Buffer + DataSize, source_name.Buffer, len1 ); - DataSize += len1; - p = (WCHAR *)(Buffer + DataSize); - if (dest) - { - if (flags & MOVEFILE_REPLACE_EXISTING) - *p++ = '!'; - memcpy( p, dest_name.Buffer, len2 ); - DataSize += len2; - } - else - { - *p = 0; - DataSize += sizeof(WCHAR); - } - - /* add final null */ - p = (WCHAR *)(Buffer + DataSize); - *p = 0; - DataSize += sizeof(WCHAR); - - rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size); - - Quit: - RtlFreeUnicodeString( &source_name ); - RtlFreeUnicodeString( &dest_name ); - if (Reboot) NtClose(Reboot); - HeapFree( GetProcessHeap(), 0, Buffer ); - return(rc); -} - - - /*********************************************************************** * GetShortPathNameA (KERNEL32.@) */ @@ -244,26 +97,6 @@ DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen } -static BOOL is_same_file(HANDLE h1, HANDLE h2) -{ - FILE_ID_INFORMATION id1, id2; - IO_STATUS_BLOCK io; - - return !NtQueryInformationFile( h1, &io, &id1, sizeof(id1), FileIdInformation ) - && !NtQueryInformationFile( h2, &io, &id2, sizeof(id2), FileIdInformation ) - && !memcmp( &id1, &id2, sizeof(FILE_ID_INFORMATION) ); -} - -/************************************************************************** - * CopyFileW (KERNEL32.@) - */ -BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists ) -{ - return CopyFileExW( source, dest, NULL, NULL, NULL, - fail_if_exists ? COPY_FILE_FAIL_IF_EXISTS : 0 ); -} - - /************************************************************************** * CopyFileA (KERNEL32.@) */ @@ -282,101 +115,6 @@ BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists) } -/************************************************************************** - * CopyFileExW (KERNEL32.@) - */ -BOOL WINAPI CopyFileExW(LPCWSTR source, LPCWSTR dest, - LPPROGRESS_ROUTINE progress, LPVOID param, - LPBOOL cancel_ptr, DWORD flags) -{ - static const int buffer_size = 65536; - HANDLE h1, h2; - BY_HANDLE_FILE_INFORMATION info; - DWORD count; - BOOL ret = FALSE; - char *buffer; - - if (!source || !dest) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size ))) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } - - TRACE("%s -> %s, %x\n", debugstr_w(source), debugstr_w(dest), flags); - - if ((h1 = CreateFileW(source, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) - { - WARN("Unable to open source %s\n", debugstr_w(source)); - HeapFree( GetProcessHeap(), 0, buffer ); - return FALSE; - } - - if (!GetFileInformationByHandle( h1, &info )) - { - WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source)); - HeapFree( GetProcessHeap(), 0, buffer ); - CloseHandle( h1 ); - return FALSE; - } - - if (!(flags & COPY_FILE_FAIL_IF_EXISTS)) - { - BOOL same_file = FALSE; - h2 = CreateFileW( dest, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, 0, 0); - if (h2 != INVALID_HANDLE_VALUE) - { - same_file = is_same_file( h1, h2 ); - CloseHandle( h2 ); - } - if (same_file) - { - HeapFree( GetProcessHeap(), 0, buffer ); - CloseHandle( h1 ); - SetLastError( ERROR_SHARING_VIOLATION ); - return FALSE; - } - } - - if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - (flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS, - info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE) - { - WARN("Unable to open dest %s\n", debugstr_w(dest)); - HeapFree( GetProcessHeap(), 0, buffer ); - CloseHandle( h1 ); - return FALSE; - } - - while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count) - { - char *p = buffer; - while (count != 0) - { - DWORD res; - if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done; - p += res; - count -= res; - } - } - ret = TRUE; -done: - /* Maintain the timestamp of source file to destination file */ - SetFileTime(h2, NULL, NULL, &info.ftLastWriteTime); - HeapFree( GetProcessHeap(), 0, buffer ); - CloseHandle( h1 ); - CloseHandle( h2 ); - return ret; -} - - /************************************************************************** * CopyFileExA (KERNEL32.@) */ @@ -421,95 +159,6 @@ BOOL WINAPI MoveFileTransactedW(const WCHAR *source, const WCHAR *dest, LPPROGRE return FALSE; } -/************************************************************************** - * MoveFileWithProgressW (KERNEL32.@) - */ -BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest, - LPPROGRESS_ROUTINE fnProgress, - LPVOID param, DWORD flag ) -{ - FILE_RENAME_INFORMATION *rename_info; - FILE_BASIC_INFORMATION info; - UNICODE_STRING nt_name; - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - NTSTATUS status; - HANDLE source_handle = 0; - ULONG size; - - TRACE("(%s,%s,%p,%p,%04x)\n", - debugstr_w(source), debugstr_w(dest), fnProgress, param, flag ); - - if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) - return add_boot_rename_entry( source, dest, flag ); - - if (!dest) - return DeleteFileW( source ); - - /* check if we are allowed to rename the source */ - - if (!RtlDosPathNameToNtPathName_U( source, &nt_name, NULL, NULL )) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - return FALSE; - } - attr.Length = sizeof(attr); - attr.RootDirectory = 0; - attr.Attributes = OBJ_CASE_INSENSITIVE; - attr.ObjectName = &nt_name; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - - status = NtOpenFile( &source_handle, DELETE | SYNCHRONIZE, &attr, &io, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT ); - RtlFreeUnicodeString( &nt_name ); - if (status != STATUS_SUCCESS) - { - SetLastError( RtlNtStatusToDosError(status) ); - goto error; - } - status = NtQueryInformationFile( source_handle, &io, &info, sizeof(info), FileBasicInformation ); - if (status != STATUS_SUCCESS) - { - SetLastError( RtlNtStatusToDosError(status) ); - goto error; - } - - if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL )) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - goto error; - } - - size = offsetof( FILE_RENAME_INFORMATION, FileName ) + nt_name.Length; - if (!(rename_info = HeapAlloc( GetProcessHeap(), 0, size ))) - goto error; - - rename_info->ReplaceIfExists = !!(flag & MOVEFILE_REPLACE_EXISTING); - rename_info->RootDirectory = NULL; - rename_info->FileNameLength = nt_name.Length; - memcpy( rename_info->FileName, nt_name.Buffer, nt_name.Length ); - RtlFreeUnicodeString( &nt_name ); - status = NtSetInformationFile( source_handle, &io, rename_info, size, FileRenameInformation ); - HeapFree( GetProcessHeap(), 0, rename_info ); - if (status == STATUS_NOT_SAME_DEVICE && (flag & MOVEFILE_COPY_ALLOWED)) - { - NtClose( source_handle ); - if (!CopyFileExW( source, dest, fnProgress, param, NULL, - flag & MOVEFILE_REPLACE_EXISTING ? - 0 : COPY_FILE_FAIL_IF_EXISTS )) - return FALSE; - return DeleteFileW( source ); - } - - NtClose( source_handle ); - return set_ntstatus( status ); - -error: - if (source_handle) NtClose( source_handle ); - return FALSE; -} - /************************************************************************** * MoveFileWithProgressA (KERNEL32.@) */ @@ -533,14 +182,6 @@ BOOL WINAPI MoveFileWithProgressA( LPCSTR source, LPCSTR dest, return ret; } -/************************************************************************** - * MoveFileExW (KERNEL32.@) - */ -BOOL WINAPI MoveFileExW( LPCWSTR source, LPCWSTR dest, DWORD flag ) -{ - return MoveFileWithProgressW( source, dest, NULL, NULL, flag ); -} - /************************************************************************** * MoveFileExA (KERNEL32.@) */ @@ -570,87 +211,6 @@ BOOL WINAPI MoveFileA( LPCSTR source, LPCSTR dest ) } -/************************************************************************* - * CreateHardLinkW (KERNEL32.@) - */ -BOOL WINAPI CreateHardLinkW(LPCWSTR lpFileName, LPCWSTR lpExistingFileName, - LPSECURITY_ATTRIBUTES lpSecurityAttributes) -{ - UNICODE_STRING ntDest, ntSource; - 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); - - ntDest.Buffer = ntSource.Buffer = NULL; - if (!RtlDosPathNameToNtPathName_U( lpFileName, &ntDest, NULL, NULL ) || - !RtlDosPathNameToNtPathName_U( lpExistingFileName, &ntSource, NULL, NULL )) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - goto err; - } - - size = offsetof( FILE_LINK_INFORMATION, FileName ) + ntDest.Length; - if (!(info = HeapAlloc( GetProcessHeap(), 0, size ))) - { - SetLastError( ERROR_OUTOFMEMORY ); - goto err; - } - - 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 ) ); - - NtClose( file ); - -err: - RtlFreeUnicodeString( &ntSource ); - RtlFreeUnicodeString( &ntDest ); - HeapFree( GetProcessHeap(), 0, info ); - return ret; -} - - -/************************************************************************* - * CreateHardLinkA (KERNEL32.@) - */ -BOOL WINAPI CreateHardLinkA(LPCSTR lpFileName, LPCSTR lpExistingFileName, - LPSECURITY_ATTRIBUTES lpSecurityAttributes) -{ - WCHAR *sourceW, *destW; - BOOL res; - - if (!(sourceW = FILE_name_AtoW( lpExistingFileName, TRUE ))) - { - return FALSE; - } - if (!(destW = FILE_name_AtoW( lpFileName, TRUE ))) - { - HeapFree( GetProcessHeap(), 0, sourceW ); - return FALSE; - } - - res = CreateHardLinkW( destW, sourceW, lpSecurityAttributes ); - - HeapFree( GetProcessHeap(), 0, sourceW ); - HeapFree( GetProcessHeap(), 0, destW ); - - return res; -} - - /*********************************************************************** * CreateDirectoryExA (KERNEL32.@) */ @@ -820,15 +380,6 @@ WCHAR * CDECL wine_get_dos_file_name( LPCSTR str ) return nt_name.Buffer; } -/************************************************************************* - * CreateSymbolicLinkW (KERNEL32.@) - */ -BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags) -{ - FIXME("(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags); - return TRUE; -} - /************************************************************************* * CreateSymbolicLinkA (KERNEL32.@) */ diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 9ca184a5d04..f820a067c7f 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -35,6 +35,7 @@ #include "winioctl.h" #include "wincon.h" #include "fileapi.h" +#include "shlwapi.h" #include "ddk/ntddk.h" #include "ddk/ntddser.h" @@ -111,6 +112,136 @@ static inline BOOL contains_path( const WCHAR *name ) } +/*********************************************************************** + * add_boot_rename_entry + * + * Adds an entry to the registry that is loaded when windows boots and + * checks if there are some files to be removed or renamed/moved. + * has to be valid and may be NULL. If both pointers are + * non-NULL then the file is moved, otherwise it is deleted. The + * entry of the registry key is always appended with two zero + * terminated strings. If is NULL then the second entry is + * simply a single 0-byte. Otherwise the second filename goes + * there. The entries are prepended with \??\ before the path and the + * second filename gets also a '!' as the first character if + * MOVEFILE_REPLACE_EXISTING is set. After the final string another + * 0-byte follows to indicate the end of the strings. + * i.e.: + * \??\D:\test\file1[0] + * !\??\D:\test\file1_renamed[0] + * \??\D:\Test|delete[0] + * [0] <- file is to be deleted, second string empty + * \??\D:\test\file2[0] + * !\??\D:\test\file2_renamed[0] + * [0] <- indicates end of strings + * + * or: + * \??\D:\test\file1[0] + * !\??\D:\test\file1_renamed[0] + * \??\D:\Test|delete[0] + * [0] <- file is to be deleted, second string empty + * [0] <- indicates end of strings + * + */ +static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags ) +{ + static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ); + + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW, source_name, dest_name; + KEY_VALUE_PARTIAL_INFORMATION *info; + BOOL rc = FALSE; + HANDLE key = 0; + DWORD len1, len2; + DWORD size = 0; + BYTE *buffer = NULL; + WCHAR *p; + + if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + dest_name.Buffer = NULL; + if (dest && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL )) + { + RtlFreeUnicodeString( &source_name ); + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" ); + + if (NtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS) + { + RtlFreeUnicodeString( &source_name ); + RtlFreeUnicodeString( &dest_name ); + return FALSE; + } + + len1 = source_name.Length + sizeof(WCHAR); + if (dest) + { + len2 = dest_name.Length + sizeof(WCHAR); + if (flags & MOVEFILE_REPLACE_EXISTING) + len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */ + } + else len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */ + + RtlInitUnicodeString( &nameW, L"PendingFileRenameOperations" ); + + /* First we check if the key exists and if so how many bytes it already contains. */ + if (NtQueryValueKey( key, &nameW, KeyValuePartialInformation, + NULL, 0, &size ) == STATUS_BUFFER_TOO_SMALL) + { + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size + len1 + len2 + sizeof(WCHAR) ))) goto done; + if (NtQueryValueKey( key, &nameW, KeyValuePartialInformation, buffer, size, &size )) goto done; + info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + if (info->Type != REG_MULTI_SZ) goto done; + if (size > sizeof(info)) size -= sizeof(WCHAR); /* remove terminating null (will be added back later) */ + } + else + { + size = info_size; + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size + len1 + len2 + sizeof(WCHAR) ))) goto done; + } + + memcpy( buffer + size, source_name.Buffer, len1 ); + size += len1; + p = (WCHAR *)(buffer + size); + if (dest) + { + if (flags & MOVEFILE_REPLACE_EXISTING) *p++ = '!'; + memcpy( p, dest_name.Buffer, len2 ); + size += len2; + } + else + { + *p = 0; + size += sizeof(WCHAR); + } + + /* add final null */ + p = (WCHAR *)(buffer + size); + *p = 0; + size += sizeof(WCHAR); + rc = !NtSetValueKey( key, &nameW, 0, REG_MULTI_SZ, buffer + info_size, size - info_size ); + + done: + RtlFreeUnicodeString( &source_name ); + RtlFreeUnicodeString( &dest_name ); + if (key) NtClose(key); + HeapFree( GetProcessHeap(), 0, buffer ); + return rc; +} + + /*********************************************************************** * append_ext */ @@ -337,6 +468,20 @@ DWORD file_name_WtoA( LPCWSTR src, INT srclen, LPSTR dest, INT destlen ) } +/*********************************************************************** + * is_same_file + */ +static BOOL is_same_file( HANDLE h1, HANDLE h2 ) +{ + FILE_ID_INFORMATION id1, id2; + IO_STATUS_BLOCK io; + + return !NtQueryInformationFile( h1, &io, &id1, sizeof(id1), FileIdInformation ) && + !NtQueryInformationFile( h2, &io, &id2, sizeof(id2), FileIdInformation ) && + !memcmp( &id1, &id2, sizeof(FILE_ID_INFORMATION) ); +} + + /****************************************************************************** * AreFileApisANSI (kernelbase.@) */ @@ -346,6 +491,107 @@ BOOL WINAPI DECLSPEC_HOTPATCH AreFileApisANSI(void) } +/*********************************************************************** + * CopyFileExW (kernelbase.@) + */ +BOOL WINAPI CopyFileExW( const WCHAR *source, const WCHAR *dest, LPPROGRESS_ROUTINE progress, + void *param, BOOL *cancel_ptr, DWORD flags ) +{ + static const int buffer_size = 65536; + HANDLE h1, h2; + BY_HANDLE_FILE_INFORMATION info; + DWORD count; + BOOL ret = FALSE; + char *buffer; + + if (!source || !dest) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + TRACE("%s -> %s, %x\n", debugstr_w(source), debugstr_w(dest), flags); + + if ((h1 = CreateFileW( source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE) + { + WARN("Unable to open source %s\n", debugstr_w(source)); + HeapFree( GetProcessHeap(), 0, buffer ); + return FALSE; + } + + if (!GetFileInformationByHandle( h1, &info )) + { + WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source)); + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + return FALSE; + } + + if (!(flags & COPY_FILE_FAIL_IF_EXISTS)) + { + BOOL same_file = FALSE; + h2 = CreateFileW( dest, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 ); + if (h2 != INVALID_HANDLE_VALUE) + { + same_file = is_same_file( h1, h2 ); + CloseHandle( h2 ); + } + if (same_file) + { + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + SetLastError( ERROR_SHARING_VIOLATION ); + return FALSE; + } + } + + if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + (flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS, + info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE) + { + WARN("Unable to open dest %s\n", debugstr_w(dest)); + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + return FALSE; + } + + while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count) + { + char *p = buffer; + while (count != 0) + { + DWORD res; + if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done; + p += res; + count -= res; + } + } + ret = TRUE; +done: + /* Maintain the timestamp of source file to destination file */ + SetFileTime( h2, NULL, NULL, &info.ftLastWriteTime ); + HeapFree( GetProcessHeap(), 0, buffer ); + CloseHandle( h1 ); + CloseHandle( h2 ); + return ret; +} + + +/************************************************************************** + * CopyFileW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CopyFileW( const WCHAR *source, const WCHAR *dest, BOOL fail_if_exists ) +{ + return CopyFileExW( source, dest, NULL, NULL, NULL, fail_if_exists ? COPY_FILE_FAIL_IF_EXISTS : 0 ); +} + + /*********************************************************************** * CreateDirectoryA (kernelbase.@) */ @@ -622,6 +868,88 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateFileW( LPCWSTR filename, DWORD access, DWO } +/************************************************************************* + * CreateHardLinkA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH CreateHardLinkA( const char *dest, const char *source, + SECURITY_ATTRIBUTES *attr ) +{ + WCHAR *sourceW, *destW; + BOOL res; + + if (!(sourceW = file_name_AtoW( source, TRUE ))) return FALSE; + if (!(destW = file_name_AtoW( dest, TRUE ))) + { + HeapFree( GetProcessHeap(), 0, sourceW ); + return FALSE; + } + res = CreateHardLinkW( destW, sourceW, attr ); + HeapFree( GetProcessHeap(), 0, sourceW ); + HeapFree( GetProcessHeap(), 0, destW ); + return res; +} + + +/************************************************************************* + * CreateHardLinkW (kernelbase.@) + */ +BOOL WINAPI CreateHardLinkW( LPCWSTR dest, LPCWSTR source, SECURITY_ATTRIBUTES *sec_attr ) +{ + UNICODE_STRING ntDest, ntSource; + 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(dest), debugstr_w(source), sec_attr ); + + ntDest.Buffer = ntSource.Buffer = NULL; + if (!RtlDosPathNameToNtPathName_U( dest, &ntDest, NULL, NULL ) || + !RtlDosPathNameToNtPathName_U( source, &ntSource, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + goto done; + } + + size = offsetof( FILE_LINK_INFORMATION, FileName ) + ntDest.Length; + if (!(info = HeapAlloc( GetProcessHeap(), 0, size ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + goto done; + } + + 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 done; + + 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 ) ); + NtClose( file ); + +done: + RtlFreeUnicodeString( &ntSource ); + RtlFreeUnicodeString( &ntDest ); + HeapFree( GetProcessHeap(), 0, info ); + return ret; +} + + +/************************************************************************* + * CreateSymbolicLinkW (kernelbase.@) + */ +BOOLEAN WINAPI /* DECLSPEC_HOTPATCH */ CreateSymbolicLinkW( LPCWSTR link, LPCWSTR target, DWORD flags ) +{ + FIXME( "(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags ); + return TRUE; +} + + /*********************************************************************** * DeleteFileA (kernelbase.@) */ @@ -1331,6 +1659,185 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetFileAttributesExW( LPCWSTR name, GET_FILEEX_INF } +/*********************************************************************** + * GetFinalPathNameByHandleA (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleA( HANDLE file, LPSTR path, + DWORD count, DWORD flags ) +{ + WCHAR *str; + DWORD result, len; + + TRACE( "(%p,%p,%d,%x)\n", file, path, count, flags); + + len = GetFinalPathNameByHandleW(file, NULL, 0, flags); + if (len == 0) return 0; + + str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!str) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + + result = GetFinalPathNameByHandleW(file, str, len, flags); + if (result != len - 1) + { + HeapFree(GetProcessHeap(), 0, str); + return 0; + } + + len = file_name_WtoA( str, -1, NULL, 0 ); + if (count < len) + { + HeapFree(GetProcessHeap(), 0, str); + return len - 1; + } + file_name_WtoA( str, -1, path, count ); + HeapFree(GetProcessHeap(), 0, str); + return len - 1; +} + + +/*********************************************************************** + * GetFinalPathNameByHandleW (kernelbase.@) + */ +DWORD WINAPI DECLSPEC_HOTPATCH GetFinalPathNameByHandleW( HANDLE file, LPWSTR path, + DWORD count, DWORD flags ) +{ + WCHAR buffer[sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH + 1]; + OBJECT_NAME_INFORMATION *info = (OBJECT_NAME_INFORMATION*)&buffer; + WCHAR drive_part[MAX_PATH]; + DWORD drive_part_len = 0; + NTSTATUS status; + DWORD result = 0; + ULONG dummy; + WCHAR *ptr; + + TRACE( "(%p,%p,%d,%x)\n", file, path, count, flags ); + + if (flags & ~(FILE_NAME_OPENED | VOLUME_NAME_GUID | VOLUME_NAME_NONE | VOLUME_NAME_NT)) + { + WARN("Unknown flags: %x\n", flags); + SetLastError( ERROR_INVALID_PARAMETER ); + return 0; + } + + /* get object name */ + status = NtQueryObject( file, ObjectNameInformation, &buffer, sizeof(buffer) - sizeof(WCHAR), &dummy ); + if (!set_ntstatus( status )) return 0; + + if (!info->Name.Buffer) + { + SetLastError( ERROR_INVALID_HANDLE ); + return 0; + } + if (info->Name.Length < 4 * sizeof(WCHAR) || info->Name.Buffer[0] != '\\' || + info->Name.Buffer[1] != '?' || info->Name.Buffer[2] != '?' || info->Name.Buffer[3] != '\\' ) + { + FIXME("Unexpected object name: %s\n", debugstr_wn(info->Name.Buffer, info->Name.Length / sizeof(WCHAR))); + SetLastError( ERROR_GEN_FAILURE ); + return 0; + } + + /* add terminating null character, remove "\\??\\" */ + info->Name.Buffer[info->Name.Length / sizeof(WCHAR)] = 0; + info->Name.Length -= 4 * sizeof(WCHAR); + info->Name.Buffer += 4; + + /* FILE_NAME_OPENED is not supported yet, and would require Wineserver changes */ + if (flags & FILE_NAME_OPENED) + { + FIXME("FILE_NAME_OPENED not supported\n"); + flags &= ~FILE_NAME_OPENED; + } + + /* Get information required for VOLUME_NAME_NONE, VOLUME_NAME_GUID and VOLUME_NAME_NT */ + if (flags == VOLUME_NAME_NONE || flags == VOLUME_NAME_GUID || flags == VOLUME_NAME_NT) + { + if (!GetVolumePathNameW( info->Name.Buffer, drive_part, MAX_PATH )) return 0; + drive_part_len = lstrlenW(drive_part); + if (!drive_part_len || drive_part_len > lstrlenW(info->Name.Buffer) || + drive_part[drive_part_len-1] != '\\' || + CompareStringOrdinal( info->Name.Buffer, drive_part_len, drive_part, drive_part_len, TRUE ) != CSTR_EQUAL) + { + FIXME( "Path %s returned by GetVolumePathNameW does not match file path %s\n", + debugstr_w(drive_part), debugstr_w(info->Name.Buffer) ); + SetLastError( ERROR_GEN_FAILURE ); + return 0; + } + } + + if (flags == VOLUME_NAME_NONE) + { + ptr = info->Name.Buffer + drive_part_len - 1; + result = lstrlenW(ptr); + if (result < count) memcpy(path, ptr, (result + 1) * sizeof(WCHAR)); + else result++; + } + else if (flags == VOLUME_NAME_GUID) + { + WCHAR volume_prefix[51]; + + /* GetVolumeNameForVolumeMountPointW sets error code on failure */ + if (!GetVolumeNameForVolumeMountPointW( drive_part, volume_prefix, 50 )) return 0; + ptr = info->Name.Buffer + drive_part_len; + result = lstrlenW(volume_prefix) + lstrlenW(ptr); + if (result < count) + { + lstrcpyW(path, volume_prefix); + lstrcatW(path, ptr); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else if (flags == VOLUME_NAME_NT) + { + WCHAR nt_prefix[MAX_PATH]; + + /* QueryDosDeviceW sets error code on failure */ + drive_part[drive_part_len - 1] = 0; + if (!QueryDosDeviceW( drive_part, nt_prefix, MAX_PATH )) return 0; + ptr = info->Name.Buffer + drive_part_len - 1; + result = lstrlenW(nt_prefix) + lstrlenW(ptr); + if (result < count) + { + lstrcpyW(path, nt_prefix); + lstrcatW(path, ptr); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else if (flags == VOLUME_NAME_DOS) + { + result = 4 + lstrlenW(info->Name.Buffer); + if (result < count) + { + lstrcpyW(path, L"\\\\?\\"); + lstrcatW(path, info->Name.Buffer); + } + else + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + result++; + } + } + else + { + /* Windows crashes here, but we prefer returning ERROR_INVALID_PARAMETER */ + WARN("Invalid combination of flags: %x\n", flags); + SetLastError( ERROR_INVALID_PARAMETER ); + } + return result; +} + + /*********************************************************************** * GetFullPathNameA (kernelbase.@) */ @@ -1891,6 +2398,94 @@ UINT WINAPI DECLSPEC_HOTPATCH GetWindowsDirectoryW( LPWSTR path, UINT count ) } +/************************************************************************** + * MoveFileExW (kernelbase.@) + */ +BOOL WINAPI MoveFileExW( const WCHAR *source, const WCHAR *dest, DWORD flag ) +{ + return MoveFileWithProgressW( source, dest, NULL, NULL, flag ); +} + + +/************************************************************************** + * MoveFileWithProgressW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH MoveFileWithProgressW( const WCHAR *source, const WCHAR *dest, + LPPROGRESS_ROUTINE progress, + void *param, DWORD flag ) +{ + FILE_RENAME_INFORMATION *rename_info; + FILE_BASIC_INFORMATION info; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE source_handle = 0; + ULONG size; + + TRACE( "(%s,%s,%p,%p,%04x)\n", debugstr_w(source), debugstr_w(dest), progress, param, flag ); + + if (flag & MOVEFILE_DELAY_UNTIL_REBOOT) return add_boot_rename_entry( source, dest, flag ); + + if (!dest) return DeleteFileW( source ); + + /* check if we are allowed to rename the source */ + + if (!RtlDosPathNameToNtPathName_U( source, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &source_handle, DELETE | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT ); + RtlFreeUnicodeString( &nt_name ); + if (!set_ntstatus( status )) goto error; + + status = NtQueryInformationFile( source_handle, &io, &info, sizeof(info), FileBasicInformation ); + if (!set_ntstatus( status )) goto error; + + if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + goto error; + } + + size = offsetof( FILE_RENAME_INFORMATION, FileName ) + nt_name.Length; + if (!(rename_info = HeapAlloc( GetProcessHeap(), 0, size ))) goto error; + + rename_info->ReplaceIfExists = !!(flag & MOVEFILE_REPLACE_EXISTING); + rename_info->RootDirectory = NULL; + rename_info->FileNameLength = nt_name.Length; + memcpy( rename_info->FileName, nt_name.Buffer, nt_name.Length ); + RtlFreeUnicodeString( &nt_name ); + status = NtSetInformationFile( source_handle, &io, rename_info, size, FileRenameInformation ); + HeapFree( GetProcessHeap(), 0, rename_info ); + if (status == STATUS_NOT_SAME_DEVICE && (flag & MOVEFILE_COPY_ALLOWED)) + { + NtClose( source_handle ); + if (!CopyFileExW( source, dest, progress, param, NULL, + flag & MOVEFILE_REPLACE_EXISTING ? 0 : COPY_FILE_FAIL_IF_EXISTS )) + return FALSE; + return DeleteFileW( source ); + } + + NtClose( source_handle ); + return set_ntstatus( status ); + +error: + if (source_handle) NtClose( source_handle ); + return FALSE; +} + + /*********************************************************************** * NeedCurrentDirectoryForExePathA (kernelbase.@) */ @@ -1916,6 +2511,111 @@ BOOL WINAPI DECLSPEC_HOTPATCH NeedCurrentDirectoryForExePathW( LPCWSTR name ) } +/*********************************************************************** + * ReplaceFileW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH ReplaceFileW( const WCHAR *replaced, const WCHAR *replacement, + const WCHAR *backup, DWORD flags, + void *exclude, void *reserved ) +{ + UNICODE_STRING nt_replaced_name, nt_replacement_name; + HANDLE hReplacement = NULL; + NTSTATUS status; + IO_STATUS_BLOCK io; + OBJECT_ATTRIBUTES attr; + FILE_BASIC_INFORMATION info; + + TRACE( "%s %s %s 0x%08x %p %p\n", debugstr_w(replaced), debugstr_w(replacement), debugstr_w(backup), + flags, exclude, reserved ); + + if (flags) FIXME("Ignoring flags %x\n", flags); + + /* First two arguments are mandatory */ + if (!replaced || !replacement) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = NULL; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + /* Open the "replaced" file for reading */ + if (!RtlDosPathNameToNtPathName_U( replaced, &nt_replaced_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.ObjectName = &nt_replaced_name; + + /* Replacement should fail if replaced is READ_ONLY */ + status = NtQueryAttributesFile(&attr, &info); + RtlFreeUnicodeString(&nt_replaced_name); + if (!set_ntstatus( status )) return FALSE; + + if (info.FileAttributes & FILE_ATTRIBUTE_READONLY) + { + SetLastError( ERROR_ACCESS_DENIED ); + return FALSE; + } + + /* + * Open the replacement file for reading, writing, and deleting + * (writing and deleting are needed when finished) + */ + if (!RtlDosPathNameToNtPathName_U( replacement, &nt_replacement_name, NULL, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return FALSE; + } + attr.ObjectName = &nt_replacement_name; + status = NtOpenFile( &hReplacement, GENERIC_READ | GENERIC_WRITE | DELETE | WRITE_DAC | SYNCHRONIZE, + &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE ); + RtlFreeUnicodeString(&nt_replacement_name); + if (!set_ntstatus( status )) return FALSE; + NtClose( hReplacement ); + + /* If the user wants a backup then that needs to be performed first */ + if (backup) + { + if (!MoveFileExW( replaced, backup, MOVEFILE_REPLACE_EXISTING )) return FALSE; + } + else + { + /* ReplaceFile() can replace an open target. To do this, we need to move + * it out of the way first. */ + WCHAR temp_path[MAX_PATH], temp_file[MAX_PATH]; + + lstrcpynW( temp_path, replaced, ARRAY_SIZE( temp_path ) ); + PathRemoveFileSpecW( temp_path ); + if (!GetTempFileNameW( temp_path, L"rf", 0, temp_file ) || + !MoveFileExW( replaced, temp_file, MOVEFILE_REPLACE_EXISTING )) + return FALSE; + + DeleteFileW( temp_file ); + } + + /* + * Now that the backup has been performed (if requested), copy the replacement + * into place + */ + if (!MoveFileExW( replacement, replaced, 0 )) + { + /* on failure we need to indicate whether a backup was made */ + if (!backup) + SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT ); + else + SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 ); + return FALSE; + } + return TRUE; +} + + /*********************************************************************** * SearchPathA (kernelbase.@) */ diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index a3a208e8fe2..904f1704eb1 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -166,8 +166,8 @@ @ stdcall ConvertToAutoInheritPrivateObjectSecurity(ptr ptr ptr ptr long ptr) # @ stub CopyContext # @ stub CopyFile2 -@ stdcall CopyFileExW(wstr wstr ptr ptr ptr long) kernel32.CopyFileExW -@ stdcall CopyFileW(wstr wstr long) kernel32.CopyFileW +@ stdcall CopyFileExW(wstr wstr ptr ptr ptr long) +@ stdcall CopyFileW(wstr wstr long) # @ stub -arch=x86_64 CopyMemoryNonTemporal @ stdcall CopySid(long ptr ptr) # @ stub CouldMultiUserAppsBehaviorBePossibleForPackage @@ -191,8 +191,8 @@ @ stdcall CreateFileMappingNumaW(long ptr long long long wstr long) @ stdcall CreateFileMappingW(long ptr long long long wstr) @ stdcall CreateFileW(wstr long long ptr long long long) -@ stdcall CreateHardLinkA(str str ptr) kernel32.CreateHardLinkA -@ stdcall CreateHardLinkW(wstr wstr ptr) kernel32.CreateHardLinkW +@ stdcall CreateHardLinkA(str str ptr) +@ stdcall CreateHardLinkW(wstr wstr ptr) @ stdcall CreateIoCompletionPort(long long long long) @ stdcall CreateMemoryResourceNotification(long) @ stdcall CreateMutexA(ptr long str) @@ -221,7 +221,7 @@ # @ stub CreateStateContainer # @ stub CreateStateLock # @ stub CreateStateSubcontainer -@ stdcall CreateSymbolicLinkW(wstr wstr long) kernel32.CreateSymbolicLinkW +@ stdcall CreateSymbolicLinkW(wstr wstr long) @ stdcall CreateThread(ptr long ptr long long ptr) @ stdcall CreateThreadpool(ptr) @ stdcall CreateThreadpoolCleanupGroup() @@ -524,8 +524,8 @@ @ stdcall GetFileVersionInfoSizeExW(long wstr ptr) @ stdcall GetFileVersionInfoSizeW(wstr ptr) @ stdcall GetFileVersionInfoW(wstr long long ptr) -@ stdcall GetFinalPathNameByHandleA(long ptr long long) kernel32.GetFinalPathNameByHandleA -@ stdcall GetFinalPathNameByHandleW(long ptr long long) kernel32.GetFinalPathNameByHandleW +@ stdcall GetFinalPathNameByHandleA(long ptr long long) +@ stdcall GetFinalPathNameByHandleW(long ptr long long) @ stdcall GetFullPathNameA(str long ptr ptr) @ stdcall GetFullPathNameW(wstr long ptr ptr) # @ stub GetGPOListInternalA @@ -953,9 +953,9 @@ @ stdcall MapViewOfFileEx(long long long long long ptr) @ stdcall MapViewOfFileExNuma(long long long long long ptr long) # @ stub MapViewOfFileFromApp -@ stdcall MoveFileExW(wstr wstr long) kernel32.MoveFileExW +@ stdcall MoveFileExW(wstr wstr long) # @ stub MoveFileWithProgressTransactedW -@ stdcall MoveFileWithProgressW(wstr wstr ptr ptr long) kernel32.MoveFileWithProgressW +@ stdcall MoveFileWithProgressW(wstr wstr ptr ptr long) @ stdcall MulDiv(long long long) kernel32.MulDiv @ stdcall MultiByteToWideChar(long long str long ptr long) # @ stub NamedPipeEventEnum @@ -1339,7 +1339,7 @@ @ stdcall RemoveVectoredContinueHandler(ptr) ntdll.RtlRemoveVectoredContinueHandler @ stdcall RemoveVectoredExceptionHandler(ptr) ntdll.RtlRemoveVectoredExceptionHandler # @ stub ReplaceFileExInternal -@ stdcall ReplaceFileW(wstr wstr wstr long ptr ptr) kernel32.ReplaceFileW +@ stdcall ReplaceFileW(wstr wstr wstr long ptr ptr) @ stdcall ResetEvent(long) # @ stub ResetState @ stdcall ResetWriteWatch(ptr long)