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