msi: Schedule a rename operation when the file to overwrite is in use.
Hans Leidekker
hans at codeweavers.com
Thu Oct 1 03:07:59 CDT 2009
diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index 8f004b3..46249c2 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -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;
}
diff --git a/dlls/msi/files.c b/dlls/msi/files.c
index 01a80b7..b180284 100644
--- a/dlls/msi/files.c
+++ b/dlls/msi/files.c
@@ -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,31 @@ 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};
+ WCHAR tmpfileW[MAX_PATH], pathW[MAX_PATH], *p;
+
+ TRACE("file in use, scheduling rename operation\n");
+
+ strcpyW(pathW, file->TargetPath);
+ if ((p = strrchrW(pathW, '\\'))) *p = 0;
+ GetTempFileNameW(pathW, msiW, 0, tmpfileW);
+
+ if (CopyFileW(source, tmpfileW, FALSE) &&
+ MoveFileExW(file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) &&
+ MoveFileExW(tmpfileW, 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);
+ }
+ }
return gle;
}
@@ -296,7 +321,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),
diff --git a/dlls/msi/media.c b/dlls/msi/media.c
index a0baa8c..4fe0efa 100644
--- a/dlls/msi/media.c
+++ b/dlls/msi/media.c
@@ -338,15 +338,43 @@ 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};
+ WCHAR tmpfileW[MAX_PATH], tmppathW[MAX_PATH], *p;
+
+ TRACE("file in use, scheduling rename operation\n");
+
+ strcpyW(tmppathW, path);
+ if ((p = strrchrW(tmppathW, '\\'))) *p = 0;
+ GetTempFileNameW(tmppathW, msiW, 0, tmpfileW);
+
+ handle = CreateFileW(tmpfileW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, attrs, NULL);
+
+ if (handle != INVALID_HANDLE_VALUE &&
+ MoveFileExW(path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) &&
+ MoveFileExW(tmpfileW, 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());
}
else
WARN("failed to create %s (error %d)\n", debugstr_w(path), err);
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index 23756fa..71c3b69 100644
--- a/dlls/msi/msipriv.h
+++ b/dlls/msi/msipriv.h
@@ -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
diff --git a/dlls/msi/tests/install.c b/dlls/msi/tests/install.c
index 6a037ef..ab305bf 100644
--- a/dlls/msi/tests/install.c
+++ b/dlls/msi/tests/install.c
@@ -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);
More information about the wine-patches
mailing list