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