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