diff --git a/dlls/msi/action.c b/dlls/msi/action.c index 30663085e29..89f7bd7b6bc 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -1394,6 +1394,41 @@ static LPWSTR folder_split_path(LPWSTR p, WCHAR ch) return p+1; } +static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','M','s','i','F','i','l','e','H','a','s','h','`',' ', + 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0}; + MSIQUERY *view = NULL; + MSIRECORD *row; + UINT r; + + TRACE("%s\n", debugstr_w(file->File)); + + r = MSI_OpenQuery(package->db, &view, query, file->File); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewExecute(view, NULL); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewFetch(view, &row); + if (r != ERROR_SUCCESS) + goto done; + + file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + file->hash.dwData[0] = MSI_RecordGetInteger(row, 3); + file->hash.dwData[1] = MSI_RecordGetInteger(row, 4); + file->hash.dwData[2] = MSI_RecordGetInteger(row, 5); + file->hash.dwData[3] = MSI_RecordGetInteger(row, 6); + +done: + if (view) msiobj_release(&view->hdr); + return r; +} + static UINT load_file(MSIRECORD *row, LPVOID param) { MSIPACKAGE* package = (MSIPACKAGE*)param; @@ -1444,6 +1479,8 @@ static UINT load_file(MSIRECORD *row, LPVOID param) file->IsCompressed = package->WordCount & MSIWORDCOUNT_COMPRESSED; } + load_file_hash(package, file); + TRACE("File Loaded (%s)\n",debugstr_w(file->File)); list_add_tail( &package->files, &file->entry ); diff --git a/dlls/msi/files.c b/dlls/msi/files.c index c50e4200a4b..6662e4a4c28 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -733,6 +733,22 @@ static UINT copy_install_file(MSIFILE *file) return gle; } +static BOOL check_dest_hash_matches(MSIFILE *file) +{ + MSIFILEHASHINFO hash; + UINT r; + + if (!file->hash.dwFileHashInfoSize) + return FALSE; + + hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO); + r = MsiGetFileHashW(file->TargetPath, 0, &hash); + if (r != ERROR_SUCCESS) + return FALSE; + + return !memcmp(&hash, &file->hash, sizeof(MSIFILEHASHINFO)); +} + /* * ACTION_InstallFiles() * @@ -776,6 +792,12 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) if (file->state != msifs_missing && !mi->is_continuous && file->state != msifs_overwrite) continue; + if (check_dest_hash_matches(file)) + { + TRACE("File hashes match, not overwriting\n"); + continue; + } + if (file->Sequence > mi->last_sequence || mi->is_continuous || (file->IsCompressed && !mi->is_extracted)) { diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 4c7565bcb31..d5412608a65 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -424,6 +424,7 @@ typedef struct tagMSIFILE LPWSTR SourcePath; LPWSTR TargetPath; BOOL IsCompressed; + MSIFILEHASHINFO hash; } MSIFILE; typedef struct tagMSITEMPFILE diff --git a/dlls/msi/tests/install.c b/dlls/msi/tests/install.c index 34fc126f8fe..fd8184be313 100644 --- a/dlls/msi/tests/install.c +++ b/dlls/msi/tests/install.c @@ -3540,10 +3540,7 @@ static void test_missingcab(void) create_pf_data("msitest\\caesar", "abcdefgh", TRUE); r = MsiInstallProductA(msifile, NULL); - todo_wine - { - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - } + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); ok(delete_pf("msitest\\caesar", TRUE), "File not installed\n"); ok(delete_pf("msitest\\maximus", TRUE), "File not installed\n");