From 14d8dbf10a32080a86dd9da8891a7727819ab661 Mon Sep 17 00:00:00 2001 From: Erich Hoover Date: Mon, 1 Apr 2002 03:43:04 -0700 Subject: Implement ReplaceFileA/ReplaceFileW (1/2) --- dlls/kernel32/file.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 199 insertions(+), 9 deletions(-) diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c index ed97c51..71a644a 100644 --- a/dlls/kernel32/file.c +++ b/dlls/kernel32/file.c @@ -1517,14 +1517,174 @@ BOOL WINAPI DeleteFileA( LPCSTR path ) * ReplaceFileW (KERNEL32.@) * ReplaceFile (KERNEL32.@) */ -BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName,LPCWSTR lpReplacementFileName, +BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileName, LPCWSTR lpBackupFileName, DWORD dwReplaceFlags, LPVOID lpExclude, LPVOID lpReserved) { - FIXME("(%s,%s,%s,%08x,%p,%p) stub\n",debugstr_w(lpReplacedFileName),debugstr_w(lpReplacementFileName), - debugstr_w(lpBackupFileName),dwReplaceFlags,lpExclude,lpReserved); - SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT); - return FALSE; + UNICODE_STRING ntReplacedFileName, ntReplacementFileName, ntBackupFileName; + ANSI_STRING uReplacedFileName, uReplacementFileName, uBackupFileName; + HANDLE hReplaced = NULL, hReplacement = NULL, hBackup = NULL; + BY_HANDLE_FILE_INFORMATION ifoReplaced, ifoReplacement; + DWORD error = GetLastError(); + UINT replaced_flags; + BOOL ret = FALSE; + NTSTATUS status; + + if (dwReplaceFlags) + FIXME("Ignoring flags %x\n", dwReplaceFlags); + /* First two arguments are mandatory */ + if (!lpReplacedFileName || !lpReplacementFileName) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + /* Convert pathnames and ensure the paths and files exist */ + if (!(RtlDosPathNameToNtPathName_U(lpReplacedFileName, &ntReplacedFileName, NULL, NULL))) + { + error = ERROR_PATH_NOT_FOUND; + goto replace_fail_1; + } + replaced_flags = lpBackupFileName ? FILE_OPEN : FILE_OPEN_IF; + status = wine_nt_to_unix_file_name(&ntReplacedFileName, &uReplacedFileName, replaced_flags, FALSE); + RtlFreeUnicodeString(&ntReplacedFileName); + if (status != STATUS_SUCCESS) + { + error = ERROR_FILE_NOT_FOUND; + goto replace_fail_2; + } + if (!(RtlDosPathNameToNtPathName_U(lpReplacementFileName, &ntReplacementFileName, NULL, NULL))) + { + error = ERROR_PATH_NOT_FOUND; + goto replace_fail_2; + } + status = wine_nt_to_unix_file_name(&ntReplacementFileName, &uReplacementFileName, FILE_OPEN, FALSE); + RtlFreeUnicodeString(&ntReplacementFileName); + if (status != STATUS_SUCCESS) + { + error = ERROR_FILE_NOT_FOUND; + goto replace_fail_3; + } + if (lpBackupFileName) + { + if (!(RtlDosPathNameToNtPathName_U(lpBackupFileName, &ntBackupFileName, NULL, NULL))) + { + error = ERROR_PATH_NOT_FOUND; + goto replace_fail_3; + } + status = wine_nt_to_unix_file_name(&ntBackupFileName, &uBackupFileName, FILE_OPEN_IF, FALSE); + RtlFreeUnicodeString(&ntBackupFileName); + if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE) + { + error = ERROR_FILE_NOT_FOUND; + goto replace_fail_4; + } + } + /* + * Open the replacement file for reading, writing, and deleting + * (writing and deleting are needed when finished) + */ + if ((hReplacement = CreateFileW(lpReplacementFileName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) + { + error = GetLastError(); /* Use error returned by CreateFileW */ + goto replace_fail_4; + } + /* Obtain the file attributes from the replacement file */ + if (!GetFileInformationByHandle( hReplacement, &ifoReplacement )) + { + WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(lpReplacementFileName)); + goto replace_fail_5; + } + /* Open the "replaced" file for reading and writing */ + if ((hReplaced = CreateFileW(lpReplacedFileName, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + ifoReplacement.dwFileAttributes, hReplacement)) == INVALID_HANDLE_VALUE) + { + error = GetLastError(); + if (error == ERROR_FILE_NOT_FOUND) + { + /* If "replaced" does not exist and a backup is specified then FAIL, this + * functionality is not specified in the documentation but was tested and + * found to occur on windows xp. + */ + goto replace_fail_5; + } + else + { + /* Inappropriate permissions to remove "replaced" */ + error = ERROR_UNABLE_TO_REMOVE_REPLACED; + goto replace_fail_5; + } + } + /* If the user wants a backup then that needs to be performed first */ + if (lpBackupFileName) + { + /* Obtain the file attributes from the "replaced" file */ + if (!GetFileInformationByHandle(hReplaced, &ifoReplaced)) + { + WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(lpReplacedFileName)); + error = GetLastError(); + goto replace_fail_6; + } + /* Open the backup with permissions to write over it */ + if ((hBackup = CreateFileW(lpBackupFileName, GENERIC_WRITE, + FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, ifoReplaced.dwFileAttributes, + hReplaced)) == INVALID_HANDLE_VALUE) + { + error = GetLastError(); + goto replace_fail_6; + } + /* If an existing backup exists then copy over it */ + if (rename(uReplacedFileName.Buffer, uBackupFileName.Buffer) == -1) + { + error = ERROR_UNABLE_TO_REMOVE_REPLACED; /* is this correct? */ + goto replace_fail_7; + } + } + /* + * Now that the backup has been performed (if requested), copy the replacement + * into place + */ + if (rename(uReplacementFileName.Buffer, uReplacedFileName.Buffer) == -1) + { + if (errno == EACCES) + { + /* Inappropriate permissions on "replaced", rename will fail */ + error = ERROR_UNABLE_TO_REMOVE_REPLACED; + goto replace_fail_7; + } + /* on failure we need to indicate whether a backup was made */ + if (!lpBackupFileName) + error = ERROR_UNABLE_TO_MOVE_REPLACEMENT; + else + error = ERROR_UNABLE_TO_MOVE_REPLACEMENT_2; + goto replace_fail_7; + } + /* Success! */ + ret = TRUE; + + /* Perform resource cleanup */ +replace_fail_7: + if(hBackup) + CloseHandle(hBackup); +replace_fail_6: + CloseHandle(hReplaced); +replace_fail_5: + CloseHandle(hReplacement); +replace_fail_4: + if(lpBackupFileName) + RtlFreeAnsiString(&uBackupFileName); +replace_fail_3: + RtlFreeAnsiString(&uReplacementFileName); +replace_fail_2: + RtlFreeAnsiString(&uReplacedFileName); +replace_fail_1: + /* If there was an error, set the error code */ + if(!ret) + SetLastError(error); + return ret; } @@ -1535,10 +1695,40 @@ BOOL WINAPI ReplaceFileA(LPCSTR lpReplacedFileName,LPCSTR lpReplacementFileName, LPCSTR lpBackupFileName, DWORD dwReplaceFlags, LPVOID lpExclude, LPVOID lpReserved) { - FIXME("(%s,%s,%s,%08x,%p,%p) stub\n",lpReplacedFileName,lpReplacementFileName, - lpBackupFileName,dwReplaceFlags,lpExclude,lpReserved); - SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT); - return FALSE; + WCHAR *replacedW, *replacementW, *backupW = NULL; + BOOL ret; + + /* This function only makes sense when the first two parameters are defined */ + if (!lpReplacedFileName || !(replacedW = FILE_name_AtoW( lpReplacedFileName, TRUE ))) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if (!lpReplacementFileName || !(replacementW = FILE_name_AtoW( lpReplacementFileName, TRUE ))) + { + HeapFree( GetProcessHeap(), 0, replacedW ); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + /* The backup parameter, however, is optional */ + if (lpBackupFileName) + { + if (!(backupW = FILE_name_AtoW( lpBackupFileName, TRUE ))) + { + HeapFree( GetProcessHeap(), 0, replacedW ); + HeapFree( GetProcessHeap(), 0, replacementW ); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + } + /* Make the actual replacement call */ + ret = ReplaceFileW( replacedW, replacementW, backupW, dwReplaceFlags, lpExclude, lpReserved ); + /* Free all the resources allocated by FILE_name_AtoW */ + HeapFree( GetProcessHeap(), 0, replacedW ); + HeapFree( GetProcessHeap(), 0, replacementW ); + if(lpBackupFileName) + HeapFree( GetProcessHeap(), 0, backupW ); + return ret; } -- 1.4.4.2