/************************************************************************** * ReplaceFileW (KERNEL32.@) * ReplaceFile (KERNEL32.@) */ BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName,LPCWSTR lpReplacementFileName, LPCWSTR lpBackupFileName, DWORD dwReplaceFlags, LPVOID lpExclude, LPVOID lpReserved) { BY_HANDLE_FILE_INFORMATION ifoReplaced, ifoReplacement; HANDLE hReplaced, hReplacement, hBackup; static const int buffer_size = 65536; BOOL skipBackup = FALSE, ret = FALSE; char *buffer; DWORD count; if (dwReplaceFlags) FIXME("Ignoring flags %x\n", dwReplaceFlags); /* First two arguments are mandatory */ if (!lpReplacedFileName || !lpReplacementFileName) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } /* Create a copying buffer */ if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size ))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } /* * 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) { goto replace_fail_1; } /* 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_2; } /* 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) { if ( GetLastError() == ERROR_FILE_NOT_FOUND ) { /* If "replaced" does not exist then create it for the write, but skip backup */ if ((hReplaced = CreateFileW(lpReplacedFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, ifoReplacement.dwFileAttributes, hReplacement)) == INVALID_HANDLE_VALUE) { goto replace_fail_2; } skipBackup = TRUE; } else { /* Inappropriate permissions to remove "replaced" */ SetLastError( ERROR_UNABLE_TO_REMOVE_REPLACED ); goto replace_fail_2; } } /* If the user wants a backup then that needs to be performed first */ if ( lpBackupFileName && !skipBackup ) { /* Obtain the file attributes from the "replaced" file */ if (!GetFileInformationByHandle( hReplaced, &ifoReplaced )) { WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(lpReplacedFileName)); goto replace_fail_3; } /* If an existing backup exists then copy over it */ if ((hBackup = CreateFileW(lpBackupFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, ifoReplaced.dwFileAttributes, hReplaced)) == INVALID_HANDLE_VALUE) { goto replace_fail_3; } /* Actually copy the "replaced" file into the backup file */ while (ReadFile( hReplaced, buffer, buffer_size, &count, NULL ) && count) { char *p = buffer; while (count != 0) { DWORD res; if (!WriteFile( hBackup, p, count, &res, NULL ) || !res) { /* on failure we need to cleanup all our resources */ CloseHandle( hBackup ); goto replace_fail_3; } p += res; count -= res; } } /* If the file was bigger before then end it after the last new write */ SetEndOfFile( hBackup ); /* Set the filetime of the backup to that of the "replaced" file */ SetFileTime( hBackup, NULL, NULL, &ifoReplaced.ftLastWriteTime ); CloseHandle( hBackup ); /* Seek back to the beginning of the file */ SetFilePointer( hReplaced, 0, NULL, FILE_BEGIN ); } /* * Now that the backup has been performed (if requested), copy the replacement * into place */ while (ReadFile( hReplacement, buffer, buffer_size, &count, NULL ) && count) { char *p = buffer; while (count != 0) { DWORD res; if (!WriteFile( hReplaced, p, count, &res, NULL ) || !res) { /* on failure we need to cleanup all our resources */ SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT); goto replace_fail_3; } p += res; count -= res; } } /* If the file was bigger before then end it after the last new write */ SetEndOfFile( hReplaced ); /* Set the filetime of the "replaced" file to that of the replacement */ SetFileTime( hReplaced, NULL, NULL, &ifoReplacement.ftLastWriteTime ); /* * Delete the replacement file, note that this delete won't really occur * until the original handle is released. */ if (!DeleteFileW( lpReplacementFileName )) { /* * This case should never occur, we've already checked permissions earlier * and we are holding the file handle open. */ ERR("Replacement file may not be deleted!\n"); } ret = TRUE; /* Clean up all allocated resources */ replace_fail_3: CloseHandle( hReplaced ); replace_fail_2: CloseHandle( hReplacement ); replace_fail_1: HeapFree( GetProcessHeap(), 0, buffer ); return ret; }