1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2005 Aric Stewart for CodeWeavers 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 /* 22 * Actions dealing with files: 23 * 24 * InstallFiles 25 * DuplicateFiles 26 * MoveFiles 27 * PatchFiles 28 * RemoveDuplicateFiles 29 * RemoveFiles 30 */ 31 32 #include <stdarg.h> 33 34 #define COBJMACROS 35 36 #include "windef.h" 37 #include "winbase.h" 38 #include "winerror.h" 39 #include "fdi.h" 40 #include "msi.h" 41 #include "msidefs.h" 42 #include "msipriv.h" 43 #include "winuser.h" 44 #include "winreg.h" 45 #include "shlwapi.h" 46 #include "patchapi.h" 47 #include "wine/debug.h" 48 49 WINE_DEFAULT_DEBUG_CHANNEL(msi); 50 51 BOOL msi_get_temp_file_name( MSIPACKAGE *package, const WCHAR *tmp_path, const WCHAR *prefix, WCHAR *tmp_filename ) 52 { 53 BOOL ret; 54 msi_disable_fs_redirection( package ); 55 ret = GetTempFileNameW( tmp_path, prefix, 0, tmp_filename ); 56 msi_revert_fs_redirection( package ); 57 return ret; 58 } 59 60 HANDLE msi_create_file( MSIPACKAGE *package, const WCHAR *filename, DWORD access, DWORD sharing, DWORD creation, 61 DWORD flags ) 62 { 63 HANDLE handle; 64 msi_disable_fs_redirection( package ); 65 handle = CreateFileW( filename, access, sharing, NULL, creation, flags, NULL ); 66 msi_revert_fs_redirection( package ); 67 return handle; 68 } 69 70 static BOOL copy_file( MSIPACKAGE *package, const WCHAR *src, const WCHAR *dst, BOOL fail_if_exists ) 71 { 72 BOOL ret; 73 msi_disable_fs_redirection( package ); 74 ret = CopyFileW( src, dst, fail_if_exists ); 75 msi_revert_fs_redirection( package ); 76 return ret; 77 } 78 79 BOOL msi_delete_file( MSIPACKAGE *package, const WCHAR *filename ) 80 { 81 BOOL ret; 82 msi_disable_fs_redirection( package ); 83 ret = DeleteFileW( filename ); 84 msi_revert_fs_redirection( package ); 85 return ret; 86 } 87 88 static BOOL create_directory( MSIPACKAGE *package, const WCHAR *path ) 89 { 90 BOOL ret; 91 msi_disable_fs_redirection( package ); 92 ret = CreateDirectoryW( path, NULL ); 93 msi_revert_fs_redirection( package ); 94 return ret; 95 } 96 97 BOOL msi_remove_directory( MSIPACKAGE *package, const WCHAR *path ) 98 { 99 BOOL ret; 100 msi_disable_fs_redirection( package ); 101 ret = RemoveDirectoryW( path ); 102 msi_revert_fs_redirection( package ); 103 return ret; 104 } 105 106 BOOL msi_set_file_attributes( MSIPACKAGE *package, const WCHAR *filename, DWORD attrs ) 107 { 108 BOOL ret; 109 msi_disable_fs_redirection( package ); 110 ret = SetFileAttributesW( filename, attrs ); 111 msi_revert_fs_redirection( package ); 112 return ret; 113 } 114 115 DWORD msi_get_file_attributes( MSIPACKAGE *package, const WCHAR *path ) 116 { 117 DWORD attrs; 118 msi_disable_fs_redirection( package ); 119 attrs = GetFileAttributesW( path ); 120 msi_revert_fs_redirection( package ); 121 return attrs; 122 } 123 124 HANDLE msi_find_first_file( MSIPACKAGE *package, const WCHAR *filename, WIN32_FIND_DATAW *data ) 125 { 126 HANDLE handle; 127 msi_disable_fs_redirection( package ); 128 handle = FindFirstFileW( filename, data ); 129 msi_revert_fs_redirection( package ); 130 return handle; 131 } 132 133 BOOL msi_find_next_file( MSIPACKAGE *package, HANDLE handle, WIN32_FIND_DATAW *data ) 134 { 135 BOOL ret; 136 msi_disable_fs_redirection( package ); 137 ret = FindNextFileW( handle, data ); 138 msi_revert_fs_redirection( package ); 139 return ret; 140 } 141 142 BOOL msi_move_file( MSIPACKAGE *package, const WCHAR *from, const WCHAR *to, DWORD flags ) 143 { 144 BOOL ret; 145 msi_disable_fs_redirection( package ); 146 ret = MoveFileExW( from, to, flags ); 147 msi_revert_fs_redirection( package ); 148 return ret; 149 } 150 151 static BOOL apply_filepatch( MSIPACKAGE *package, const WCHAR *patch, const WCHAR *old, const WCHAR *new ) 152 { 153 BOOL ret; 154 msi_disable_fs_redirection( package ); 155 ret = ApplyPatchToFileW( patch, old, new, 0 ); 156 msi_revert_fs_redirection( package ); 157 return ret; 158 } 159 160 DWORD msi_get_file_version_info( MSIPACKAGE *package, const WCHAR *path, DWORD buflen, BYTE *buffer ) 161 { 162 DWORD size, handle; 163 msi_disable_fs_redirection( package ); 164 if (buffer) size = GetFileVersionInfoW( path, 0, buflen, buffer ); 165 else size = GetFileVersionInfoSizeW( path, &handle ); 166 msi_revert_fs_redirection( package ); 167 return size; 168 } 169 170 VS_FIXEDFILEINFO *msi_get_disk_file_version( MSIPACKAGE *package, const WCHAR *filename ) 171 { 172 VS_FIXEDFILEINFO *ptr, *ret; 173 DWORD version_size; 174 UINT size; 175 void *version; 176 177 if (!(version_size = msi_get_file_version_info( package, filename, 0, NULL ))) return NULL; 178 if (!(version = malloc( version_size ))) return NULL; 179 180 msi_get_file_version_info( package, filename, version_size, version ); 181 182 if (!VerQueryValueW( version, L"\\", (void **)&ptr, &size )) 183 { 184 free( version ); 185 return NULL; 186 } 187 188 if (!(ret = malloc( size ))) 189 { 190 free( version ); 191 return NULL; 192 } 193 194 memcpy( ret, ptr, size ); 195 free( version ); 196 return ret; 197 } 198 199 DWORD msi_get_disk_file_size( MSIPACKAGE *package, const WCHAR *filename ) 200 { 201 DWORD size; 202 HANDLE file; 203 file = msi_create_file( package, filename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ); 204 if (file == INVALID_HANDLE_VALUE) return INVALID_FILE_SIZE; 205 size = GetFileSize( file, NULL ); 206 CloseHandle( file ); 207 return size; 208 } 209 210 /* Recursively create all directories in the path. */ 211 BOOL msi_create_full_path( MSIPACKAGE *package, const WCHAR *path ) 212 { 213 BOOL ret = TRUE; 214 WCHAR *new_path; 215 int len; 216 217 if (!(new_path = malloc( (wcslen( path ) + 1) * sizeof(WCHAR) ))) return FALSE; 218 lstrcpyW( new_path, path ); 219 220 while ((len = lstrlenW( new_path )) && new_path[len - 1] == '\\') 221 new_path[len - 1] = 0; 222 223 while (!create_directory( package, new_path )) 224 { 225 WCHAR *slash; 226 DWORD last_error = GetLastError(); 227 if (last_error == ERROR_ALREADY_EXISTS) break; 228 if (last_error != ERROR_PATH_NOT_FOUND) 229 { 230 ret = FALSE; 231 break; 232 } 233 if (!(slash = wcsrchr( new_path, '\\' ))) 234 { 235 ret = FALSE; 236 break; 237 } 238 len = slash - new_path; 239 new_path[len] = 0; 240 if (!msi_create_full_path( package, new_path )) 241 { 242 ret = FALSE; 243 break; 244 } 245 new_path[len] = '\\'; 246 } 247 free( new_path ); 248 return ret; 249 } 250 251 static void file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action ) 252 { 253 MSIRECORD *uirow; 254 255 uirow = MSI_CreateRecord( 9 ); 256 MSI_RecordSetStringW( uirow, 1, f->FileName ); 257 MSI_RecordSetStringW( uirow, 9, f->Component->Directory ); 258 MSI_RecordSetInteger( uirow, 6, f->FileSize ); 259 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 260 msiobj_release( &uirow->hdr ); 261 msi_ui_progress( package, 2, f->FileSize, 0, 0 ); 262 } 263 264 static BOOL is_registered_patch_media( MSIPACKAGE *package, UINT disk_id ) 265 { 266 MSIPATCHINFO *patch; 267 268 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry ) 269 { 270 if (patch->disk_id == disk_id && patch->registered) return TRUE; 271 } 272 return FALSE; 273 } 274 275 static BOOL is_obsoleted_by_patch( MSIPACKAGE *package, MSIFILE *file ) 276 { 277 if (!list_empty( &package->patches ) && file->disk_id < MSI_INITIAL_MEDIA_TRANSFORM_DISKID) 278 { 279 if (!msi_get_property_int( package->db, L"Installed", 0 )) return FALSE; 280 return TRUE; 281 } 282 if (is_registered_patch_media( package, file->disk_id )) return TRUE; 283 return FALSE; 284 } 285 286 static BOOL file_hash_matches( MSIPACKAGE *package, MSIFILE *file ) 287 { 288 UINT r; 289 MSIFILEHASHINFO hash; 290 291 hash.dwFileHashInfoSize = sizeof(hash); 292 r = msi_get_filehash( package, file->TargetPath, &hash ); 293 if (r != ERROR_SUCCESS) 294 return FALSE; 295 296 return !memcmp( &hash, &file->hash, sizeof(hash) ); 297 } 298 299 static msi_file_state calculate_install_state( MSIPACKAGE *package, MSIFILE *file ) 300 { 301 MSICOMPONENT *comp = file->Component; 302 VS_FIXEDFILEINFO *file_version; 303 WCHAR *font_version; 304 msi_file_state state; 305 DWORD size; 306 307 comp->Action = msi_get_component_action( package, comp ); 308 if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) 309 { 310 TRACE("skipping %s (not scheduled for install)\n", debugstr_w(file->File)); 311 return msifs_skipped; 312 } 313 if (is_obsoleted_by_patch( package, file )) 314 { 315 TRACE("skipping %s (obsoleted by patch)\n", debugstr_w(file->File)); 316 return msifs_skipped; 317 } 318 if (msi_get_file_attributes( package, file->TargetPath ) == INVALID_FILE_ATTRIBUTES) 319 { 320 TRACE("installing %s (missing)\n", debugstr_w(file->File)); 321 return msifs_missing; 322 } 323 if (file->Version) 324 { 325 if ((file_version = msi_get_disk_file_version( package, file->TargetPath ))) 326 { 327 if (msi_compare_file_versions( file_version, file->Version ) < 0) 328 { 329 TRACE("overwriting %s (new version %s old version %u.%u.%u.%u)\n", 330 debugstr_w(file->File), debugstr_w(file->Version), 331 HIWORD(file_version->dwFileVersionMS), LOWORD(file_version->dwFileVersionMS), 332 HIWORD(file_version->dwFileVersionLS), LOWORD(file_version->dwFileVersionLS)); 333 state = msifs_overwrite; 334 } 335 else 336 { 337 TRACE("keeping %s (new version %s old version %u.%u.%u.%u)\n", 338 debugstr_w(file->File), debugstr_w(file->Version), 339 HIWORD(file_version->dwFileVersionMS), LOWORD(file_version->dwFileVersionMS), 340 HIWORD(file_version->dwFileVersionLS), LOWORD(file_version->dwFileVersionLS)); 341 state = msifs_present; 342 } 343 free( file_version ); 344 return state; 345 } 346 else if ((font_version = msi_get_font_file_version( package, file->TargetPath ))) 347 { 348 if (msi_compare_font_versions( font_version, file->Version ) < 0) 349 { 350 TRACE("overwriting %s (new version %s old version %s)\n", 351 debugstr_w(file->File), debugstr_w(file->Version), debugstr_w(font_version)); 352 state = msifs_overwrite; 353 } 354 else 355 { 356 TRACE("keeping %s (new version %s old version %s)\n", 357 debugstr_w(file->File), debugstr_w(file->Version), debugstr_w(font_version)); 358 state = msifs_present; 359 } 360 free( font_version ); 361 return state; 362 } 363 } 364 if ((size = msi_get_disk_file_size( package, file->TargetPath )) != file->FileSize) 365 { 366 TRACE("overwriting %s (old size %lu new size %d)\n", debugstr_w(file->File), size, file->FileSize); 367 return msifs_overwrite; 368 } 369 if (file->hash.dwFileHashInfoSize) 370 { 371 if (file_hash_matches( package, file )) 372 { 373 TRACE("keeping %s (hash match)\n", debugstr_w(file->File)); 374 return msifs_hashmatch; 375 } 376 else 377 { 378 TRACE("overwriting %s (hash mismatch)\n", debugstr_w(file->File)); 379 return msifs_overwrite; 380 } 381 } 382 /* assume present */ 383 TRACE("keeping %s\n", debugstr_w(file->File)); 384 return msifs_present; 385 } 386 387 static void schedule_install_files(MSIPACKAGE *package) 388 { 389 MSIFILE *file; 390 391 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry) 392 { 393 MSICOMPONENT *comp = file->Component; 394 395 file->state = calculate_install_state( package, file ); 396 if (file->state == msifs_overwrite && (comp->Attributes & msidbComponentAttributesNeverOverwrite)) 397 { 398 TRACE("not overwriting %s\n", debugstr_w(file->TargetPath)); 399 file->state = msifs_skipped; 400 } 401 } 402 } 403 404 static UINT copy_file_attributes( MSIPACKAGE *package, MSIFILE *file, WCHAR *source ) 405 { 406 BOOL ret; 407 408 ret = copy_file( package, source, file->TargetPath, FALSE ); 409 if (!ret) 410 return GetLastError(); 411 412 msi_set_file_attributes( package, file->TargetPath, FILE_ATTRIBUTE_NORMAL ); 413 return ERROR_SUCCESS; 414 } 415 416 static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source) 417 { 418 UINT gle; 419 420 TRACE("Copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath)); 421 422 gle = copy_file_attributes( package, file, source ); 423 if (gle == ERROR_SUCCESS) 424 return gle; 425 426 if (gle == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite) 427 { 428 TRACE("overwriting existing file\n"); 429 return ERROR_SUCCESS; 430 } 431 else if (gle == ERROR_ACCESS_DENIED) 432 { 433 msi_set_file_attributes( package, file->TargetPath, FILE_ATTRIBUTE_NORMAL ); 434 435 gle = copy_file_attributes( package, file, source ); 436 TRACE("Overwriting existing file: %d\n", gle); 437 } 438 if (gle == ERROR_SHARING_VIOLATION || gle == ERROR_USER_MAPPED_FILE) 439 { 440 WCHAR *tmpfileW, *pathW, *p; 441 DWORD len; 442 443 TRACE("file in use, scheduling rename operation\n"); 444 445 if (!(pathW = wcsdup( file->TargetPath ))) return ERROR_OUTOFMEMORY; 446 if ((p = wcsrchr(pathW, '\\'))) *p = 0; 447 len = lstrlenW( pathW ) + 16; 448 if (!(tmpfileW = malloc(len * sizeof(WCHAR)))) 449 { 450 free( pathW ); 451 return ERROR_OUTOFMEMORY; 452 } 453 if (!GetTempFileNameW( pathW, L"msi", 0, tmpfileW )) tmpfileW[0] = 0; 454 free( pathW ); 455 456 if (copy_file( package, source, tmpfileW, FALSE ) && 457 msi_move_file( package, file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT ) && 458 msi_move_file( package, tmpfileW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT )) 459 { 460 package->need_reboot_at_end = 1; 461 gle = ERROR_SUCCESS; 462 } 463 else 464 { 465 gle = GetLastError(); 466 WARN("failed to schedule rename operation: %d)\n", gle); 467 DeleteFileW( tmpfileW ); 468 } 469 free(tmpfileW); 470 } 471 472 return gle; 473 } 474 475 static UINT create_folder( MSIPACKAGE *package, const WCHAR *dir ) 476 { 477 MSIFOLDER *folder; 478 const WCHAR *install_path; 479 480 install_path = msi_get_target_folder( package, dir ); 481 if (!install_path) return ERROR_FUNCTION_FAILED; 482 483 folder = msi_get_loaded_folder( package, dir ); 484 if (folder->State == FOLDER_STATE_UNINITIALIZED) 485 { 486 msi_create_full_path( package, install_path ); 487 folder->State = FOLDER_STATE_CREATED; 488 } 489 return ERROR_SUCCESS; 490 } 491 492 static MSIFILE *find_file( MSIPACKAGE *package, UINT disk_id, const WCHAR *filename ) 493 { 494 MSIFILE *file; 495 496 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) 497 { 498 if (file->disk_id == disk_id && 499 file->state != msifs_installed && 500 !wcsicmp( filename, file->File )) return file; 501 } 502 return NULL; 503 } 504 505 static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR filename, DWORD action, 506 LPWSTR *path, DWORD *attrs, PVOID user) 507 { 508 MSIFILE *file = *(MSIFILE **)user; 509 510 if (action == MSICABEXTRACT_BEGINEXTRACT) 511 { 512 if (!(file = find_file( package, file->disk_id, filename ))) 513 { 514 TRACE("unknown file in cabinet (%s)\n", debugstr_w(filename)); 515 return FALSE; 516 } 517 if (file->state != msifs_missing && file->state != msifs_overwrite) 518 return FALSE; 519 520 if (!msi_is_global_assembly( file->Component )) 521 { 522 create_folder( package, file->Component->Directory ); 523 } 524 *path = wcsdup( file->TargetPath ); 525 *attrs = file->Attributes; 526 *(MSIFILE **)user = file; 527 } 528 else if (action == MSICABEXTRACT_FILEEXTRACTED) 529 { 530 if (!msi_is_global_assembly( file->Component )) file->state = msifs_installed; 531 } 532 533 return TRUE; 534 } 535 536 WCHAR *msi_resolve_file_source( MSIPACKAGE *package, MSIFILE *file ) 537 { 538 WCHAR *p, *path; 539 540 TRACE("Working to resolve source of file %s\n", debugstr_w(file->File)); 541 542 if (file->IsCompressed) return NULL; 543 544 p = msi_resolve_source_folder( package, file->Component->Directory, NULL ); 545 path = msi_build_directory_name( 2, p, file->ShortName ); 546 547 if (file->LongName && msi_get_file_attributes( package, path ) == INVALID_FILE_ATTRIBUTES) 548 { 549 free( path ); 550 path = msi_build_directory_name( 2, p, file->LongName ); 551 } 552 free( p ); 553 TRACE("file %s source resolves to %s\n", debugstr_w(file->File), debugstr_w(path)); 554 return path; 555 } 556 557 /* 558 * ACTION_InstallFiles() 559 * 560 * For efficiency, this is done in two passes: 561 * 1) Correct all the TargetPaths and determine what files are to be installed. 562 * 2) Extract Cabinets and copy files. 563 */ 564 UINT ACTION_InstallFiles(MSIPACKAGE *package) 565 { 566 MSIMEDIAINFO *mi; 567 UINT rc = ERROR_SUCCESS; 568 MSIFILE *file; 569 570 msi_set_sourcedir_props(package, FALSE); 571 572 if (package->script == SCRIPT_NONE) 573 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallFiles"); 574 575 schedule_install_files(package); 576 mi = calloc(1, sizeof(MSIMEDIAINFO)); 577 578 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) 579 { 580 BOOL is_global_assembly = msi_is_global_assembly( file->Component ); 581 582 file_update_ui( package, file, L"InstallFiles" ); 583 584 rc = msi_load_media_info( package, file->Sequence, mi ); 585 if (rc != ERROR_SUCCESS) 586 { 587 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc); 588 rc = ERROR_FUNCTION_FAILED; 589 goto done; 590 } 591 592 if (file->state != msifs_hashmatch && 593 file->state != msifs_skipped && 594 (file->state != msifs_present || !msi_get_property_int( package->db, L"Installed", 0 )) && 595 (rc = ready_media( package, file->IsCompressed, mi ))) 596 { 597 ERR("Failed to ready media for %s\n", debugstr_w(file->File)); 598 goto done; 599 } 600 601 if (file->state != msifs_missing && !mi->is_continuous && file->state != msifs_overwrite) 602 continue; 603 604 if (file->Sequence > mi->last_sequence || mi->is_continuous || 605 (file->IsCompressed && !mi->is_extracted)) 606 { 607 MSICABDATA data; 608 MSIFILE *cursor = file; 609 610 data.mi = mi; 611 data.package = package; 612 data.cb = installfiles_cb; 613 data.user = &cursor; 614 615 if (file->IsCompressed && !msi_cabextract(package, mi, &data)) 616 { 617 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); 618 rc = ERROR_INSTALL_FAILURE; 619 goto done; 620 } 621 } 622 623 if (!file->IsCompressed) 624 { 625 WCHAR *source = msi_resolve_file_source(package, file); 626 627 TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath)); 628 629 if (!is_global_assembly) 630 { 631 create_folder(package, file->Component->Directory); 632 } 633 rc = copy_install_file(package, file, source); 634 if (rc != ERROR_SUCCESS) 635 { 636 ERR("Failed to copy %s to %s (%u)\n", debugstr_w(source), debugstr_w(file->TargetPath), rc); 637 rc = ERROR_INSTALL_FAILURE; 638 free(source); 639 goto done; 640 } 641 if (!is_global_assembly) file->state = msifs_installed; 642 free(source); 643 } 644 else if (!is_global_assembly && file->state != msifs_installed && 645 !(file->Attributes & msidbFileAttributesPatchAdded)) 646 { 647 ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->File)); 648 rc = ERROR_INSTALL_FAILURE; 649 goto done; 650 } 651 } 652 653 done: 654 msi_free_media_info(mi); 655 return rc; 656 } 657 658 static MSIFILEPATCH *find_filepatch( MSIPACKAGE *package, UINT disk_id, const WCHAR *key ) 659 { 660 MSIFILEPATCH *patch; 661 662 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) 663 { 664 if (!patch->extracted && patch->disk_id == disk_id && !wcscmp( key, patch->File->File )) 665 return patch; 666 } 667 return NULL; 668 } 669 670 static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action, 671 LPWSTR *path, DWORD *attrs, PVOID user) 672 { 673 MSIFILEPATCH *patch = *(MSIFILEPATCH **)user; 674 675 if (action == MSICABEXTRACT_BEGINEXTRACT) 676 { 677 MSICOMPONENT *comp; 678 679 if (is_registered_patch_media( package, patch->disk_id ) || 680 !(patch = find_filepatch( package, patch->disk_id, file ))) return FALSE; 681 682 comp = patch->File->Component; 683 comp->Action = msi_get_component_action( package, comp ); 684 if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) 685 { 686 TRACE("file %s component %s not installed or disabled\n", 687 debugstr_w(patch->File->File), debugstr_w(comp->Component)); 688 return FALSE; 689 } 690 691 patch->path = msi_create_temp_file( package->db ); 692 *path = wcsdup( patch->path ); 693 *attrs = patch->File->Attributes; 694 *(MSIFILEPATCH **)user = patch; 695 } 696 else if (action == MSICABEXTRACT_FILEEXTRACTED) 697 { 698 patch->extracted = TRUE; 699 } 700 701 return TRUE; 702 } 703 704 static UINT patch_file( MSIPACKAGE *package, MSIFILEPATCH *patch ) 705 { 706 UINT r = ERROR_SUCCESS; 707 WCHAR *tmpfile = msi_create_temp_file( package->db ); 708 709 if (!tmpfile) return ERROR_INSTALL_FAILURE; 710 if (apply_filepatch( package, patch->path, patch->File->TargetPath, tmpfile )) 711 { 712 msi_delete_file( package, patch->File->TargetPath ); 713 msi_move_file( package, tmpfile, patch->File->TargetPath, 0 ); 714 } 715 else 716 { 717 WARN( "failed to patch %s: %#lx\n", debugstr_w(patch->File->TargetPath), GetLastError() ); 718 r = ERROR_INSTALL_FAILURE; 719 } 720 DeleteFileW( patch->path ); 721 DeleteFileW( tmpfile ); 722 free( tmpfile ); 723 return r; 724 } 725 726 UINT msi_patch_assembly( MSIPACKAGE *package, MSIASSEMBLY *assembly, MSIFILEPATCH *patch ) 727 { 728 UINT r = ERROR_FUNCTION_FAILED; 729 IAssemblyName *name; 730 IAssemblyEnum *iter; 731 732 if (!(iter = msi_create_assembly_enum( package, assembly->display_name ))) 733 return ERROR_FUNCTION_FAILED; 734 735 while ((IAssemblyEnum_GetNextAssembly( iter, NULL, &name, 0 ) == S_OK)) 736 { 737 WCHAR *displayname, *path; 738 DWORD len = 0; 739 HRESULT hr; 740 741 hr = IAssemblyName_GetDisplayName( name, NULL, &len, 0 ); 742 if (hr != E_NOT_SUFFICIENT_BUFFER || !(displayname = malloc( len * sizeof(WCHAR) ))) 743 break; 744 745 hr = IAssemblyName_GetDisplayName( name, displayname, &len, 0 ); 746 if (FAILED( hr )) 747 { 748 free( displayname ); 749 break; 750 } 751 752 if ((path = msi_get_assembly_path( package, displayname ))) 753 { 754 if (!copy_file( package, path, patch->File->TargetPath, FALSE )) 755 { 756 ERR( "failed to copy file %s -> %s (%lu)\n", debugstr_w(path), 757 debugstr_w(patch->File->TargetPath), GetLastError() ); 758 free( path ); 759 free( displayname ); 760 IAssemblyName_Release( name ); 761 break; 762 } 763 r = patch_file( package, patch ); 764 free( path ); 765 } 766 767 free( displayname ); 768 IAssemblyName_Release( name ); 769 if (r == ERROR_SUCCESS) break; 770 } 771 772 IAssemblyEnum_Release( iter ); 773 return r; 774 } 775 776 UINT ACTION_PatchFiles( MSIPACKAGE *package ) 777 { 778 MSIFILEPATCH *patch; 779 MSIMEDIAINFO *mi; 780 UINT rc = ERROR_SUCCESS; 781 782 TRACE("%p\n", package); 783 784 if (package->script == SCRIPT_NONE) 785 return msi_schedule_action(package, SCRIPT_INSTALL, L"PatchFiles"); 786 787 mi = calloc( 1, sizeof(MSIMEDIAINFO) ); 788 789 TRACE("extracting files\n"); 790 791 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) 792 { 793 MSIFILE *file = patch->File; 794 MSICOMPONENT *comp = file->Component; 795 796 rc = msi_load_media_info( package, patch->Sequence, mi ); 797 if (rc != ERROR_SUCCESS) 798 { 799 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc); 800 rc = ERROR_FUNCTION_FAILED; 801 goto done; 802 } 803 comp->Action = msi_get_component_action( package, comp ); 804 if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) continue; 805 806 if (!patch->extracted) 807 { 808 MSICABDATA data; 809 MSIFILEPATCH *cursor = patch; 810 811 rc = ready_media( package, TRUE, mi ); 812 if (rc != ERROR_SUCCESS) 813 { 814 ERR("Failed to ready media for %s\n", debugstr_w(file->File)); 815 goto done; 816 } 817 data.mi = mi; 818 data.package = package; 819 data.cb = patchfiles_cb; 820 data.user = &cursor; 821 822 if (!msi_cabextract( package, mi, &data )) 823 { 824 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet)); 825 rc = ERROR_INSTALL_FAILURE; 826 goto done; 827 } 828 } 829 } 830 831 TRACE("applying patches\n"); 832 833 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry ) 834 { 835 MSICOMPONENT *comp = patch->File->Component; 836 837 if (msi_is_global_assembly( comp ) || !patch->path) continue; 838 839 rc = patch_file( package, patch ); 840 if (rc && !(patch->Attributes & msidbPatchAttributesNonVital)) 841 { 842 ERR("Failed to apply patch to file: %s\n", debugstr_w(patch->File->File)); 843 break; 844 } 845 } 846 847 done: 848 msi_free_media_info(mi); 849 return rc; 850 } 851 852 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0)))) 853 854 struct file_list 855 { 856 struct list entry; 857 LPWSTR sourcename; 858 LPWSTR destname; 859 LPWSTR source; 860 LPWSTR dest; 861 }; 862 863 static BOOL move_file( MSIPACKAGE *package, const WCHAR *source, const WCHAR *dest, int options ) 864 { 865 BOOL ret; 866 867 if (msi_get_file_attributes( package, source ) == FILE_ATTRIBUTE_DIRECTORY || 868 msi_get_file_attributes( package, dest ) == FILE_ATTRIBUTE_DIRECTORY) 869 { 870 WARN("Source or dest is directory, not moving\n"); 871 return FALSE; 872 } 873 874 if (options == msidbMoveFileOptionsMove) 875 { 876 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest)); 877 ret = msi_move_file( package, source, dest, MOVEFILE_REPLACE_EXISTING ); 878 if (!ret) 879 { 880 WARN( "msi_move_file failed: %lu\n", GetLastError() ); 881 return FALSE; 882 } 883 } 884 else 885 { 886 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest)); 887 ret = copy_file( package, source, dest, FALSE ); 888 if (!ret) 889 { 890 WARN( "copy_file failed: %lu\n", GetLastError() ); 891 return FALSE; 892 } 893 } 894 895 return TRUE; 896 } 897 898 static WCHAR *wildcard_to_file( const WCHAR *wildcard, const WCHAR *filename ) 899 { 900 const WCHAR *ptr; 901 WCHAR *path; 902 DWORD dirlen, pathlen; 903 904 ptr = wcsrchr(wildcard, '\\'); 905 dirlen = ptr - wildcard + 1; 906 907 pathlen = dirlen + lstrlenW(filename) + 1; 908 if (!(path = malloc(pathlen * sizeof(WCHAR)))) return NULL; 909 910 lstrcpynW(path, wildcard, dirlen + 1); 911 lstrcatW(path, filename); 912 913 return path; 914 } 915 916 static void free_file_entry(struct file_list *file) 917 { 918 free(file->source); 919 free(file->dest); 920 free(file); 921 } 922 923 static void free_list(struct file_list *list) 924 { 925 while (!list_empty(&list->entry)) 926 { 927 struct file_list *file = LIST_ENTRY(list_head(&list->entry), struct file_list, entry); 928 929 list_remove(&file->entry); 930 free_file_entry(file); 931 } 932 } 933 934 static BOOL add_wildcard( struct file_list *files, const WCHAR *source, WCHAR *dest ) 935 { 936 struct file_list *new, *file; 937 WCHAR *ptr, *filename; 938 DWORD size; 939 940 new = calloc(1, sizeof(*new)); 941 if (!new) 942 return FALSE; 943 944 new->source = wcsdup(source); 945 ptr = wcsrchr(dest, '\\') + 1; 946 filename = wcsrchr(new->source, '\\') + 1; 947 948 new->sourcename = filename; 949 950 if (*ptr) 951 new->destname = ptr; 952 else 953 new->destname = new->sourcename; 954 955 size = (ptr - dest) + lstrlenW(filename) + 1; 956 new->dest = malloc(size * sizeof(WCHAR)); 957 if (!new->dest) 958 { 959 free_file_entry(new); 960 return FALSE; 961 } 962 963 lstrcpynW(new->dest, dest, ptr - dest + 1); 964 lstrcatW(new->dest, filename); 965 966 if (list_empty(&files->entry)) 967 { 968 list_add_head(&files->entry, &new->entry); 969 return TRUE; 970 } 971 972 LIST_FOR_EACH_ENTRY(file, &files->entry, struct file_list, entry) 973 { 974 if (wcscmp( source, file->source ) < 0) 975 { 976 list_add_before(&file->entry, &new->entry); 977 return TRUE; 978 } 979 } 980 981 list_add_after(&file->entry, &new->entry); 982 return TRUE; 983 } 984 985 static BOOL move_files_wildcard( MSIPACKAGE *package, const WCHAR *source, WCHAR *dest, int options ) 986 { 987 WIN32_FIND_DATAW wfd; 988 HANDLE hfile; 989 LPWSTR path; 990 BOOL res; 991 struct file_list files, *file; 992 DWORD size; 993 994 hfile = msi_find_first_file( package, source, &wfd ); 995 if (hfile == INVALID_HANDLE_VALUE) return FALSE; 996 997 list_init(&files.entry); 998 999 for (res = TRUE; res; res = msi_find_next_file( package, hfile, &wfd )) 1000 { 1001 if (is_dot_dir(wfd.cFileName)) continue; 1002 1003 path = wildcard_to_file( source, wfd.cFileName ); 1004 if (!path) 1005 { 1006 res = FALSE; 1007 goto done; 1008 } 1009 1010 add_wildcard(&files, path, dest); 1011 free(path); 1012 } 1013 1014 /* no files match the wildcard */ 1015 if (list_empty(&files.entry)) 1016 goto done; 1017 1018 /* only the first wildcard match gets renamed to dest */ 1019 file = LIST_ENTRY(list_head(&files.entry), struct file_list, entry); 1020 size = (wcsrchr(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2; 1021 file->dest = realloc(file->dest, size * sizeof(WCHAR)); 1022 if (!file->dest) 1023 { 1024 res = FALSE; 1025 goto done; 1026 } 1027 1028 /* file->dest may be shorter after the reallocation, so add a NULL 1029 * terminator. This is needed for the call to wcsrchr, as there will no 1030 * longer be a NULL terminator within the bounds of the allocation in this case. 1031 */ 1032 file->dest[size - 1] = '\0'; 1033 lstrcpyW(wcsrchr(file->dest, '\\') + 1, file->destname); 1034 1035 while (!list_empty(&files.entry)) 1036 { 1037 file = LIST_ENTRY(list_head(&files.entry), struct file_list, entry); 1038 1039 move_file( package, file->source, file->dest, options ); 1040 1041 list_remove(&file->entry); 1042 free_file_entry(file); 1043 } 1044 1045 res = TRUE; 1046 1047 done: 1048 free_list(&files); 1049 FindClose(hfile); 1050 return res; 1051 } 1052 1053 void msi_reduce_to_long_filename( WCHAR *filename ) 1054 { 1055 WCHAR *p = wcschr( filename, '|' ); 1056 if (p) memmove( filename, p + 1, (lstrlenW( p + 1 ) + 1) * sizeof(WCHAR) ); 1057 } 1058 1059 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param ) 1060 { 1061 MSIPACKAGE *package = param; 1062 MSIRECORD *uirow; 1063 MSICOMPONENT *comp; 1064 LPCWSTR sourcename, component; 1065 LPWSTR sourcedir, destname = NULL, destdir = NULL, source = NULL, dest = NULL; 1066 int options; 1067 DWORD size; 1068 BOOL wildcards; 1069 1070 component = MSI_RecordGetString(rec, 2); 1071 comp = msi_get_loaded_component(package, component); 1072 if (!comp) 1073 return ERROR_SUCCESS; 1074 1075 comp->Action = msi_get_component_action( package, comp ); 1076 if (comp->Action != INSTALLSTATE_LOCAL) 1077 { 1078 TRACE("component not scheduled for installation %s\n", debugstr_w(component)); 1079 return ERROR_SUCCESS; 1080 } 1081 1082 sourcename = MSI_RecordGetString(rec, 3); 1083 options = MSI_RecordGetInteger(rec, 7); 1084 1085 sourcedir = msi_dup_property(package->db, MSI_RecordGetString(rec, 5)); 1086 if (!sourcedir) 1087 goto done; 1088 1089 destdir = msi_dup_property(package->db, MSI_RecordGetString(rec, 6)); 1090 if (!destdir) 1091 goto done; 1092 1093 if (!sourcename) 1094 { 1095 if (msi_get_file_attributes( package, sourcedir ) == INVALID_FILE_ATTRIBUTES) 1096 goto done; 1097 1098 source = wcsdup(sourcedir); 1099 if (!source) 1100 goto done; 1101 } 1102 else 1103 { 1104 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2; 1105 source = malloc(size * sizeof(WCHAR)); 1106 if (!source) 1107 goto done; 1108 1109 lstrcpyW(source, sourcedir); 1110 if (source[lstrlenW(source) - 1] != '\\') 1111 lstrcatW(source, L"\\"); 1112 lstrcatW(source, sourcename); 1113 } 1114 1115 wildcards = wcschr(source, '*') || wcschr(source, '?'); 1116 1117 if (MSI_RecordIsNull(rec, 4)) 1118 { 1119 if (!wildcards) 1120 { 1121 WCHAR *p; 1122 if (sourcename) 1123 destname = wcsdup(sourcename); 1124 else if ((p = wcsrchr(sourcedir, '\\'))) 1125 destname = wcsdup(p + 1); 1126 else 1127 destname = wcsdup(sourcedir); 1128 if (!destname) 1129 goto done; 1130 } 1131 } 1132 else 1133 { 1134 destname = wcsdup(MSI_RecordGetString(rec, 4)); 1135 if (destname) msi_reduce_to_long_filename(destname); 1136 } 1137 1138 size = 0; 1139 if (destname) 1140 size = lstrlenW(destname); 1141 1142 size += lstrlenW(destdir) + 2; 1143 dest = malloc(size * sizeof(WCHAR)); 1144 if (!dest) 1145 goto done; 1146 1147 lstrcpyW(dest, destdir); 1148 if (dest[lstrlenW(dest) - 1] != '\\') 1149 lstrcatW(dest, L"\\"); 1150 1151 if (destname) 1152 lstrcatW(dest, destname); 1153 1154 if (msi_get_file_attributes( package, destdir ) == INVALID_FILE_ATTRIBUTES) 1155 { 1156 if (!msi_create_full_path( package, destdir )) 1157 { 1158 WARN( "failed to create directory %lu\n", GetLastError() ); 1159 goto done; 1160 } 1161 } 1162 1163 if (!wildcards) 1164 move_file( package, source, dest, options ); 1165 else 1166 move_files_wildcard( package, source, dest, options ); 1167 1168 done: 1169 uirow = MSI_CreateRecord( 9 ); 1170 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(rec, 1) ); 1171 MSI_RecordSetInteger( uirow, 6, 1 ); /* FIXME */ 1172 MSI_RecordSetStringW( uirow, 9, destdir ); 1173 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 1174 msiobj_release( &uirow->hdr ); 1175 1176 free(sourcedir); 1177 free(destdir); 1178 free(destname); 1179 free(source); 1180 free(dest); 1181 1182 return ERROR_SUCCESS; 1183 } 1184 1185 UINT ACTION_MoveFiles( MSIPACKAGE *package ) 1186 { 1187 MSIQUERY *view; 1188 UINT rc; 1189 1190 if (package->script == SCRIPT_NONE) 1191 return msi_schedule_action(package, SCRIPT_INSTALL, L"MoveFiles"); 1192 1193 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `MoveFile`", &view); 1194 if (rc != ERROR_SUCCESS) 1195 return ERROR_SUCCESS; 1196 1197 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package); 1198 msiobj_release(&view->hdr); 1199 return rc; 1200 } 1201 1202 static WCHAR *get_duplicate_filename( MSIPACKAGE *package, MSIRECORD *row, const WCHAR *file_key, const WCHAR *src ) 1203 { 1204 DWORD len; 1205 WCHAR *dst_name, *dst_path, *dst; 1206 1207 if (MSI_RecordIsNull( row, 4 )) 1208 { 1209 len = lstrlenW( src ) + 1; 1210 if (!(dst_name = malloc( len * sizeof(WCHAR)))) return NULL; 1211 lstrcpyW( dst_name, wcsrchr( src, '\\' ) + 1 ); 1212 } 1213 else 1214 { 1215 MSI_RecordGetStringW( row, 4, NULL, &len ); 1216 if (!(dst_name = malloc( ++len * sizeof(WCHAR) ))) return NULL; 1217 MSI_RecordGetStringW( row, 4, dst_name, &len ); 1218 msi_reduce_to_long_filename( dst_name ); 1219 } 1220 1221 if (MSI_RecordIsNull( row, 5 )) 1222 { 1223 WCHAR *p; 1224 dst_path = wcsdup( src ); 1225 p = wcsrchr( dst_path, '\\' ); 1226 if (p) *p = 0; 1227 } 1228 else 1229 { 1230 const WCHAR *dst_key = MSI_RecordGetString( row, 5 ); 1231 1232 dst_path = wcsdup( msi_get_target_folder( package, dst_key ) ); 1233 if (!dst_path) 1234 { 1235 /* try a property */ 1236 dst_path = msi_dup_property( package->db, dst_key ); 1237 if (!dst_path) 1238 { 1239 FIXME("Unable to get destination folder, try AppSearch properties\n"); 1240 free( dst_name ); 1241 return NULL; 1242 } 1243 } 1244 } 1245 1246 dst = msi_build_directory_name( 2, dst_path, dst_name ); 1247 msi_create_full_path( package, dst_path ); 1248 1249 free( dst_name ); 1250 free( dst_path ); 1251 return dst; 1252 } 1253 1254 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param) 1255 { 1256 MSIPACKAGE *package = param; 1257 LPWSTR dest; 1258 LPCWSTR file_key, component; 1259 MSICOMPONENT *comp; 1260 MSIRECORD *uirow; 1261 MSIFILE *file; 1262 1263 component = MSI_RecordGetString(row,2); 1264 comp = msi_get_loaded_component(package, component); 1265 if (!comp) 1266 return ERROR_SUCCESS; 1267 1268 comp->Action = msi_get_component_action( package, comp ); 1269 if (comp->Action != INSTALLSTATE_LOCAL) 1270 { 1271 TRACE("component not scheduled for installation %s\n", debugstr_w(component)); 1272 return ERROR_SUCCESS; 1273 } 1274 1275 file_key = MSI_RecordGetString(row,3); 1276 if (!file_key) 1277 { 1278 ERR("Unable to get file key\n"); 1279 return ERROR_FUNCTION_FAILED; 1280 } 1281 1282 file = msi_get_loaded_file( package, file_key ); 1283 if (!file) 1284 { 1285 ERR("Original file unknown %s\n", debugstr_w(file_key)); 1286 return ERROR_SUCCESS; 1287 } 1288 1289 dest = get_duplicate_filename( package, row, file_key, file->TargetPath ); 1290 if (!dest) 1291 { 1292 WARN("Unable to get duplicate filename\n"); 1293 return ERROR_SUCCESS; 1294 } 1295 1296 TRACE("Duplicating file %s to %s\n", debugstr_w(file->TargetPath), debugstr_w(dest)); 1297 if (!copy_file( package, file->TargetPath, dest, TRUE )) 1298 { 1299 WARN( "failed to copy file %s -> %s (%lu)\n", 1300 debugstr_w(file->TargetPath), debugstr_w(dest), GetLastError() ); 1301 } 1302 FIXME("We should track these duplicate files as well\n"); 1303 1304 uirow = MSI_CreateRecord( 9 ); 1305 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) ); 1306 MSI_RecordSetInteger( uirow, 6, file->FileSize ); 1307 MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) ); 1308 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 1309 msiobj_release( &uirow->hdr ); 1310 1311 free(dest); 1312 return ERROR_SUCCESS; 1313 } 1314 1315 UINT ACTION_DuplicateFiles(MSIPACKAGE *package) 1316 { 1317 MSIQUERY *view; 1318 UINT rc; 1319 1320 if (package->script == SCRIPT_NONE) 1321 return msi_schedule_action(package, SCRIPT_INSTALL, L"DuplicateFiles"); 1322 1323 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `DuplicateFile`", &view); 1324 if (rc != ERROR_SUCCESS) 1325 return ERROR_SUCCESS; 1326 1327 rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package); 1328 msiobj_release(&view->hdr); 1329 return rc; 1330 } 1331 1332 static UINT ITERATE_RemoveDuplicateFiles( MSIRECORD *row, LPVOID param ) 1333 { 1334 MSIPACKAGE *package = param; 1335 LPWSTR dest; 1336 LPCWSTR file_key, component; 1337 MSICOMPONENT *comp; 1338 MSIRECORD *uirow; 1339 MSIFILE *file; 1340 1341 component = MSI_RecordGetString( row, 2 ); 1342 comp = msi_get_loaded_component( package, component ); 1343 if (!comp) 1344 return ERROR_SUCCESS; 1345 1346 comp->Action = msi_get_component_action( package, comp ); 1347 if (comp->Action != INSTALLSTATE_ABSENT) 1348 { 1349 TRACE("component not scheduled for removal %s\n", debugstr_w(component)); 1350 return ERROR_SUCCESS; 1351 } 1352 1353 file_key = MSI_RecordGetString( row, 3 ); 1354 if (!file_key) 1355 { 1356 ERR("Unable to get file key\n"); 1357 return ERROR_FUNCTION_FAILED; 1358 } 1359 1360 file = msi_get_loaded_file( package, file_key ); 1361 if (!file) 1362 { 1363 ERR("Original file unknown %s\n", debugstr_w(file_key)); 1364 return ERROR_SUCCESS; 1365 } 1366 1367 dest = get_duplicate_filename( package, row, file_key, file->TargetPath ); 1368 if (!dest) 1369 { 1370 WARN("Unable to get duplicate filename\n"); 1371 return ERROR_SUCCESS; 1372 } 1373 1374 TRACE("Removing duplicate %s of %s\n", debugstr_w(dest), debugstr_w(file->TargetPath)); 1375 if (!msi_delete_file( package, dest )) 1376 { 1377 WARN( "failed to delete duplicate file %s (%lu)\n", debugstr_w(dest), GetLastError() ); 1378 } 1379 1380 uirow = MSI_CreateRecord( 9 ); 1381 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) ); 1382 MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) ); 1383 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 1384 msiobj_release( &uirow->hdr ); 1385 1386 free(dest); 1387 return ERROR_SUCCESS; 1388 } 1389 1390 UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package ) 1391 { 1392 MSIQUERY *view; 1393 UINT rc; 1394 1395 if (package->script == SCRIPT_NONE) 1396 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveDuplicateFiles"); 1397 1398 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `DuplicateFile`", &view ); 1399 if (rc != ERROR_SUCCESS) 1400 return ERROR_SUCCESS; 1401 1402 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveDuplicateFiles, package ); 1403 msiobj_release( &view->hdr ); 1404 return rc; 1405 } 1406 1407 static BOOL verify_comp_for_removal(MSICOMPONENT *comp, UINT install_mode) 1408 { 1409 /* special case */ 1410 if (comp->Action != INSTALLSTATE_SOURCE && 1411 comp->Attributes & msidbComponentAttributesSourceOnly && 1412 (install_mode == msidbRemoveFileInstallModeOnRemove || 1413 install_mode == msidbRemoveFileInstallModeOnBoth)) return TRUE; 1414 1415 switch (comp->Action) 1416 { 1417 case INSTALLSTATE_LOCAL: 1418 case INSTALLSTATE_SOURCE: 1419 if (install_mode == msidbRemoveFileInstallModeOnInstall || 1420 install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE; 1421 break; 1422 case INSTALLSTATE_ABSENT: 1423 if (install_mode == msidbRemoveFileInstallModeOnRemove || 1424 install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE; 1425 break; 1426 default: break; 1427 } 1428 return FALSE; 1429 } 1430 1431 static UINT ITERATE_RemoveFiles(MSIRECORD *row, LPVOID param) 1432 { 1433 MSIPACKAGE *package = param; 1434 MSICOMPONENT *comp; 1435 MSIRECORD *uirow; 1436 LPCWSTR component, dirprop; 1437 UINT install_mode; 1438 LPWSTR dir = NULL, path = NULL, filename = NULL; 1439 DWORD size; 1440 UINT ret = ERROR_SUCCESS; 1441 1442 component = MSI_RecordGetString(row, 2); 1443 dirprop = MSI_RecordGetString(row, 4); 1444 install_mode = MSI_RecordGetInteger(row, 5); 1445 1446 comp = msi_get_loaded_component(package, component); 1447 if (!comp) 1448 return ERROR_SUCCESS; 1449 1450 comp->Action = msi_get_component_action( package, comp ); 1451 if (!verify_comp_for_removal(comp, install_mode)) 1452 { 1453 TRACE("Skipping removal due to install mode\n"); 1454 return ERROR_SUCCESS; 1455 } 1456 if (comp->assembly && !comp->assembly->application) 1457 { 1458 return ERROR_SUCCESS; 1459 } 1460 if (comp->Attributes & msidbComponentAttributesPermanent) 1461 { 1462 TRACE("permanent component, not removing file\n"); 1463 return ERROR_SUCCESS; 1464 } 1465 1466 dir = msi_dup_property(package->db, dirprop); 1467 if (!dir) 1468 { 1469 WARN("directory property has no value\n"); 1470 return ERROR_SUCCESS; 1471 } 1472 size = 0; 1473 if ((filename = wcsdup( MSI_RecordGetString(row, 3) ))) 1474 { 1475 msi_reduce_to_long_filename( filename ); 1476 size = lstrlenW( filename ); 1477 } 1478 size += lstrlenW(dir) + 2; 1479 path = malloc(size * sizeof(WCHAR)); 1480 if (!path) 1481 { 1482 ret = ERROR_OUTOFMEMORY; 1483 goto done; 1484 } 1485 1486 if (filename) 1487 { 1488 lstrcpyW(path, dir); 1489 PathAddBackslashW(path); 1490 lstrcatW(path, filename); 1491 1492 TRACE("Deleting misc file: %s\n", debugstr_w(path)); 1493 msi_delete_file( package, path ); 1494 } 1495 else 1496 { 1497 TRACE("Removing misc directory: %s\n", debugstr_w(dir)); 1498 msi_remove_directory( package, dir ); 1499 } 1500 1501 done: 1502 uirow = MSI_CreateRecord( 9 ); 1503 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(row, 1) ); 1504 MSI_RecordSetStringW( uirow, 9, dir ); 1505 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 1506 msiobj_release( &uirow->hdr ); 1507 1508 free(filename); 1509 free(path); 1510 free(dir); 1511 return ret; 1512 } 1513 1514 static void remove_folder( MSIFOLDER *folder ) 1515 { 1516 FolderList *fl; 1517 1518 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry ) 1519 { 1520 remove_folder( fl->folder ); 1521 } 1522 if (!folder->persistent && folder->State != FOLDER_STATE_REMOVED) 1523 { 1524 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED; 1525 } 1526 } 1527 1528 UINT ACTION_RemoveFiles( MSIPACKAGE *package ) 1529 { 1530 MSIQUERY *view; 1531 MSICOMPONENT *comp; 1532 MSIFILE *file; 1533 UINT r; 1534 1535 if (package->script == SCRIPT_NONE) 1536 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveFiles"); 1537 1538 r = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `RemoveFile`", &view); 1539 if (r == ERROR_SUCCESS) 1540 { 1541 r = MSI_IterateRecords(view, NULL, ITERATE_RemoveFiles, package); 1542 msiobj_release(&view->hdr); 1543 if (r != ERROR_SUCCESS) 1544 return r; 1545 } 1546 1547 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) 1548 { 1549 MSIRECORD *uirow; 1550 VS_FIXEDFILEINFO *ver; 1551 1552 comp = file->Component; 1553 file_update_ui( package, file, L"RemoveFiles" ); 1554 1555 comp->Action = msi_get_component_action( package, comp ); 1556 if (comp->Action != INSTALLSTATE_ABSENT || comp->Installed == INSTALLSTATE_SOURCE) 1557 continue; 1558 1559 if (comp->assembly && !comp->assembly->application) 1560 continue; 1561 1562 if (comp->Attributes & msidbComponentAttributesPermanent) 1563 { 1564 TRACE("permanent component, not removing file\n"); 1565 continue; 1566 } 1567 1568 if (file->Version) 1569 { 1570 ver = msi_get_disk_file_version( package, file->TargetPath ); 1571 if (ver && msi_compare_file_versions( ver, file->Version ) > 0) 1572 { 1573 TRACE("newer version detected, not removing file\n"); 1574 free( ver ); 1575 continue; 1576 } 1577 free( ver ); 1578 } 1579 1580 if (file->state == msifs_installed) 1581 WARN("removing installed file %s\n", debugstr_w(file->TargetPath)); 1582 1583 TRACE("removing %s\n", debugstr_w(file->File) ); 1584 1585 msi_set_file_attributes( package, file->TargetPath, FILE_ATTRIBUTE_NORMAL ); 1586 if (!msi_delete_file( package, file->TargetPath )) 1587 { 1588 WARN( "failed to delete %s (%lu)\n", debugstr_w(file->TargetPath), GetLastError() ); 1589 } 1590 file->state = msifs_missing; 1591 1592 uirow = MSI_CreateRecord( 9 ); 1593 MSI_RecordSetStringW( uirow, 1, file->FileName ); 1594 MSI_RecordSetStringW( uirow, 9, comp->Directory ); 1595 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 1596 msiobj_release( &uirow->hdr ); 1597 } 1598 1599 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) 1600 { 1601 comp->Action = msi_get_component_action( package, comp ); 1602 if (comp->Action != INSTALLSTATE_ABSENT) continue; 1603 1604 if (comp->Attributes & msidbComponentAttributesPermanent) 1605 { 1606 TRACE("permanent component, not removing directory\n"); 1607 continue; 1608 } 1609 if (comp->assembly && !comp->assembly->application) 1610 msi_uninstall_assembly( package, comp ); 1611 else 1612 { 1613 MSIFOLDER *folder = msi_get_loaded_folder( package, comp->Directory ); 1614 if (folder) remove_folder( folder ); 1615 } 1616 } 1617 return ERROR_SUCCESS; 1618 } 1619