mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 11:56:26 +00:00
4f0b8d3db0
svn path=/branches/ntvdm/; revision=59241
1011 lines
29 KiB
C
1011 lines
29 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: lib/kernel32/file/file.c
|
|
* PURPOSE: Directory functions
|
|
* PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
|
|
* Gerhard W. Gruber (sparhawk_at_gmx.at)
|
|
* Dmitry Philippov (shedon@mail.ru)
|
|
* UPDATE HISTORY:
|
|
* Created 01/11/98
|
|
* DP (29/07/2006)
|
|
* Fix some bugs in the add_boot_rename_entry function
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <k32.h>
|
|
#include <malloc.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
DEBUG_CHANNEL(kernel32file);
|
|
|
|
/* GLOBALS *****************************************************************/
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
static BOOL
|
|
RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName)
|
|
{
|
|
DWORD Attributes;
|
|
Attributes = GetFileAttributesW(lpFileName);
|
|
if (Attributes != INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
return SetFileAttributesW(lpFileName,Attributes -
|
|
(Attributes & ~FILE_ATTRIBUTE_READONLY));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* 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 registrykey 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};
|
|
|
|
UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
|
|
|
|
static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
|
|
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING nameW, source_name, dest_name;
|
|
KEY_VALUE_PARTIAL_INFORMATION *info;
|
|
BOOL rc = FALSE;
|
|
HANDLE Reboot = NULL;
|
|
DWORD len1, len2;
|
|
DWORD DestLen = 0;
|
|
DWORD DataSize = 0;
|
|
BYTE *Buffer = NULL;
|
|
WCHAR *p;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("add_boot_rename_entry( %S, %S, %d ) \n", source, dest, flags);
|
|
|
|
if(dest)
|
|
DestLen = wcslen(dest);
|
|
|
|
if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL ))
|
|
{
|
|
SetLastError( ERROR_PATH_NOT_FOUND );
|
|
return FALSE;
|
|
}
|
|
dest_name.Buffer = NULL;
|
|
if (DestLen && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL ))
|
|
{
|
|
RtlFreeHeap( RtlGetProcessHeap(), 0, source_name.Buffer );
|
|
SetLastError( ERROR_PATH_NOT_FOUND );
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateKey(&Reboot,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL);
|
|
|
|
if (Status == STATUS_ACCESS_DENIED)
|
|
{
|
|
Status = NtCreateKey(
|
|
&Reboot,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_BACKUP_RESTORE,
|
|
NULL);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
WARN("NtCreateKey() failed (Status 0x%lx)\n", Status);
|
|
if (source_name.Buffer)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer);
|
|
if (dest_name.Buffer)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
len1 = source_name.Length + sizeof(WCHAR);
|
|
if (DestLen)
|
|
{
|
|
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. */
|
|
Status = NtQueryValueKey(
|
|
Reboot,
|
|
&nameW,
|
|
KeyValuePartialInformation,
|
|
NULL,
|
|
0,
|
|
&DataSize );
|
|
if ((Status == STATUS_BUFFER_OVERFLOW) ||
|
|
(Status == STATUS_BUFFER_TOO_SMALL))
|
|
{
|
|
if (!(Buffer = HeapAlloc(GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR))))
|
|
goto Quit;
|
|
Status = NtQueryValueKey(Reboot, &nameW, KeyValuePartialInformation,
|
|
Buffer, DataSize, &DataSize);
|
|
if(!NT_SUCCESS(Status))
|
|
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 (DestLen)
|
|
{
|
|
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 = NT_SUCCESS(NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size));
|
|
|
|
Quit:
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, source_name.Buffer);
|
|
if (dest_name.Buffer)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name.Buffer);
|
|
NtClose(Reboot);
|
|
if(Buffer)
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
return(rc);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
MoveFileWithProgressW (
|
|
LPCWSTR lpExistingFileName,
|
|
LPCWSTR lpNewFileName,
|
|
LPPROGRESS_ROUTINE lpProgressRoutine,
|
|
LPVOID lpData,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HANDLE hFile = NULL, hNewFile = NULL;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PFILE_RENAME_INFORMATION FileRename;
|
|
NTSTATUS errCode;
|
|
BOOL Result;
|
|
UNICODE_STRING DstPathU;
|
|
BOOL folder = FALSE;
|
|
|
|
TRACE("MoveFileWithProgressW()\n");
|
|
|
|
if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT)
|
|
return add_boot_rename_entry( lpExistingFileName, lpNewFileName, dwFlags );
|
|
|
|
// if (dwFlags & MOVEFILE_WRITE_THROUGH)
|
|
// FIXME("MOVEFILE_WRITE_THROUGH unimplemented\n");
|
|
|
|
if (!lpNewFileName)
|
|
return DeleteFileW(lpExistingFileName);
|
|
|
|
/* validate & translate the filename */
|
|
if (!RtlDosPathNameToNtPathName_U (lpNewFileName,
|
|
&DstPathU,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
WARN("Invalid destination path\n");
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&DstPathU,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
errCode = NtOpenFile( &hNewFile,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
0,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
|
|
((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_WRITE_THROUGH : 0) );
|
|
|
|
if (NT_SUCCESS(errCode)) /* Destination exists */
|
|
{
|
|
NtClose(hNewFile);
|
|
|
|
if (!(dwFlags & MOVEFILE_REPLACE_EXISTING))
|
|
{
|
|
SetLastError(ERROR_ALREADY_EXISTS);
|
|
return FALSE;
|
|
}
|
|
else if (GetFileAttributesW(lpNewFileName) & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
hFile = CreateFileW (lpExistingFileName,
|
|
GENERIC_ALL,
|
|
FILE_SHARE_WRITE|FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS |
|
|
((dwFlags & MOVEFILE_WRITE_THROUGH) ? FILE_FLAG_WRITE_THROUGH : 0),
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
FileRename = RtlAllocateHeap(
|
|
RtlGetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length);
|
|
if( !FileRename ) {
|
|
CloseHandle(hFile);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
if( dwFlags & MOVEFILE_REPLACE_EXISTING ) {
|
|
FileRename->ReplaceIfExists = TRUE;
|
|
}
|
|
else {
|
|
FileRename->ReplaceIfExists = FALSE;
|
|
}
|
|
|
|
|
|
memcpy(FileRename->FileName, DstPathU.Buffer, DstPathU.Length);
|
|
RtlFreeHeap (RtlGetProcessHeap (),
|
|
0,
|
|
DstPathU.Buffer);
|
|
|
|
FileRename->FileNameLength = DstPathU.Length;
|
|
errCode = NtSetInformationFile (hFile,
|
|
&IoStatusBlock,
|
|
FileRename,
|
|
sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length,
|
|
FileRenameInformation);
|
|
CloseHandle(hFile);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, FileRename);
|
|
|
|
if (GetFileAttributesW(lpExistingFileName) & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
folder = TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* FIXME:
|
|
* Fail now move the folder
|
|
* Before we fail at CreateFileW
|
|
*/
|
|
|
|
|
|
if (NT_SUCCESS(errCode))
|
|
{
|
|
Result = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (folder==FALSE)
|
|
{
|
|
Result = CopyFileExW (lpExistingFileName,
|
|
lpNewFileName,
|
|
lpProgressRoutine,
|
|
lpData,
|
|
NULL,
|
|
(dwFlags & MOVEFILE_REPLACE_EXISTING) ? 0 : COPY_FILE_FAIL_IF_EXISTS);
|
|
if (Result)
|
|
{
|
|
/* Cleanup the source file */
|
|
Result = DeleteFileW (lpExistingFileName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* move folder code start */
|
|
WIN32_FIND_DATAW findBuffer;
|
|
LPWSTR lpExistingFileName2 = NULL;
|
|
LPWSTR lpNewFileName2 = NULL;
|
|
LPWSTR lpDeleteFile = NULL;
|
|
INT size;
|
|
INT size2;
|
|
BOOL loop = TRUE;
|
|
BOOL Result = FALSE;
|
|
INT max_size = MAX_PATH;
|
|
|
|
|
|
/* Build the string */
|
|
size = wcslen(lpExistingFileName);
|
|
if (size+6> max_size)
|
|
max_size = size + 6;
|
|
|
|
lpDeleteFile = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
|
|
if (lpDeleteFile == NULL)
|
|
return FALSE;
|
|
|
|
lpNewFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
|
|
if (lpNewFileName2 == NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
|
|
return FALSE;
|
|
}
|
|
|
|
lpExistingFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
|
|
if (lpExistingFileName2 == NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy( (WCHAR *)lpExistingFileName2,lpExistingFileName);
|
|
wcscpy( (WCHAR *)&lpExistingFileName2[size],L"\\*.*\0");
|
|
|
|
/* Get the file name */
|
|
memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
|
|
hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
loop=FALSE;
|
|
|
|
if (findBuffer.cFileName[0] == L'\0')
|
|
loop=FALSE;
|
|
|
|
|
|
/* FIXME
|
|
* remove readonly flag from source folder and do not set the readonly flag to dest folder
|
|
*/
|
|
RemoveReadOnlyAttributeW(lpExistingFileName);
|
|
RemoveReadOnlyAttributeW(lpNewFileName);
|
|
//CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
|
|
CreateDirectoryW(lpNewFileName, NULL);
|
|
|
|
/* search the files/folders and move them */
|
|
while (loop==TRUE)
|
|
{
|
|
Result = TRUE;
|
|
|
|
if ((!wcscmp(findBuffer.cFileName,L"..")) || (!wcscmp(findBuffer.cFileName,L".")))
|
|
{
|
|
loop = FindNextFileW(hFile, &findBuffer);
|
|
|
|
if (!loop)
|
|
{
|
|
size = wcslen(lpExistingFileName2)-4;
|
|
FindClose(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
wcscpy( &lpExistingFileName2[size],L"\0");
|
|
|
|
if (wcsncmp(lpExistingFileName,lpExistingFileName2,size))
|
|
{
|
|
DWORD Attributes;
|
|
|
|
/* delete folder */
|
|
TRACE("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile);
|
|
|
|
/* remove system folder flag other wise we can not delete the folder */
|
|
Attributes = GetFileAttributesW(lpExistingFileName2);
|
|
if (Attributes != INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
SetFileAttributesW(lpExistingFileName2,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
|
|
}
|
|
|
|
RemoveReadOnlyAttributeW(lpExistingFileName2);
|
|
|
|
Result = RemoveDirectoryW(lpExistingFileName2);
|
|
if (Result == FALSE)
|
|
break;
|
|
|
|
loop=TRUE;
|
|
size = wcslen(lpExistingFileName);
|
|
|
|
if (size+6>max_size)
|
|
{
|
|
if (lpNewFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
|
|
|
|
if (lpExistingFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
|
|
|
|
if (lpDeleteFile != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy( lpExistingFileName2,lpExistingFileName);
|
|
wcscpy( &lpExistingFileName2[size],L"\\*.*\0");
|
|
|
|
/* Get the file name */
|
|
memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
|
|
hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
|
|
/* Build the new src string */
|
|
size = wcslen(findBuffer.cFileName);
|
|
size2= wcslen(lpExistingFileName2);
|
|
|
|
if (size2+size+6>max_size)
|
|
{
|
|
FindClose(hFile);
|
|
|
|
if (lpNewFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
|
|
|
|
if (lpExistingFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
|
|
|
|
if (lpDeleteFile != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy( &lpExistingFileName2[size2-3],findBuffer.cFileName);
|
|
wcscpy( &lpExistingFileName2[size2+size-3],L"\0");
|
|
|
|
|
|
/* Continue */
|
|
wcscpy( lpDeleteFile,lpExistingFileName2);
|
|
wcscpy( &lpExistingFileName2[size2+size-3],L"\\*.*\0");
|
|
|
|
|
|
/* Build the new dst string */
|
|
size = wcslen(lpExistingFileName2) + wcslen(lpNewFileName);
|
|
size2 = wcslen(lpExistingFileName);
|
|
|
|
if (size>max_size)
|
|
{
|
|
FindClose(hFile);
|
|
|
|
if (lpNewFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
|
|
|
|
if (lpExistingFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
|
|
|
|
if (lpDeleteFile != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy( lpNewFileName2,lpNewFileName);
|
|
size = wcslen(lpNewFileName);
|
|
wcscpy( &lpNewFileName2[size], &lpExistingFileName2[size2]);
|
|
size = wcslen(lpNewFileName2);
|
|
wcscpy( &lpNewFileName2[size-4],L"\0");
|
|
|
|
/* Create Folder */
|
|
|
|
/* FIXME
|
|
* remove readonly flag from source folder and do not set the readonly flag to dest folder
|
|
*/
|
|
RemoveReadOnlyAttributeW(lpDeleteFile);
|
|
RemoveReadOnlyAttributeW(lpNewFileName2);
|
|
|
|
CreateDirectoryW(lpNewFileName2,NULL);
|
|
//CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
|
|
|
|
|
|
/* set new search path from src string */
|
|
FindClose(hFile);
|
|
memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
|
|
hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Build the new string */
|
|
size = wcslen(findBuffer.cFileName);
|
|
size2= wcslen(lpExistingFileName2);
|
|
wcscpy( lpDeleteFile,lpExistingFileName2);
|
|
wcscpy( &lpDeleteFile[size2-3],findBuffer.cFileName);
|
|
|
|
/* Build dest string */
|
|
size = wcslen(lpDeleteFile) + wcslen(lpNewFileName);
|
|
size2 = wcslen(lpExistingFileName);
|
|
|
|
if (size>max_size)
|
|
{
|
|
FindClose(hFile);
|
|
|
|
if (lpNewFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
|
|
|
|
if (lpExistingFileName2 != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
|
|
|
|
if (lpDeleteFile != NULL)
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy( lpNewFileName2,lpNewFileName);
|
|
size = wcslen(lpNewFileName);
|
|
wcscpy(&lpNewFileName2[size],&lpDeleteFile[size2]);
|
|
|
|
|
|
/* overrite existsen file, if the file got the flag have readonly
|
|
* we need reomve that flag
|
|
*/
|
|
|
|
/* copy file */
|
|
|
|
TRACE("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile, lpNewFileName2);
|
|
RemoveReadOnlyAttributeW(lpDeleteFile);
|
|
RemoveReadOnlyAttributeW(lpNewFileName2);
|
|
|
|
Result = CopyFileExW (lpDeleteFile,
|
|
lpNewFileName2,
|
|
lpProgressRoutine,
|
|
lpData,
|
|
NULL,
|
|
0);
|
|
|
|
if (Result == FALSE)
|
|
break;
|
|
|
|
/* delete file */
|
|
TRACE("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2);
|
|
Result = RemoveReadOnlyAttributeW(lpDeleteFile);
|
|
if (Result == FALSE)
|
|
break;
|
|
|
|
TRACE("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile);
|
|
Result = DeleteFileW(lpDeleteFile);
|
|
if (Result == FALSE)
|
|
break;
|
|
|
|
}
|
|
loop = FindNextFileW(hFile, &findBuffer);
|
|
}
|
|
|
|
|
|
/* Remove last folder */
|
|
if ((loop == FALSE) && (Result != FALSE))
|
|
{
|
|
DWORD Attributes;
|
|
|
|
Attributes = GetFileAttributesW(lpDeleteFile);
|
|
if (Attributes != INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
SetFileAttributesW(lpDeleteFile,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
|
|
}
|
|
|
|
Result = RemoveDirectoryW(lpExistingFileName);
|
|
}
|
|
|
|
/* Cleanup */
|
|
FindClose(hFile);
|
|
|
|
if (lpNewFileName2 != NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
|
|
lpNewFileName2 = NULL;
|
|
}
|
|
|
|
if (lpExistingFileName2 != NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
|
|
lpExistingFileName2 = NULL;
|
|
}
|
|
|
|
if (lpDeleteFile != NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
|
|
lpDeleteFile = NULL;
|
|
}
|
|
|
|
return Result;
|
|
|
|
// end move folder code
|
|
}
|
|
}
|
|
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
MoveFileWithProgressA (
|
|
LPCSTR lpExistingFileName,
|
|
LPCSTR lpNewFileName,
|
|
LPPROGRESS_ROUTINE lpProgressRoutine,
|
|
LPVOID lpData,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
PWCHAR ExistingFileNameW;
|
|
PWCHAR NewFileNameW;
|
|
BOOL ret;
|
|
|
|
if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE)))
|
|
return FALSE;
|
|
|
|
if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE)))
|
|
return FALSE;
|
|
|
|
ret = MoveFileWithProgressW (ExistingFileNameW ,
|
|
NewFileNameW,
|
|
lpProgressRoutine,
|
|
lpData,
|
|
dwFlags);
|
|
|
|
RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
MoveFileW (
|
|
LPCWSTR lpExistingFileName,
|
|
LPCWSTR lpNewFileName
|
|
)
|
|
{
|
|
return MoveFileExW (lpExistingFileName,
|
|
lpNewFileName,
|
|
MOVEFILE_COPY_ALLOWED);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
MoveFileExW (
|
|
LPCWSTR lpExistingFileName,
|
|
LPCWSTR lpNewFileName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
return MoveFileWithProgressW (lpExistingFileName,
|
|
lpNewFileName,
|
|
NULL,
|
|
NULL,
|
|
dwFlags);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
MoveFileA (
|
|
LPCSTR lpExistingFileName,
|
|
LPCSTR lpNewFileName
|
|
)
|
|
{
|
|
return MoveFileExA (lpExistingFileName,
|
|
lpNewFileName,
|
|
MOVEFILE_COPY_ALLOWED);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
MoveFileExA (
|
|
LPCSTR lpExistingFileName,
|
|
LPCSTR lpNewFileName,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
return MoveFileWithProgressA (lpExistingFileName,
|
|
lpNewFileName,
|
|
NULL,
|
|
NULL,
|
|
dwFlags);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
ReplaceFileA(
|
|
LPCSTR lpReplacedFileName,
|
|
LPCSTR lpReplacementFileName,
|
|
LPCSTR lpBackupFileName,
|
|
DWORD dwReplaceFlags,
|
|
LPVOID lpExclude,
|
|
LPVOID lpReserved
|
|
)
|
|
{
|
|
WCHAR *replacedW, *replacementW, *backupW = NULL;
|
|
BOOL ret;
|
|
|
|
/* This function only makes sense when the first two parameters are defined */
|
|
if (!lpReplacedFileName || !(replacedW = FilenameA2W(lpReplacedFileName, TRUE)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!lpReplacementFileName || !(replacementW = FilenameA2W(lpReplacementFileName, TRUE)))
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, replacedW);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* The backup parameter, however, is optional */
|
|
if (lpBackupFileName)
|
|
{
|
|
if (!(backupW = FilenameA2W(lpBackupFileName, TRUE)))
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, replacedW);
|
|
HeapFree(GetProcessHeap(), 0, replacementW);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ret = ReplaceFileW(replacedW, replacementW, backupW, dwReplaceFlags, lpExclude, lpReserved);
|
|
HeapFree(GetProcessHeap(), 0, replacedW);
|
|
HeapFree(GetProcessHeap(), 0, replacementW);
|
|
HeapFree(GetProcessHeap(), 0, backupW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
ReplaceFileW(
|
|
LPCWSTR lpReplacedFileName,
|
|
LPCWSTR lpReplacementFileName,
|
|
LPCWSTR lpBackupFileName,
|
|
DWORD dwReplaceFlags,
|
|
LPVOID lpExclude,
|
|
LPVOID lpReserved
|
|
)
|
|
{
|
|
HANDLE hReplaced = NULL, hReplacement = NULL;
|
|
UNICODE_STRING NtReplacedName = { 0, 0, NULL };
|
|
UNICODE_STRING NtReplacementName = { 0, 0, NULL };
|
|
DWORD Error = ERROR_SUCCESS;
|
|
NTSTATUS Status;
|
|
BOOL Ret = FALSE;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PVOID Buffer = NULL ;
|
|
|
|
if (dwReplaceFlags)
|
|
FIXME("Ignoring flags %x\n", dwReplaceFlags);
|
|
|
|
/* First two arguments are mandatory */
|
|
if (!lpReplacedFileName || !lpReplacementFileName)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Back it up */
|
|
if(lpBackupFileName)
|
|
{
|
|
if(!CopyFileW(lpReplacedFileName, lpBackupFileName, FALSE))
|
|
{
|
|
Error = GetLastError();
|
|
goto Cleanup ;
|
|
}
|
|
}
|
|
|
|
/* Open the "replaced" file for reading and writing */
|
|
if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &NtReplacedName, NULL, NULL)))
|
|
{
|
|
Error = ERROR_PATH_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&NtReplacedName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile(&hReplaced,
|
|
GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE | WRITE_DAC,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
Error = ERROR_FILE_NOT_FOUND;
|
|
else
|
|
Error = ERROR_UNABLE_TO_REMOVE_REPLACED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Blank it */
|
|
SetEndOfFile(hReplaced) ;
|
|
|
|
/*
|
|
* Open the replacement file for reading, writing, and deleting
|
|
* (deleting is needed when finished)
|
|
*/
|
|
if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &NtReplacementName, NULL, NULL)))
|
|
{
|
|
Error = ERROR_PATH_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&NtReplacementName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile(&hReplacement,
|
|
GENERIC_READ | DELETE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
0,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
Error = RtlNtStatusToDosError(Status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, 0x10000) ;
|
|
if (!Buffer)
|
|
{
|
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup ;
|
|
}
|
|
while (Status != STATUS_END_OF_FILE)
|
|
{
|
|
Status = NtReadFile(hReplacement, NULL, NULL, NULL, &IoStatusBlock, Buffer, 0x10000, NULL, NULL) ;
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = NtWriteFile(hReplaced, NULL, NULL, NULL, &IoStatusBlock, Buffer,
|
|
IoStatusBlock.Information, NULL, NULL) ;
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
Error = RtlNtStatusToDosError(Status);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if (Status != STATUS_END_OF_FILE)
|
|
{
|
|
Error = RtlNtStatusToDosError(Status);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Ret = TRUE;
|
|
|
|
/* Perform resource cleanup */
|
|
Cleanup:
|
|
if (hReplaced) NtClose(hReplaced);
|
|
if (hReplacement) NtClose(hReplacement);
|
|
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
|
|
|
|
if (NtReplacementName.Buffer)
|
|
RtlFreeHeap(GetProcessHeap(), 0, NtReplacementName.Buffer);
|
|
if (NtReplacedName.Buffer)
|
|
RtlFreeHeap(GetProcessHeap(), 0, NtReplacedName.Buffer);
|
|
|
|
/* If there was an error, set the error code */
|
|
if(!Ret)
|
|
{
|
|
TRACE("ReplaceFileW failed (error=%d)\n", Error);
|
|
SetLastError(Error);
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
PrivMoveFileIdentityW(DWORD Unknown1, DWORD Unknown2, DWORD Unknown3)
|
|
{
|
|
STUB;
|
|
return FALSE;
|
|
}
|
|
|
|
/* EOF */
|