kernel32: Implement ReplaceFileA/ReplaceFileW
Erich Hoover
ehoover at mines.edu
Mon Feb 26 01:50:04 CST 2007
Skipped content of type multipart/alternative-------------- next part --------------
From 455ff9c0c309321b4301b9cbf50e5f2757fce1b5 Mon Sep 17 00:00:00 2001
From: Erich Hoover <ehoover at mediaserver.(none)>
Date: Mon, 26 Feb 2007 00:42:28 -0700
Subject: kernel32: Implement ReplaceFileA/ReplaceFileW
---
dlls/kernel32/file.c | 82 ++++++++++++++--
dlls/kernel32/tests/file.c | 226 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 300 insertions(+), 8 deletions(-)
diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c
index f92d37a..365fb3e 100644
--- a/dlls/kernel32/file.c
+++ b/dlls/kernel32/file.c
@@ -1531,10 +1531,46 @@ BOOL WINAPI ReplaceFileW(LPCWSTR lpRepla
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;
+ DWORD flags = MOVEFILE_COPY_ALLOWED;
+
+ if(dwReplaceFlags)
+ FIXME("Ignoring flags %x\n", dwReplaceFlags);
+ /* Maker sure the "replacement" file exists before continuing */
+ if(!RtlDoesFileExists_U( lpReplacementFileName ))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ /*
+ * If the user wants a backup then that needs to be performed first
+ * * Do not fail to backup if "replaced" does not exist
+ */
+ if( lpBackupFileName && RtlDoesFileExists_U( lpReplacedFileName ) )
+ {
+ /* If an existing backup exists then copy over it */
+ if(!CopyFileW( lpReplacedFileName, lpBackupFileName, FALSE ))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ }
+ /*
+ * Now that the backup has been performed (if requested), copy the replacement
+ * into place (make sure we can delete the original file first)
+ * * Do not fail if "replaced" does not exist
+ */
+ if(RtlDoesFileExists_U( lpReplacedFileName ) && !DeleteFileW( lpReplacedFileName ))
+ {
+ SetLastError(ERROR_UNABLE_TO_REMOVE_REPLACED);
+ return FALSE;
+ }
+ if(!MoveFileExW( lpReplacementFileName, lpReplacedFileName, flags ))
+ {
+ SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT);
+ return FALSE;
+ }
+ /* All done, file replacement successful */
+ return TRUE;
}
@@ -1545,10 +1581,40 @@ BOOL WINAPI ReplaceFileA(LPCSTR lpReplac
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;
}
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index 96fe96e..e6cb238 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -1795,6 +1795,230 @@ static void test_overlapped(void)
ok( r == TRUE, "close handle failed\n");
}
+static void test_ReplaceFileA(void)
+{
+ char replaced[MAX_PATH], replacement[MAX_PATH], backup[MAX_PATH];
+ HANDLE hReplacedFile, hReplacementFile, hBackupFile;
+ static const char replacedData[] = "file-to-replace";
+ static const char replacementData[] = "new-file";
+ static const char backupData[] = "backup-file";
+ FILETIME ftReplaced, ftReplacement, ftBackup;
+ static const char prefix[] = "pfx";
+ char temp_path[MAX_PATH];
+ DWORD ret;
+ BOOL retok;
+
+ ret = GetTempPathA(MAX_PATH, temp_path);
+ ok(ret != 0, "GetTempPathA error %d\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameA(temp_path, prefix, 0, replaced);
+ ok(ret != 0, "GetTempFileNameA error (replaced) %d\n", GetLastError());
+
+ ret = GetTempFileNameA(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameA error (replacement) %d\n", GetLastError());
+
+ ret = GetTempFileNameA(temp_path, prefix, 0, backup);
+ ok(ret != 0, "GetTempFileNameA error (backup) %d\n", GetLastError());
+
+ /* place predictable data in the file to be replaced */
+ hReplacedFile = CreateFileA(replaced, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok(hReplacedFile != INVALID_HANDLE_VALUE,
+ "failed to open replaced file\n");
+ retok = WriteFile(hReplacedFile, replacedData, sizeof(replacedData), &ret, NULL );
+ ok( retok && ret == sizeof(replacedData),
+ "WriteFile error (replaced) %d\n", GetLastError());
+ ok(GetFileSize(hReplacedFile, NULL) == sizeof(replacedData),
+ "replaced file has wrong size\n");
+ /* place predictable data in the file to be the replacement */
+ hReplacementFile = CreateFileA(replacement, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok(hReplacementFile != INVALID_HANDLE_VALUE,
+ "failed to open replacement file\n");
+ retok = WriteFile(hReplacementFile, replacementData, sizeof(replacementData), &ret, NULL );
+ ok( retok && ret == sizeof(replacementData),
+ "WriteFile error (replacement) %d\n", GetLastError());
+ ok(GetFileSize(hReplacementFile, NULL) == sizeof(replacementData),
+ "replacement file has wrong size\n");
+ /* place predictable data in the backup file (to be over-written) */
+ hBackupFile = CreateFileA(backup, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok(hBackupFile != INVALID_HANDLE_VALUE,
+ "failed to open backup file\n");
+ retok = WriteFile(hBackupFile, backupData, sizeof(backupData), &ret, NULL );
+ ok( retok && ret == sizeof(backupData),
+ "WriteFile error (replacement) %d\n", GetLastError());
+ ok(GetFileSize(hBackupFile, NULL) == sizeof(backupData),
+ "backup file has wrong size\n");
+ /* change the filetime on the "replaced" file to ensure that it changes */
+ ret = GetFileTime(hReplacedFile, NULL, NULL, &ftReplaced);
+ ok( ret, "GetFileTime error (replaced) %d\n", GetLastError());
+ ftReplaced.dwLowDateTime -= 600000000; /* 60 second */
+ ret = SetFileTime(hReplacedFile, NULL, NULL, &ftReplaced);
+ ok( ret, "SetFileTime error (replaced) %d\n", GetLastError());
+ GetFileTime(hReplacedFile, NULL, NULL, &ftReplaced); /* get the actual time back */
+ CloseHandle(hReplacedFile);
+ /* change the filetime on the backup to ensure that it changes */
+ ret = GetFileTime(hBackupFile, NULL, NULL, &ftBackup);
+ ok( ret, "GetFileTime error (backup) %d\n", GetLastError());
+ ftBackup.dwLowDateTime -= 1200000000; /* 120 second */
+ ret = SetFileTime(hBackupFile, NULL, NULL, &ftBackup);
+ ok( ret, "SetFileTime error (backup) %d\n", GetLastError());
+ GetFileTime(hBackupFile, NULL, NULL, &ftBackup); /* get the actual time back */
+ CloseHandle(hBackupFile);
+ /* get the filetime on the replacement file to perform checks */
+ ret = GetFileTime(hReplacementFile, NULL, NULL, &ftReplacement);
+ ok( ret, "GetFileTime error (replacement) %d\n", GetLastError());
+ CloseHandle(hReplacementFile);
+
+ /* perform replacement w/ backup
+ * TODO: flags are not implemented
+ */
+ SetLastError(0xdeadbeef);
+ ret = ReplaceFileA(replaced, replacement, backup, 0, 0, 0);
+ ok(ret, "ReplaceFileA: unexpected error %d\n", GetLastError());
+ /* make sure that the backup has the size of the old "replaced" file */
+ hBackupFile = CreateFileA(backup, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ ok(hBackupFile != INVALID_HANDLE_VALUE,
+ "failed to open backup file\n");
+ ret = GetFileSize(hBackupFile, NULL);
+ ok(ret == sizeof(replacedData),
+ "backup file has wrong size %d\n", ret);
+ /* make sure that the "replaced" file has the size of the replacement file */
+ hReplacedFile = CreateFileA(replaced, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ ok(hReplacedFile != INVALID_HANDLE_VALUE,
+ "failed to open replaced file\n");
+ ret = GetFileSize(hReplacedFile, NULL);
+ ok(ret == sizeof(replacementData),
+ "replaced file has wrong size %d\n", ret);
+ /* make sure that the replacement file no-longer exists */
+ hReplacementFile = CreateFileA(replacement, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ ok(hReplacementFile == INVALID_HANDLE_VALUE,
+ "unexpected error, replacement file should not exist %d\n", GetLastError());
+ /* make sure that the backup has the old "replaced" filetime */
+ ret = GetFileTime(hBackupFile, NULL, NULL, &ftBackup);
+ ok( ret, "GetFileTime error (backup %d\n", GetLastError());
+ ok(CompareFileTime(&ftBackup, &ftReplaced) == 0,
+ "backup file has wrong filetime\n");
+ CloseHandle(hBackupFile);
+ /* make sure that the "replaced" has the old replacement filetime */
+ ret = GetFileTime(hReplacedFile, NULL, NULL, &ftReplaced);
+ ok( ret, "GetFileTime error (backup %d\n", GetLastError());
+ ok(CompareFileTime(&ftReplaced, &ftReplacement) == 0,
+ "replaced file has wrong filetime\n");
+ CloseHandle(hReplacedFile);
+
+ /* re-create replacement file for pass w/o backup (blank) */
+ ret = GetTempFileNameA(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameA error (replacement) %d\n", GetLastError());
+ /* perform replacement w/o backup
+ * TODO: flags are not implemented
+ */
+ SetLastError(0xdeadbeef);
+ ret = ReplaceFileA(replaced, replacement, NULL, 0, 0, 0);
+ ok(ret, "ReplaceFileA: unexpected error %d\n", GetLastError());
+
+ /* re-create replacement file for pass w/ backup (backup-file not existing) */
+ ret = GetTempFileNameA(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameA error (replacement) %d\n", GetLastError());
+ ret = DeleteFileA(backup);
+ ok(ret, "DeleteFileA: error (backup) %d\n", GetLastError());
+ /* perform replacement w/ backup (no pre-existing backup)
+ * TODO: flags are not implemented
+ */
+ SetLastError(0xdeadbeef);
+ ret = ReplaceFileA(replaced, replacement, backup, 0, 0, 0);
+ ok(ret, "ReplaceFileA: unexpected error %d\n", GetLastError());
+
+ /* re-create replacement file for pass w/o "replaced" */
+ ret = GetTempFileNameA(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameA error (replacement) %d\n", GetLastError());
+ ret = DeleteFileA(replaced);
+ ok(ret, "DeleteFileA: error (replaced) %d\n", GetLastError());
+ /* perform replacement w/ backup (no pre-existing backup or "replaced")
+ * TODO: flags are not implemented
+ */
+ SetLastError(0xdeadbeef);
+ ret = ReplaceFileA(replaced, replacement, backup, 0, 0, 0);
+ ok(ret, "ReplaceFileA: unexpected error %d\n", GetLastError());
+
+ /* perform replacement w/o existing "replacement" file
+ * TODO: flags are not implemented
+ */
+ SetLastError(0xdeadbeef);
+ ret = ReplaceFileA(replaced, replacement, NULL, 0, 0, 0);
+ ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER,
+ "ReplaceFileA: unexpected error %d\n", GetLastError());
+
+ /*
+ * if the first round (w/ backup) worked then as long as there is no
+ * failure then there is no need to check this round (w/ backup is the
+ * more complete case)
+ */
+
+ /* delete temporary files, replacement is already deleted */
+ ret = DeleteFileA(backup);
+ ok(ret, "DeleteFileA: error (backup) %d\n", GetLastError());
+ ret = DeleteFileA(replaced);
+ ok(ret, "DeleteFileA: error (replaced) %d\n", GetLastError());
+}
+
+/*
+ * ReplaceFileW is a simpler case of ReplaceFileA, there is no
+ * need to be as thorough.
+ */
+static void test_ReplaceFileW(void)
+{
+ WCHAR replaced[MAX_PATH], replacement[MAX_PATH], backup[MAX_PATH];
+ static const WCHAR prefix[] = {'p','f','x',0};
+ WCHAR temp_path[MAX_PATH];
+ DWORD ret;
+
+ ret = GetTempPathW(MAX_PATH, temp_path);
+ if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(ret != 0, "GetTempPathW error %d\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, replaced);
+ ok(ret != 0, "GetTempFileNameW error (replaced) %d\n", GetLastError());
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameW error (replacement) %d\n", GetLastError());
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, backup);
+ ok(ret != 0, "GetTempFileNameW error (backup) %d\n", GetLastError());
+
+ ret = ReplaceFileW(replaced, replacement, backup, 0, 0, 0);
+ ok(ret, "ReplaceFileW: error %d\n", GetLastError());
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameW error (replacement) %d\n", GetLastError());
+ ret = ReplaceFileW(replaced, replacement, NULL, 0, 0, 0);
+ ok(ret, "ReplaceFileW: error %d\n", GetLastError());
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameW error (replacement) %d\n", GetLastError());
+ ret = DeleteFileW(backup);
+ ok(ret, "DeleteFileW: error (backup) %d\n", GetLastError());
+ ret = ReplaceFileW(replaced, replacement, backup, 0, 0, 0);
+ ok(ret, "ReplaceFileW: error %d\n", GetLastError());
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, replacement);
+ ok(ret != 0, "GetTempFileNameW error (replacement) %d\n", GetLastError());
+ ret = DeleteFileW(replaced);
+ ok(ret, "DeleteFileW: error (replaced) %d\n", GetLastError());
+ ret = ReplaceFileW(replaced, replacement, backup, 0, 0, 0);
+ ok(ret, "ReplaceFileW: error %d\n", GetLastError());
+
+ ret = ReplaceFileW(replaced, replacement, NULL, 0, 0, 0);
+ ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER,
+ "ReplaceFileW: unexpected error %d\n", GetLastError());
+
+ ret = DeleteFileW(backup);
+ ok(ret, "DeleteFileW: error %d\n", GetLastError());
+ ret = DeleteFileW(replaced);
+ ok(ret, "DeleteFileW: error %d\n", GetLastError());
+}
+
START_TEST(file)
{
test__hread( );
@@ -1825,4 +2049,6 @@ START_TEST(file)
test_read_write();
test_OpenFile();
test_overlapped();
+ test_ReplaceFileA();
+ test_ReplaceFileW();
}
--
1.4.1
More information about the wine-patches
mailing list