Commit 9c8b83ce authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

msi: Schedule a rename operation when the file to overwrite is in use.

parent 93c949eb
......@@ -880,6 +880,9 @@ UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
/* finish up running custom actions */
ACTION_FinishCustomActions(package);
if (rc == ERROR_SUCCESS && package->need_reboot)
return ERROR_SUCCESS_REBOOT_REQUIRED;
return rc;
}
......
......@@ -139,7 +139,7 @@ static UINT copy_file(MSIFILE *file, LPWSTR source)
return ERROR_SUCCESS;
}
static UINT copy_install_file(MSIFILE *file, LPWSTR source)
static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source)
{
UINT gle;
......@@ -153,7 +153,7 @@ static UINT copy_install_file(MSIFILE *file, LPWSTR source)
if (gle == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite)
{
TRACE("overwriting existing file\n");
gle = ERROR_SUCCESS;
return ERROR_SUCCESS;
}
else if (gle == ERROR_ACCESS_DENIED)
{
......@@ -162,6 +162,39 @@ static UINT copy_install_file(MSIFILE *file, LPWSTR source)
gle = copy_file(file, source);
TRACE("Overwriting existing file: %d\n", gle);
}
if (gle == ERROR_SHARING_VIOLATION)
{
static const WCHAR msiW[] = {'m','s','i',0};
static const WCHAR slashW[] = {'\\',0};
WCHAR tmpfileW[MAX_PATH], *pathW, *p;
DWORD len;
TRACE("file in use, scheduling rename operation\n");
GetTempFileNameW(slashW, msiW, 0, tmpfileW);
len = strlenW(file->TargetPath) + strlenW(tmpfileW) + 1;
if (!(pathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
return ERROR_OUTOFMEMORY;
strcpyW(pathW, file->TargetPath);
if ((p = strrchrW(pathW, '\\'))) *p = 0;
strcatW(pathW, tmpfileW);
if (CopyFileW(source, pathW, FALSE) &&
MoveFileExW(file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) &&
MoveFileExW(pathW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT))
{
file->state = msifs_installed;
package->need_reboot = 1;
gle = ERROR_SUCCESS;
}
else
{
gle = GetLastError();
WARN("failed to schedule rename operation: %d)\n", gle);
}
HeapFree(GetProcessHeap(), 0, pathW);
}
return gle;
}
......@@ -296,7 +329,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
debugstr_w(file->TargetPath));
msi_file_update_ui(package, file, szInstallFiles);
rc = copy_install_file(file, source);
rc = copy_install_file(package, file, source);
if (rc != ERROR_SUCCESS)
{
ERR("Failed to copy %s to %s (%d)\n", debugstr_w(source),
......
......@@ -338,15 +338,52 @@ static INT_PTR cabinet_copy_file(FDINOTIFICATIONTYPE fdint,
if (handle == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
DWORD attrs = GetFileAttributesW(path);
DWORD attrs2 = GetFileAttributesW(path);
if (attrs == INVALID_FILE_ATTRIBUTES)
if (attrs2 == INVALID_FILE_ATTRIBUTES)
{
ERR("failed to create %s (error %d)\n", debugstr_w(path), err);
else if (err == ERROR_ACCESS_DENIED && (attrs & FILE_ATTRIBUTE_READONLY))
goto done;
}
else if (err == ERROR_ACCESS_DENIED && (attrs2 & FILE_ATTRIBUTE_READONLY))
{
TRACE("removing read-only attribute on %s\n", debugstr_w(path));
SetFileAttributesW( path, attrs & ~FILE_ATTRIBUTE_READONLY );
handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, attrs, NULL);
SetFileAttributesW( path, attrs2 & ~FILE_ATTRIBUTE_READONLY );
handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, attrs2, NULL);
if (handle != INVALID_HANDLE_VALUE) goto done;
err = GetLastError();
}
if (err == ERROR_SHARING_VIOLATION)
{
static const WCHAR msiW[] = {'m','s','i',0};
static const WCHAR slashW[] = {'\\',0};
WCHAR tmpfileW[MAX_PATH], *tmppathW, *p;
DWORD len;
TRACE("file in use, scheduling rename operation\n");
GetTempFileNameW(slashW, msiW, 0, tmpfileW);
len = strlenW(path) + strlenW(tmpfileW) + 1;
if (!(tmppathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))))
return ERROR_OUTOFMEMORY;
strcpyW(tmppathW, path);
if ((p = strrchrW(tmppathW, '\\'))) *p = 0;
strcatW(tmppathW, tmpfileW);
handle = CreateFileW(tmppathW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, attrs, NULL);
if (handle != INVALID_HANDLE_VALUE &&
MoveFileExW(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) &&
MoveFileExW(tmppathW, path, MOVEFILE_DELAY_UNTIL_REBOOT))
{
data->package->need_reboot = 1;
}
else
WARN("failed to schedule rename operation %s (error %d)\n", debugstr_w(path), GetLastError());
HeapFree(GetProcessHeap(), 0, tmppathW);
}
else
WARN("failed to create %s (error %d)\n", debugstr_w(path), err);
......
......@@ -340,6 +340,7 @@ typedef struct tagMSIPACKAGE
unsigned char scheduled_action_running : 1;
unsigned char commit_action_running : 1;
unsigned char rollback_action_running : 1;
unsigned char need_reboot : 1;
} MSIPACKAGE;
typedef struct tagMSIPREVIEW
......
......@@ -1551,6 +1551,18 @@ static const msi_table fiu_tables[] =
ADD_TABLE(property),
};
static const msi_table fiuc_tables[] =
{
ADD_TABLE(rof_component),
ADD_TABLE(directory),
ADD_TABLE(rof_feature),
ADD_TABLE(rof_feature_comp),
ADD_TABLE(rofc_file),
ADD_TABLE(pp_install_exec_seq),
ADD_TABLE(rofc_media),
ADD_TABLE(property),
};
/* cabinet definitions */
/* make the max size large so there is only one cab file */
......@@ -6689,20 +6701,74 @@ static void test_installed_prop(void)
delete_test_files();
}
static char session_manager[] = "System\\CurrentControlSet\\Control\\Session Manager";
static char rename_ops[] = "PendingFileRenameOperations";
static void process_pending_renames(HKEY hkey)
{
char *buf, *src, *dst;
DWORD size;
LONG ret;
ret = RegQueryValueExA(hkey, rename_ops, NULL, NULL, NULL, &size);
buf = HeapAlloc(GetProcessHeap(), 0, size);
buf[0] = 0;
ret = RegQueryValueExA(hkey, rename_ops, NULL, NULL, (LPBYTE)buf, &size);
ok(!ret, "RegQueryValueExA failed %d (%u)\n", ret, GetLastError());
ok(strstr(buf, "msitest\\maximus") != NULL, "Unexpected value \"%s\"\n", buf);
for (src = buf; *src; src = dst + strlen(dst) + 1)
{
DWORD flags = MOVEFILE_COPY_ALLOWED;
dst = src + strlen(src) + 1;
if (*dst == '!')
{
flags |= MOVEFILE_REPLACE_EXISTING;
dst++;
}
if (src[0] == '\\' && src[1] == '?' && src[2] == '?' && src[3] == '\\') src += 4;
if (*dst)
{
if (dst[0] == '\\' && dst[1] == '?' && dst[2] == '?' && dst[3] == '\\') dst += 4;
ok(MoveFileExA(src, dst, flags), "Failed to move file %s -> %s (%u)\n", src, dst, GetLastError());
}
else
ok(DeleteFileA(src), "Failed to delete file %s (%u)\n", src, GetLastError());
}
HeapFree(GetProcessHeap(), 0, buf);
RegDeleteValueA(hkey, rename_ops);
}
static BOOL file_matches_data(LPCSTR file, LPCSTR data)
{
DWORD len, data_len = strlen(data);
HANDLE handle;
char buf[128];
handle = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
ok(handle != INVALID_HANDLE_VALUE, "failed to open %s (%u)\n", file, GetLastError());
if (ReadFile(handle, buf, sizeof(buf), &len, NULL) && len >= data_len)
{
CloseHandle(handle);
return !memcmp(buf, data, data_len);
}
CloseHandle(handle);
return FALSE;
}
static void test_file_in_use(void)
{
UINT r;
DWORD size;
HANDLE file;
HKEY hkey;
LONG ret;
char path[MAX_PATH], *buf, *src, *dst;
static char key[] = "System\\CurrentControlSet\\Control\\Session Manager";
static char value[] = "PendingFileRenameOperations";
char path[MAX_PATH];
RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_ALL_ACCESS, &hkey);
if (!RegQueryValueExA(hkey, value, NULL, NULL, NULL, &size))
RegOpenKeyExA(HKEY_LOCAL_MACHINE, session_manager, 0, KEY_ALL_ACCESS, &hkey);
if (!RegQueryValueExA(hkey, rename_ops, NULL, NULL, NULL, &size))
{
skip("Pending file rename operations, skipping test\n");
return;
......@@ -6722,48 +6788,72 @@ static void test_file_in_use(void)
file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
r = MsiInstallProductA(msifile, "REBOOT=ReallySuppress FULL=1");
todo_wine ok(r == ERROR_SUCCESS_REBOOT_REQUIRED, "Expected ERROR_SUCCESS_REBOOT_REQUIRED got %u\n", r);
ok(!file_matches(path), "Expected file not to match\n");
ok(r == ERROR_SUCCESS_REBOOT_REQUIRED, "Expected ERROR_SUCCESS_REBOOT_REQUIRED got %u\n", r);
ok(!file_matches_data(path, "msitest\\maximus"), "Expected file not to match\n");
CloseHandle(file);
ok(!file_matches_data(path, "msitest\\maximus"), "Expected file not to match\n");
ret = RegQueryValueExA(hkey, value, NULL, NULL, NULL, &size);
buf = HeapAlloc(GetProcessHeap(), 0, size);
process_pending_renames(hkey);
RegCloseKey(hkey);
buf[0] = 0;
ret = RegQueryValueExA(hkey, value, NULL, NULL, (LPBYTE)buf, &size);
todo_wine ok(!ret, "RegQueryValueExA failed %d (%u)\n", ret, GetLastError());
todo_wine ok(strstr(buf, "msitest\\maximus") != NULL, "Unexpected value \"%s\"\n", buf);
ok(file_matches_data(path, "msitest\\maximus"), "Expected file to match\n");
ok(delete_pf("msitest\\maximus", TRUE), "File not present\n");
ok(delete_pf("msitest", FALSE), "Directory not present or not empty\n");
for (src = buf; *src; src = dst + strlen(dst) + 1)
{
DWORD flags = MOVEFILE_COPY_ALLOWED;
r = MsiInstallProductA(msifile, "REMOVE=ALL");
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
dst = src + strlen(src) + 1;
if (*dst == '!')
{
flags |= MOVEFILE_REPLACE_EXISTING;
dst++;
}
src += strlen("\\??\\");
if (*dst)
{
dst += strlen("\\??\\");
ok(MoveFileExA(src, dst, flags), "Failed to move file %s -> %s (%u)\n", src, dst, GetLastError());
}
else
ok(DeleteFileA(src), "Failed to delete file %s (%u)\n", src, GetLastError());
delete_test_files();
}
static void test_file_in_use_cab(void)
{
UINT r;
DWORD size;
HANDLE file;
HKEY hkey;
char path[MAX_PATH];
RegOpenKeyExA(HKEY_LOCAL_MACHINE, session_manager, 0, KEY_ALL_ACCESS, &hkey);
if (!RegQueryValueExA(hkey, rename_ops, NULL, NULL, NULL, &size))
{
skip("Pending file rename operations, skipping test\n");
return;
}
HeapFree(GetProcessHeap(), 0, buf);
RegDeleteValueA(hkey, value);
CreateDirectoryA("msitest", NULL);
create_file("maximus", 500);
create_cab_file("test1.cab", MEDIA_SIZE, "maximus\0");
DeleteFile("maximus");
create_database(msifile, fiuc_tables, sizeof(fiuc_tables) / sizeof(msi_table));
MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
lstrcpy(path, PROG_FILES_DIR);
lstrcat(path, "\\msitest");
CreateDirectoryA(path, NULL);
lstrcat(path, "\\maximus");
file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
r = MsiInstallProductA(msifile, "REBOOT=ReallySuppress FULL=1");
ok(r == ERROR_SUCCESS_REBOOT_REQUIRED, "Expected ERROR_SUCCESS_REBOOT_REQUIRED got %u\n", r);
ok(!file_matches_data(path, "maximus"), "Expected file not to match\n");
CloseHandle(file);
ok(!file_matches_data(path, "maximus"), "Expected file not to match\n");
process_pending_renames(hkey);
RegCloseKey(hkey);
todo_wine ok(file_matches(path), "Expected file to match\n");
ok(file_matches_data(path, "maximus"), "Expected file to match\n");
ok(delete_pf("msitest\\maximus", TRUE), "File not present\n");
ok(delete_pf("msitest", FALSE), "Directory not present\n");
ok(delete_pf("msitest", FALSE), "Directory not present or not empty\n");
r = MsiInstallProductA(msifile, "REMOVE=ALL");
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
delete_cab_files();
delete_test_files();
}
......@@ -6854,6 +6944,7 @@ START_TEST(install)
test_preselected();
test_installed_prop();
test_file_in_use();
test_file_in_use_cab();
DeleteFileA(log_file);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment