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