kernel32: Move some file functions to kernelbase.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
feature/deterministic
Alexandre Julliard 2020-05-05 13:10:03 +02:00
parent e36a9c459d
commit b6c9401882
5 changed files with 721 additions and 796 deletions

View File

@ -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;
}

View File

@ -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)

View File

@ -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.
* <fn1> has to be valid and <fn2> 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 <fn2> 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.@)
*/

View File

@ -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.
* <fn1> has to be valid and <fn2> 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 <fn2> 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.@)
*/

View File

@ -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)