msi: Schedule a rename operation when the file to overwrite is in use.
This commit is contained in:
parent
93c949eba5
commit
9c8b83ce01
|
@ -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;
|
||||
char path[MAX_PATH];
|
||||
|
||||
static char key[] = "System\\CurrentControlSet\\Control\\Session Manager";
|
||||
static char value[] = "PendingFileRenameOperations";
|
||||
|
||||
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,44 +6788,17 @@ 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);
|
||||
|
||||
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);
|
||||
|
||||
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++;
|
||||
}
|
||||
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());
|
||||
}
|
||||
HeapFree(GetProcessHeap(), 0, buf);
|
||||
RegDeleteValueA(hkey, value);
|
||||
process_pending_renames(hkey);
|
||||
RegCloseKey(hkey);
|
||||
|
||||
todo_wine ok(file_matches(path), "Expected file to match\n");
|
||||
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\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);
|
||||
|
@ -6767,6 +6806,57 @@ static void test_file_in_use(void)
|
|||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 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();
|
||||
}
|
||||
|
||||
START_TEST(install)
|
||||
{
|
||||
DWORD len;
|
||||
|
@ -6854,6 +6944,7 @@ START_TEST(install)
|
|||
test_preselected();
|
||||
test_installed_prop();
|
||||
test_file_in_use();
|
||||
test_file_in_use_cab();
|
||||
|
||||
DeleteFileA(log_file);
|
||||
|
||||
|
|
Loading…
Reference in New Issue