vbscript: Handle index read access to array properties.
[wine.git] / dlls / msi / files.c
blob94c733fca8fa834b108c5220671801863b80d936
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Aric Stewart for CodeWeavers
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.
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.
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
22 * Actions dealing with files:
24 * InstallFiles
25 * DuplicateFiles
26 * MoveFiles
27 * PatchFiles
28 * RemoveDuplicateFiles
29 * RemoveFiles
32 #include <stdarg.h>
34 #define COBJMACROS
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"
49 WINE_DEFAULT_DEBUG_CHANNEL(msi);
51 BOOL msi_get_temp_file_name( MSIPACKAGE *package, const WCHAR *tmp_path, const WCHAR *prefix, WCHAR *tmp_filename )
53 BOOL ret;
54 msi_disable_fs_redirection( package );
55 ret = GetTempFileNameW( tmp_path, prefix, 0, tmp_filename );
56 msi_revert_fs_redirection( package );
57 return ret;
60 HANDLE msi_create_file( MSIPACKAGE *package, const WCHAR *filename, DWORD access, DWORD sharing, DWORD creation,
61 DWORD flags )
63 HANDLE handle;
64 msi_disable_fs_redirection( package );
65 handle = CreateFileW( filename, access, sharing, NULL, creation, flags, NULL );
66 msi_revert_fs_redirection( package );
67 return handle;
70 static BOOL msi_copy_file( MSIPACKAGE *package, const WCHAR *src, const WCHAR *dst, BOOL fail_if_exists )
72 BOOL ret;
73 msi_disable_fs_redirection( package );
74 ret = CopyFileW( src, dst, fail_if_exists );
75 msi_revert_fs_redirection( package );
76 return ret;
79 BOOL msi_delete_file( MSIPACKAGE *package, const WCHAR *filename )
81 BOOL ret;
82 msi_disable_fs_redirection( package );
83 ret = DeleteFileW( filename );
84 msi_revert_fs_redirection( package );
85 return ret;
88 static BOOL msi_create_directory( MSIPACKAGE *package, const WCHAR *path )
90 BOOL ret;
91 msi_disable_fs_redirection( package );
92 ret = CreateDirectoryW( path, NULL );
93 msi_revert_fs_redirection( package );
94 return ret;
97 BOOL msi_remove_directory( MSIPACKAGE *package, const WCHAR *path )
99 BOOL ret;
100 msi_disable_fs_redirection( package );
101 ret = RemoveDirectoryW( path );
102 msi_revert_fs_redirection( package );
103 return ret;
106 BOOL msi_set_file_attributes( MSIPACKAGE *package, const WCHAR *filename, DWORD attrs )
108 BOOL ret;
109 msi_disable_fs_redirection( package );
110 ret = SetFileAttributesW( filename, attrs );
111 msi_revert_fs_redirection( package );
112 return ret;
115 DWORD msi_get_file_attributes( MSIPACKAGE *package, const WCHAR *path )
117 DWORD attrs;
118 msi_disable_fs_redirection( package );
119 attrs = GetFileAttributesW( path );
120 msi_revert_fs_redirection( package );
121 return attrs;
124 HANDLE msi_find_first_file( MSIPACKAGE *package, const WCHAR *filename, WIN32_FIND_DATAW *data )
126 HANDLE handle;
127 msi_disable_fs_redirection( package );
128 handle = FindFirstFileW( filename, data );
129 msi_revert_fs_redirection( package );
130 return handle;
133 BOOL msi_find_next_file( MSIPACKAGE *package, HANDLE handle, WIN32_FIND_DATAW *data )
135 BOOL ret;
136 msi_disable_fs_redirection( package );
137 ret = FindNextFileW( handle, data );
138 msi_revert_fs_redirection( package );
139 return ret;
142 BOOL msi_move_file( MSIPACKAGE *package, const WCHAR *from, const WCHAR *to, DWORD flags )
144 BOOL ret;
145 msi_disable_fs_redirection( package );
146 ret = MoveFileExW( from, to, flags );
147 msi_revert_fs_redirection( package );
148 return ret;
151 static BOOL msi_apply_filepatch( MSIPACKAGE *package, const WCHAR *patch, const WCHAR *old, const WCHAR *new )
153 BOOL ret;
154 msi_disable_fs_redirection( package );
155 ret = ApplyPatchToFileW( patch, old, new, 0 );
156 msi_revert_fs_redirection( package );
157 return ret;
160 DWORD msi_get_file_version_info( MSIPACKAGE *package, const WCHAR *path, DWORD buflen, BYTE *buffer )
162 DWORD size, handle;
163 msi_disable_fs_redirection( package );
164 if (buffer) size = GetFileVersionInfoW( path, 0, buflen, buffer );
165 else size = GetFileVersionInfoSizeW( path, &handle );
166 msi_revert_fs_redirection( package );
167 return size;
170 VS_FIXEDFILEINFO *msi_get_disk_file_version( MSIPACKAGE *package, const WCHAR *filename )
172 VS_FIXEDFILEINFO *ptr, *ret;
173 DWORD version_size;
174 UINT size;
175 void *version;
177 if (!(version_size = msi_get_file_version_info( package, filename, 0, NULL ))) return NULL;
178 if (!(version = msi_alloc( version_size ))) return NULL;
180 msi_get_file_version_info( package, filename, version_size, version );
182 if (!VerQueryValueW( version, L"\\", (void **)&ptr, &size ))
184 msi_free( version );
185 return NULL;
188 if (!(ret = msi_alloc( size )))
190 msi_free( version );
191 return NULL;
194 memcpy( ret, ptr, size );
195 msi_free( version );
196 return ret;
199 DWORD msi_get_disk_file_size( MSIPACKAGE *package, const WCHAR *filename )
201 DWORD size;
202 HANDLE file;
203 file = msi_create_file( package, filename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 );
204 if (file == INVALID_HANDLE_VALUE) return INVALID_FILE_SIZE;
205 size = GetFileSize( file, NULL );
206 CloseHandle( file );
207 return size;
210 /* Recursively create all directories in the path. */
211 BOOL msi_create_full_path( MSIPACKAGE *package, const WCHAR *path )
213 BOOL ret = TRUE;
214 WCHAR *new_path;
215 int len;
217 if (!(new_path = msi_alloc( (lstrlenW( path ) + 1) * sizeof(WCHAR) ))) return FALSE;
218 lstrcpyW( new_path, path );
220 while ((len = lstrlenW( new_path )) && new_path[len - 1] == '\\')
221 new_path[len - 1] = 0;
223 while (!msi_create_directory( package, new_path ))
225 WCHAR *slash;
226 DWORD last_error = GetLastError();
227 if (last_error == ERROR_ALREADY_EXISTS) break;
228 if (last_error != ERROR_PATH_NOT_FOUND)
230 ret = FALSE;
231 break;
233 if (!(slash = wcsrchr( new_path, '\\' )))
235 ret = FALSE;
236 break;
238 len = slash - new_path;
239 new_path[len] = 0;
240 if (!msi_create_full_path( package, new_path ))
242 ret = FALSE;
243 break;
245 new_path[len] = '\\';
247 msi_free( new_path );
248 return ret;
251 static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action )
253 MSIRECORD *uirow;
255 uirow = MSI_CreateRecord( 9 );
256 MSI_RecordSetStringW( uirow, 1, f->FileName );
257 MSI_RecordSetStringW( uirow, 9, f->Component->Directory );
258 MSI_RecordSetInteger( uirow, 6, f->FileSize );
259 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
260 msiobj_release( &uirow->hdr );
261 msi_ui_progress( package, 2, f->FileSize, 0, 0 );
264 static BOOL is_registered_patch_media( MSIPACKAGE *package, UINT disk_id )
266 MSIPATCHINFO *patch;
268 LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
270 if (patch->disk_id == disk_id && patch->registered) return TRUE;
272 return FALSE;
275 static BOOL is_obsoleted_by_patch( MSIPACKAGE *package, MSIFILE *file )
277 if (!list_empty( &package->patches ) && file->disk_id < MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
279 if (!msi_get_property_int( package->db, L"Installed", 0 )) return FALSE;
280 return TRUE;
282 if (is_registered_patch_media( package, file->disk_id )) return TRUE;
283 return FALSE;
286 static BOOL file_hash_matches( MSIPACKAGE *package, MSIFILE *file )
288 UINT r;
289 MSIFILEHASHINFO hash;
291 hash.dwFileHashInfoSize = sizeof(hash);
292 r = msi_get_filehash( package, file->TargetPath, &hash );
293 if (r != ERROR_SUCCESS)
294 return FALSE;
296 return !memcmp( &hash, &file->hash, sizeof(hash) );
299 static msi_file_state calculate_install_state( MSIPACKAGE *package, MSIFILE *file )
301 MSICOMPONENT *comp = file->Component;
302 VS_FIXEDFILEINFO *file_version;
303 WCHAR *font_version;
304 msi_file_state state;
305 DWORD size;
307 comp->Action = msi_get_component_action( package, comp );
308 if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL || (comp->assembly && comp->assembly->installed))
310 TRACE("skipping %s (not scheduled for install)\n", debugstr_w(file->File));
311 return msifs_skipped;
313 if (is_obsoleted_by_patch( package, file ))
315 TRACE("skipping %s (obsoleted by patch)\n", debugstr_w(file->File));
316 return msifs_skipped;
318 if ((msi_is_global_assembly( comp ) && !comp->assembly->installed) ||
319 msi_get_file_attributes( package, file->TargetPath ) == INVALID_FILE_ATTRIBUTES)
321 TRACE("installing %s (missing)\n", debugstr_w(file->File));
322 return msifs_missing;
324 if (file->Version)
326 if ((file_version = msi_get_disk_file_version( package, file->TargetPath )))
328 if (msi_compare_file_versions( file_version, file->Version ) < 0)
330 TRACE("overwriting %s (new version %s old version %u.%u.%u.%u)\n",
331 debugstr_w(file->File), debugstr_w(file->Version),
332 HIWORD(file_version->dwFileVersionMS), LOWORD(file_version->dwFileVersionMS),
333 HIWORD(file_version->dwFileVersionLS), LOWORD(file_version->dwFileVersionLS));
334 state = msifs_overwrite;
336 else
338 TRACE("keeping %s (new version %s old version %u.%u.%u.%u)\n",
339 debugstr_w(file->File), debugstr_w(file->Version),
340 HIWORD(file_version->dwFileVersionMS), LOWORD(file_version->dwFileVersionMS),
341 HIWORD(file_version->dwFileVersionLS), LOWORD(file_version->dwFileVersionLS));
342 state = msifs_present;
344 msi_free( file_version );
345 return state;
347 else if ((font_version = msi_get_font_file_version( package, file->TargetPath )))
349 if (msi_compare_font_versions( font_version, file->Version ) < 0)
351 TRACE("overwriting %s (new version %s old version %s)\n",
352 debugstr_w(file->File), debugstr_w(file->Version), debugstr_w(font_version));
353 state = msifs_overwrite;
355 else
357 TRACE("keeping %s (new version %s old version %s)\n",
358 debugstr_w(file->File), debugstr_w(file->Version), debugstr_w(font_version));
359 state = msifs_present;
361 msi_free( font_version );
362 return state;
365 if ((size = msi_get_disk_file_size( package, file->TargetPath )) != file->FileSize)
367 TRACE("overwriting %s (old size %lu new size %d)\n", debugstr_w(file->File), size, file->FileSize);
368 return msifs_overwrite;
370 if (file->hash.dwFileHashInfoSize)
372 if (file_hash_matches( package, file ))
374 TRACE("keeping %s (hash match)\n", debugstr_w(file->File));
375 return msifs_hashmatch;
377 else
379 TRACE("overwriting %s (hash mismatch)\n", debugstr_w(file->File));
380 return msifs_overwrite;
383 /* assume present */
384 TRACE("keeping %s\n", debugstr_w(file->File));
385 return msifs_present;
388 static void schedule_install_files(MSIPACKAGE *package)
390 MSIFILE *file;
392 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
394 MSICOMPONENT *comp = file->Component;
396 file->state = calculate_install_state( package, file );
397 if (file->state == msifs_overwrite && (comp->Attributes & msidbComponentAttributesNeverOverwrite))
399 TRACE("not overwriting %s\n", debugstr_w(file->TargetPath));
400 file->state = msifs_skipped;
405 static UINT copy_file( MSIPACKAGE *package, MSIFILE *file, WCHAR *source )
407 BOOL ret;
409 ret = msi_copy_file( package, source, file->TargetPath, FALSE );
410 if (!ret)
411 return GetLastError();
413 msi_set_file_attributes( package, file->TargetPath, FILE_ATTRIBUTE_NORMAL );
414 return ERROR_SUCCESS;
417 static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source)
419 UINT gle;
421 TRACE("Copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath));
423 gle = copy_file( package, file, source );
424 if (gle == ERROR_SUCCESS)
425 return gle;
427 if (gle == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite)
429 TRACE("overwriting existing file\n");
430 return ERROR_SUCCESS;
432 else if (gle == ERROR_ACCESS_DENIED)
434 msi_set_file_attributes( package, file->TargetPath, FILE_ATTRIBUTE_NORMAL );
436 gle = copy_file( package, file, source );
437 TRACE("Overwriting existing file: %d\n", gle);
439 if (gle == ERROR_SHARING_VIOLATION || gle == ERROR_USER_MAPPED_FILE)
441 WCHAR *tmpfileW, *pathW, *p;
442 DWORD len;
444 TRACE("file in use, scheduling rename operation\n");
446 if (!(pathW = strdupW( file->TargetPath ))) return ERROR_OUTOFMEMORY;
447 if ((p = wcsrchr(pathW, '\\'))) *p = 0;
448 len = lstrlenW( pathW ) + 16;
449 if (!(tmpfileW = msi_alloc(len * sizeof(WCHAR))))
451 msi_free( pathW );
452 return ERROR_OUTOFMEMORY;
454 if (!GetTempFileNameW( pathW, L"msi", 0, tmpfileW )) tmpfileW[0] = 0;
455 msi_free( pathW );
457 if (msi_copy_file( package, source, tmpfileW, FALSE ) &&
458 msi_move_file( package, file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT ) &&
459 msi_move_file( package, tmpfileW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT ))
461 package->need_reboot_at_end = 1;
462 gle = ERROR_SUCCESS;
464 else
466 gle = GetLastError();
467 WARN("failed to schedule rename operation: %d)\n", gle);
468 DeleteFileW( tmpfileW );
470 msi_free(tmpfileW);
473 return gle;
476 static UINT create_directory( MSIPACKAGE *package, const WCHAR *dir )
478 MSIFOLDER *folder;
479 const WCHAR *install_path;
481 install_path = msi_get_target_folder( package, dir );
482 if (!install_path) return ERROR_FUNCTION_FAILED;
484 folder = msi_get_loaded_folder( package, dir );
485 if (folder->State == FOLDER_STATE_UNINITIALIZED)
487 msi_create_full_path( package, install_path );
488 folder->State = FOLDER_STATE_CREATED;
490 return ERROR_SUCCESS;
493 static MSIFILE *find_file( MSIPACKAGE *package, UINT disk_id, const WCHAR *filename )
495 MSIFILE *file;
497 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
499 if (file->disk_id == disk_id &&
500 file->state != msifs_installed &&
501 !wcsicmp( filename, file->File )) return file;
503 return NULL;
506 static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR filename, DWORD action,
507 LPWSTR *path, DWORD *attrs, PVOID user)
509 MSIFILE *file = *(MSIFILE **)user;
511 if (action == MSICABEXTRACT_BEGINEXTRACT)
513 if (!(file = find_file( package, file->disk_id, filename )))
515 TRACE("unknown file in cabinet (%s)\n", debugstr_w(filename));
516 return FALSE;
518 if (file->state != msifs_missing && file->state != msifs_overwrite)
519 return FALSE;
521 if (!msi_is_global_assembly( file->Component ))
523 create_directory( package, file->Component->Directory );
525 *path = strdupW( file->TargetPath );
526 *attrs = file->Attributes;
527 *(MSIFILE **)user = file;
529 else if (action == MSICABEXTRACT_FILEEXTRACTED)
531 if (!msi_is_global_assembly( file->Component )) file->state = msifs_installed;
534 return TRUE;
537 WCHAR *msi_resolve_file_source( MSIPACKAGE *package, MSIFILE *file )
539 WCHAR *p, *path;
541 TRACE("Working to resolve source of file %s\n", debugstr_w(file->File));
543 if (file->IsCompressed) return NULL;
545 p = msi_resolve_source_folder( package, file->Component->Directory, NULL );
546 path = msi_build_directory_name( 2, p, file->ShortName );
548 if (file->LongName && msi_get_file_attributes( package, path ) == INVALID_FILE_ATTRIBUTES)
550 msi_free( path );
551 path = msi_build_directory_name( 2, p, file->LongName );
553 msi_free( p );
554 TRACE("file %s source resolves to %s\n", debugstr_w(file->File), debugstr_w(path));
555 return path;
559 * ACTION_InstallFiles()
561 * For efficiency, this is done in two passes:
562 * 1) Correct all the TargetPaths and determine what files are to be installed.
563 * 2) Extract Cabinets and copy files.
565 UINT ACTION_InstallFiles(MSIPACKAGE *package)
567 MSIMEDIAINFO *mi;
568 UINT rc = ERROR_SUCCESS;
569 MSIFILE *file;
571 msi_set_sourcedir_props(package, FALSE);
573 if (package->script == SCRIPT_NONE)
574 return msi_schedule_action(package, SCRIPT_INSTALL, L"InstallFiles");
576 schedule_install_files(package);
577 mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
579 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
581 BOOL is_global_assembly = msi_is_global_assembly( file->Component );
583 msi_file_update_ui( package, file, L"InstallFiles" );
585 rc = msi_load_media_info( package, file->Sequence, mi );
586 if (rc != ERROR_SUCCESS)
588 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc);
589 rc = ERROR_FUNCTION_FAILED;
590 goto done;
593 if (file->state != msifs_hashmatch &&
594 file->state != msifs_skipped &&
595 (file->state != msifs_present || !msi_get_property_int( package->db, L"Installed", 0 )) &&
596 (rc = ready_media( package, file->IsCompressed, mi )))
598 ERR("Failed to ready media for %s\n", debugstr_w(file->File));
599 goto done;
602 if (file->state != msifs_missing && !mi->is_continuous && file->state != msifs_overwrite)
603 continue;
605 if (file->Sequence > mi->last_sequence || mi->is_continuous ||
606 (file->IsCompressed && !mi->is_extracted))
608 MSICABDATA data;
609 MSIFILE *cursor = file;
611 data.mi = mi;
612 data.package = package;
613 data.cb = installfiles_cb;
614 data.user = &cursor;
616 if (file->IsCompressed && !msi_cabextract(package, mi, &data))
618 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
619 rc = ERROR_INSTALL_FAILURE;
620 goto done;
624 if (!file->IsCompressed)
626 WCHAR *source = msi_resolve_file_source(package, file);
628 TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath));
630 if (!is_global_assembly)
632 create_directory(package, file->Component->Directory);
634 rc = copy_install_file(package, file, source);
635 if (rc != ERROR_SUCCESS)
637 ERR("Failed to copy %s to %s (%u)\n", debugstr_w(source), debugstr_w(file->TargetPath), rc);
638 rc = ERROR_INSTALL_FAILURE;
639 msi_free(source);
640 goto done;
642 if (!is_global_assembly) file->state = msifs_installed;
643 msi_free(source);
645 else if (!is_global_assembly && file->state != msifs_installed &&
646 !(file->Attributes & msidbFileAttributesPatchAdded))
648 ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->File));
649 rc = ERROR_INSTALL_FAILURE;
650 goto done;
653 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
655 MSICOMPONENT *comp = file->Component;
657 if (!msi_is_global_assembly( comp ) || comp->assembly->installed ||
658 (file->state != msifs_missing && file->state != msifs_overwrite)) continue;
660 rc = msi_install_assembly( package, comp );
661 if (rc != ERROR_SUCCESS)
663 ERR("Failed to install assembly\n");
664 rc = ERROR_INSTALL_FAILURE;
665 break;
667 file->state = msifs_installed;
670 done:
671 msi_free_media_info(mi);
672 return rc;
675 static MSIFILEPATCH *find_filepatch( MSIPACKAGE *package, UINT disk_id, const WCHAR *key )
677 MSIFILEPATCH *patch;
679 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
681 if (!patch->extracted && patch->disk_id == disk_id && !wcscmp( key, patch->File->File ))
682 return patch;
684 return NULL;
687 static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
688 LPWSTR *path, DWORD *attrs, PVOID user)
690 MSIFILEPATCH *patch = *(MSIFILEPATCH **)user;
692 if (action == MSICABEXTRACT_BEGINEXTRACT)
694 MSICOMPONENT *comp;
696 if (is_registered_patch_media( package, patch->disk_id ) ||
697 !(patch = find_filepatch( package, patch->disk_id, file ))) return FALSE;
699 comp = patch->File->Component;
700 comp->Action = msi_get_component_action( package, comp );
701 if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL)
703 TRACE("file %s component %s not installed or disabled\n",
704 debugstr_w(patch->File->File), debugstr_w(comp->Component));
705 return FALSE;
708 patch->path = msi_create_temp_file( package->db );
709 *path = strdupW( patch->path );
710 *attrs = patch->File->Attributes;
711 *(MSIFILEPATCH **)user = patch;
713 else if (action == MSICABEXTRACT_FILEEXTRACTED)
715 patch->extracted = TRUE;
718 return TRUE;
721 static UINT patch_file( MSIPACKAGE *package, MSIFILEPATCH *patch )
723 UINT r = ERROR_SUCCESS;
724 WCHAR *tmpfile = msi_create_temp_file( package->db );
726 if (!tmpfile) return ERROR_INSTALL_FAILURE;
727 if (msi_apply_filepatch( package, patch->path, patch->File->TargetPath, tmpfile ))
729 msi_delete_file( package, patch->File->TargetPath );
730 msi_move_file( package, tmpfile, patch->File->TargetPath, 0 );
732 else
734 WARN( "failed to patch %s: %#lx\n", debugstr_w(patch->File->TargetPath), GetLastError() );
735 r = ERROR_INSTALL_FAILURE;
737 DeleteFileW( patch->path );
738 DeleteFileW( tmpfile );
739 msi_free( tmpfile );
740 return r;
743 static UINT patch_assembly( MSIPACKAGE *package, MSIASSEMBLY *assembly, MSIFILEPATCH *patch )
745 UINT r = ERROR_FUNCTION_FAILED;
746 IAssemblyName *name;
747 IAssemblyEnum *iter;
749 if (!(iter = msi_create_assembly_enum( package, assembly->display_name )))
750 return ERROR_FUNCTION_FAILED;
752 while ((IAssemblyEnum_GetNextAssembly( iter, NULL, &name, 0 ) == S_OK))
754 WCHAR *displayname, *path;
755 DWORD len = 0;
756 HRESULT hr;
758 hr = IAssemblyName_GetDisplayName( name, NULL, &len, 0 );
759 if (hr != E_NOT_SUFFICIENT_BUFFER || !(displayname = msi_alloc( len * sizeof(WCHAR) )))
760 break;
762 hr = IAssemblyName_GetDisplayName( name, displayname, &len, 0 );
763 if (FAILED( hr ))
765 msi_free( displayname );
766 break;
769 if ((path = msi_get_assembly_path( package, displayname )))
771 if (!msi_copy_file( package, path, patch->File->TargetPath, FALSE ))
773 ERR( "failed to copy file %s -> %s (%lu)\n", debugstr_w(path),
774 debugstr_w(patch->File->TargetPath), GetLastError() );
775 msi_free( path );
776 msi_free( displayname );
777 IAssemblyName_Release( name );
778 break;
780 r = patch_file( package, patch );
781 msi_free( path );
784 msi_free( displayname );
785 IAssemblyName_Release( name );
786 if (r == ERROR_SUCCESS) break;
789 IAssemblyEnum_Release( iter );
790 return r;
793 UINT ACTION_PatchFiles( MSIPACKAGE *package )
795 MSIFILEPATCH *patch;
796 MSIMEDIAINFO *mi;
797 UINT rc = ERROR_SUCCESS;
799 TRACE("%p\n", package);
801 if (package->script == SCRIPT_NONE)
802 return msi_schedule_action(package, SCRIPT_INSTALL, L"PatchFiles");
804 mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
806 TRACE("extracting files\n");
808 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
810 MSIFILE *file = patch->File;
811 MSICOMPONENT *comp = file->Component;
813 rc = msi_load_media_info( package, patch->Sequence, mi );
814 if (rc != ERROR_SUCCESS)
816 ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc);
817 rc = ERROR_FUNCTION_FAILED;
818 goto done;
820 comp->Action = msi_get_component_action( package, comp );
821 if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) continue;
823 if (!patch->extracted)
825 MSICABDATA data;
826 MSIFILEPATCH *cursor = patch;
828 rc = ready_media( package, TRUE, mi );
829 if (rc != ERROR_SUCCESS)
831 ERR("Failed to ready media for %s\n", debugstr_w(file->File));
832 goto done;
834 data.mi = mi;
835 data.package = package;
836 data.cb = patchfiles_cb;
837 data.user = &cursor;
839 if (!msi_cabextract( package, mi, &data ))
841 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
842 rc = ERROR_INSTALL_FAILURE;
843 goto done;
848 TRACE("applying patches\n");
850 LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
852 MSICOMPONENT *comp = patch->File->Component;
854 if (!patch->path) continue;
856 if (msi_is_global_assembly( comp ))
857 rc = patch_assembly( package, comp->assembly, patch );
858 else
859 rc = patch_file( package, patch );
861 if (rc && !(patch->Attributes & msidbPatchAttributesNonVital))
863 ERR("Failed to apply patch to file: %s\n", debugstr_w(patch->File->File));
864 break;
867 if (msi_is_global_assembly( comp ))
869 if ((rc = msi_install_assembly( package, comp )))
871 ERR("Failed to install patched assembly\n");
872 break;
877 done:
878 msi_free_media_info(mi);
879 return rc;
882 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
884 typedef struct
886 struct list entry;
887 LPWSTR sourcename;
888 LPWSTR destname;
889 LPWSTR source;
890 LPWSTR dest;
891 } FILE_LIST;
893 static BOOL move_file( MSIPACKAGE *package, const WCHAR *source, const WCHAR *dest, int options )
895 BOOL ret;
897 if (msi_get_file_attributes( package, source ) == FILE_ATTRIBUTE_DIRECTORY ||
898 msi_get_file_attributes( package, dest ) == FILE_ATTRIBUTE_DIRECTORY)
900 WARN("Source or dest is directory, not moving\n");
901 return FALSE;
904 if (options == msidbMoveFileOptionsMove)
906 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
907 ret = msi_move_file( package, source, dest, MOVEFILE_REPLACE_EXISTING );
908 if (!ret)
910 WARN( "msi_move_file failed: %lu\n", GetLastError() );
911 return FALSE;
914 else
916 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
917 ret = msi_copy_file( package, source, dest, FALSE );
918 if (!ret)
920 WARN( "msi_copy_file failed: %lu\n", GetLastError() );
921 return FALSE;
925 return TRUE;
928 static WCHAR *wildcard_to_file( const WCHAR *wildcard, const WCHAR *filename )
930 const WCHAR *ptr;
931 WCHAR *path;
932 DWORD dirlen, pathlen;
934 ptr = wcsrchr(wildcard, '\\');
935 dirlen = ptr - wildcard + 1;
937 pathlen = dirlen + lstrlenW(filename) + 1;
938 if (!(path = msi_alloc(pathlen * sizeof(WCHAR)))) return NULL;
940 lstrcpynW(path, wildcard, dirlen + 1);
941 lstrcatW(path, filename);
943 return path;
946 static void free_file_entry(FILE_LIST *file)
948 msi_free(file->source);
949 msi_free(file->dest);
950 msi_free(file);
953 static void free_list(FILE_LIST *list)
955 while (!list_empty(&list->entry))
957 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
959 list_remove(&file->entry);
960 free_file_entry(file);
964 static BOOL add_wildcard( FILE_LIST *files, const WCHAR *source, WCHAR *dest )
966 FILE_LIST *new, *file;
967 WCHAR *ptr, *filename;
968 DWORD size;
970 new = msi_alloc_zero(sizeof(FILE_LIST));
971 if (!new)
972 return FALSE;
974 new->source = strdupW(source);
975 ptr = wcsrchr(dest, '\\') + 1;
976 filename = wcsrchr(new->source, '\\') + 1;
978 new->sourcename = filename;
980 if (*ptr)
981 new->destname = ptr;
982 else
983 new->destname = new->sourcename;
985 size = (ptr - dest) + lstrlenW(filename) + 1;
986 new->dest = msi_alloc(size * sizeof(WCHAR));
987 if (!new->dest)
989 free_file_entry(new);
990 return FALSE;
993 lstrcpynW(new->dest, dest, ptr - dest + 1);
994 lstrcatW(new->dest, filename);
996 if (list_empty(&files->entry))
998 list_add_head(&files->entry, &new->entry);
999 return TRUE;
1002 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
1004 if (wcscmp( source, file->source ) < 0)
1006 list_add_before(&file->entry, &new->entry);
1007 return TRUE;
1011 list_add_after(&file->entry, &new->entry);
1012 return TRUE;
1015 static BOOL move_files_wildcard( MSIPACKAGE *package, const WCHAR *source, WCHAR *dest, int options )
1017 WIN32_FIND_DATAW wfd;
1018 HANDLE hfile;
1019 LPWSTR path;
1020 BOOL res;
1021 FILE_LIST files, *file;
1022 DWORD size;
1024 hfile = msi_find_first_file( package, source, &wfd );
1025 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
1027 list_init(&files.entry);
1029 for (res = TRUE; res; res = msi_find_next_file( package, hfile, &wfd ))
1031 if (is_dot_dir(wfd.cFileName)) continue;
1033 path = wildcard_to_file( source, wfd.cFileName );
1034 if (!path)
1036 res = FALSE;
1037 goto done;
1040 add_wildcard(&files, path, dest);
1041 msi_free(path);
1044 /* no files match the wildcard */
1045 if (list_empty(&files.entry))
1046 goto done;
1048 /* only the first wildcard match gets renamed to dest */
1049 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
1050 size = (wcsrchr(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
1051 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
1052 if (!file->dest)
1054 res = FALSE;
1055 goto done;
1058 /* file->dest may be shorter after the reallocation, so add a NULL
1059 * terminator. This is needed for the call to wcsrchr, as there will no
1060 * longer be a NULL terminator within the bounds of the allocation in this case.
1062 file->dest[size - 1] = '\0';
1063 lstrcpyW(wcsrchr(file->dest, '\\') + 1, file->destname);
1065 while (!list_empty(&files.entry))
1067 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
1069 move_file( package, file->source, file->dest, options );
1071 list_remove(&file->entry);
1072 free_file_entry(file);
1075 res = TRUE;
1077 done:
1078 free_list(&files);
1079 FindClose(hfile);
1080 return res;
1083 void msi_reduce_to_long_filename( WCHAR *filename )
1085 WCHAR *p = wcschr( filename, '|' );
1086 if (p) memmove( filename, p + 1, (lstrlenW( p + 1 ) + 1) * sizeof(WCHAR) );
1089 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
1091 MSIPACKAGE *package = param;
1092 MSIRECORD *uirow;
1093 MSICOMPONENT *comp;
1094 LPCWSTR sourcename, component;
1095 LPWSTR sourcedir, destname = NULL, destdir = NULL, source = NULL, dest = NULL;
1096 int options;
1097 DWORD size;
1098 BOOL wildcards;
1100 component = MSI_RecordGetString(rec, 2);
1101 comp = msi_get_loaded_component(package, component);
1102 if (!comp)
1103 return ERROR_SUCCESS;
1105 comp->Action = msi_get_component_action( package, comp );
1106 if (comp->Action != INSTALLSTATE_LOCAL)
1108 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
1109 return ERROR_SUCCESS;
1112 sourcename = MSI_RecordGetString(rec, 3);
1113 options = MSI_RecordGetInteger(rec, 7);
1115 sourcedir = msi_dup_property(package->db, MSI_RecordGetString(rec, 5));
1116 if (!sourcedir)
1117 goto done;
1119 destdir = msi_dup_property(package->db, MSI_RecordGetString(rec, 6));
1120 if (!destdir)
1121 goto done;
1123 if (!sourcename)
1125 if (msi_get_file_attributes( package, sourcedir ) == INVALID_FILE_ATTRIBUTES)
1126 goto done;
1128 source = strdupW(sourcedir);
1129 if (!source)
1130 goto done;
1132 else
1134 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
1135 source = msi_alloc(size * sizeof(WCHAR));
1136 if (!source)
1137 goto done;
1139 lstrcpyW(source, sourcedir);
1140 if (source[lstrlenW(source) - 1] != '\\')
1141 lstrcatW(source, L"\\");
1142 lstrcatW(source, sourcename);
1145 wildcards = wcschr(source, '*') || wcschr(source, '?');
1147 if (MSI_RecordIsNull(rec, 4))
1149 if (!wildcards)
1151 WCHAR *p;
1152 if (sourcename)
1153 destname = strdupW(sourcename);
1154 else if ((p = wcsrchr(sourcedir, '\\')))
1155 destname = strdupW(p + 1);
1156 else
1157 destname = strdupW(sourcedir);
1158 if (!destname)
1159 goto done;
1162 else
1164 destname = strdupW(MSI_RecordGetString(rec, 4));
1165 if (destname) msi_reduce_to_long_filename(destname);
1168 size = 0;
1169 if (destname)
1170 size = lstrlenW(destname);
1172 size += lstrlenW(destdir) + 2;
1173 dest = msi_alloc(size * sizeof(WCHAR));
1174 if (!dest)
1175 goto done;
1177 lstrcpyW(dest, destdir);
1178 if (dest[lstrlenW(dest) - 1] != '\\')
1179 lstrcatW(dest, L"\\");
1181 if (destname)
1182 lstrcatW(dest, destname);
1184 if (msi_get_file_attributes( package, destdir ) == INVALID_FILE_ATTRIBUTES)
1186 if (!msi_create_full_path( package, destdir ))
1188 WARN( "failed to create directory %lu\n", GetLastError() );
1189 goto done;
1193 if (!wildcards)
1194 move_file( package, source, dest, options );
1195 else
1196 move_files_wildcard( package, source, dest, options );
1198 done:
1199 uirow = MSI_CreateRecord( 9 );
1200 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(rec, 1) );
1201 MSI_RecordSetInteger( uirow, 6, 1 ); /* FIXME */
1202 MSI_RecordSetStringW( uirow, 9, destdir );
1203 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1204 msiobj_release( &uirow->hdr );
1206 msi_free(sourcedir);
1207 msi_free(destdir);
1208 msi_free(destname);
1209 msi_free(source);
1210 msi_free(dest);
1212 return ERROR_SUCCESS;
1215 UINT ACTION_MoveFiles( MSIPACKAGE *package )
1217 MSIQUERY *view;
1218 UINT rc;
1220 if (package->script == SCRIPT_NONE)
1221 return msi_schedule_action(package, SCRIPT_INSTALL, L"MoveFiles");
1223 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `MoveFile`", &view);
1224 if (rc != ERROR_SUCCESS)
1225 return ERROR_SUCCESS;
1227 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
1228 msiobj_release(&view->hdr);
1229 return rc;
1232 static WCHAR *get_duplicate_filename( MSIPACKAGE *package, MSIRECORD *row, const WCHAR *file_key, const WCHAR *src )
1234 DWORD len;
1235 WCHAR *dst_name, *dst_path, *dst;
1237 if (MSI_RecordIsNull( row, 4 ))
1239 len = lstrlenW( src ) + 1;
1240 if (!(dst_name = msi_alloc( len * sizeof(WCHAR)))) return NULL;
1241 lstrcpyW( dst_name, wcsrchr( src, '\\' ) + 1 );
1243 else
1245 MSI_RecordGetStringW( row, 4, NULL, &len );
1246 if (!(dst_name = msi_alloc( ++len * sizeof(WCHAR) ))) return NULL;
1247 MSI_RecordGetStringW( row, 4, dst_name, &len );
1248 msi_reduce_to_long_filename( dst_name );
1251 if (MSI_RecordIsNull( row, 5 ))
1253 WCHAR *p;
1254 dst_path = strdupW( src );
1255 p = wcsrchr( dst_path, '\\' );
1256 if (p) *p = 0;
1258 else
1260 const WCHAR *dst_key = MSI_RecordGetString( row, 5 );
1262 dst_path = strdupW( msi_get_target_folder( package, dst_key ) );
1263 if (!dst_path)
1265 /* try a property */
1266 dst_path = msi_dup_property( package->db, dst_key );
1267 if (!dst_path)
1269 FIXME("Unable to get destination folder, try AppSearch properties\n");
1270 msi_free( dst_name );
1271 return NULL;
1276 dst = msi_build_directory_name( 2, dst_path, dst_name );
1277 msi_create_full_path( package, dst_path );
1279 msi_free( dst_name );
1280 msi_free( dst_path );
1281 return dst;
1284 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
1286 MSIPACKAGE *package = param;
1287 LPWSTR dest;
1288 LPCWSTR file_key, component;
1289 MSICOMPONENT *comp;
1290 MSIRECORD *uirow;
1291 MSIFILE *file;
1293 component = MSI_RecordGetString(row,2);
1294 comp = msi_get_loaded_component(package, component);
1295 if (!comp)
1296 return ERROR_SUCCESS;
1298 comp->Action = msi_get_component_action( package, comp );
1299 if (comp->Action != INSTALLSTATE_LOCAL)
1301 TRACE("component not scheduled for installation %s\n", debugstr_w(component));
1302 return ERROR_SUCCESS;
1305 file_key = MSI_RecordGetString(row,3);
1306 if (!file_key)
1308 ERR("Unable to get file key\n");
1309 return ERROR_FUNCTION_FAILED;
1312 file = msi_get_loaded_file( package, file_key );
1313 if (!file)
1315 ERR("Original file unknown %s\n", debugstr_w(file_key));
1316 return ERROR_SUCCESS;
1319 dest = get_duplicate_filename( package, row, file_key, file->TargetPath );
1320 if (!dest)
1322 WARN("Unable to get duplicate filename\n");
1323 return ERROR_SUCCESS;
1326 TRACE("Duplicating file %s to %s\n", debugstr_w(file->TargetPath), debugstr_w(dest));
1327 if (!msi_copy_file( package, file->TargetPath, dest, TRUE ))
1329 WARN( "failed to copy file %s -> %s (%lu)\n",
1330 debugstr_w(file->TargetPath), debugstr_w(dest), GetLastError() );
1332 FIXME("We should track these duplicate files as well\n");
1334 uirow = MSI_CreateRecord( 9 );
1335 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) );
1336 MSI_RecordSetInteger( uirow, 6, file->FileSize );
1337 MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) );
1338 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1339 msiobj_release( &uirow->hdr );
1341 msi_free(dest);
1342 return ERROR_SUCCESS;
1345 UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
1347 MSIQUERY *view;
1348 UINT rc;
1350 if (package->script == SCRIPT_NONE)
1351 return msi_schedule_action(package, SCRIPT_INSTALL, L"DuplicateFiles");
1353 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `DuplicateFile`", &view);
1354 if (rc != ERROR_SUCCESS)
1355 return ERROR_SUCCESS;
1357 rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
1358 msiobj_release(&view->hdr);
1359 return rc;
1362 static UINT ITERATE_RemoveDuplicateFiles( MSIRECORD *row, LPVOID param )
1364 MSIPACKAGE *package = param;
1365 LPWSTR dest;
1366 LPCWSTR file_key, component;
1367 MSICOMPONENT *comp;
1368 MSIRECORD *uirow;
1369 MSIFILE *file;
1371 component = MSI_RecordGetString( row, 2 );
1372 comp = msi_get_loaded_component( package, component );
1373 if (!comp)
1374 return ERROR_SUCCESS;
1376 comp->Action = msi_get_component_action( package, comp );
1377 if (comp->Action != INSTALLSTATE_ABSENT)
1379 TRACE("component not scheduled for removal %s\n", debugstr_w(component));
1380 return ERROR_SUCCESS;
1383 file_key = MSI_RecordGetString( row, 3 );
1384 if (!file_key)
1386 ERR("Unable to get file key\n");
1387 return ERROR_FUNCTION_FAILED;
1390 file = msi_get_loaded_file( package, file_key );
1391 if (!file)
1393 ERR("Original file unknown %s\n", debugstr_w(file_key));
1394 return ERROR_SUCCESS;
1397 dest = get_duplicate_filename( package, row, file_key, file->TargetPath );
1398 if (!dest)
1400 WARN("Unable to get duplicate filename\n");
1401 return ERROR_SUCCESS;
1404 TRACE("Removing duplicate %s of %s\n", debugstr_w(dest), debugstr_w(file->TargetPath));
1405 if (!msi_delete_file( package, dest ))
1407 WARN( "failed to delete duplicate file %s (%lu)\n", debugstr_w(dest), GetLastError() );
1410 uirow = MSI_CreateRecord( 9 );
1411 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) );
1412 MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) );
1413 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1414 msiobj_release( &uirow->hdr );
1416 msi_free(dest);
1417 return ERROR_SUCCESS;
1420 UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
1422 MSIQUERY *view;
1423 UINT rc;
1425 if (package->script == SCRIPT_NONE)
1426 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveDuplicateFiles");
1428 rc = MSI_DatabaseOpenViewW( package->db, L"SELECT * FROM `DuplicateFile`", &view );
1429 if (rc != ERROR_SUCCESS)
1430 return ERROR_SUCCESS;
1432 rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveDuplicateFiles, package );
1433 msiobj_release( &view->hdr );
1434 return rc;
1437 static BOOL verify_comp_for_removal(MSICOMPONENT *comp, UINT install_mode)
1439 /* special case */
1440 if (comp->Action != INSTALLSTATE_SOURCE &&
1441 comp->Attributes & msidbComponentAttributesSourceOnly &&
1442 (install_mode == msidbRemoveFileInstallModeOnRemove ||
1443 install_mode == msidbRemoveFileInstallModeOnBoth)) return TRUE;
1445 switch (comp->Action)
1447 case INSTALLSTATE_LOCAL:
1448 case INSTALLSTATE_SOURCE:
1449 if (install_mode == msidbRemoveFileInstallModeOnInstall ||
1450 install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE;
1451 break;
1452 case INSTALLSTATE_ABSENT:
1453 if (install_mode == msidbRemoveFileInstallModeOnRemove ||
1454 install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE;
1455 break;
1456 default: break;
1458 return FALSE;
1461 static UINT ITERATE_RemoveFiles(MSIRECORD *row, LPVOID param)
1463 MSIPACKAGE *package = param;
1464 MSICOMPONENT *comp;
1465 MSIRECORD *uirow;
1466 LPCWSTR component, dirprop;
1467 UINT install_mode;
1468 LPWSTR dir = NULL, path = NULL, filename = NULL;
1469 DWORD size;
1470 UINT ret = ERROR_SUCCESS;
1472 component = MSI_RecordGetString(row, 2);
1473 dirprop = MSI_RecordGetString(row, 4);
1474 install_mode = MSI_RecordGetInteger(row, 5);
1476 comp = msi_get_loaded_component(package, component);
1477 if (!comp)
1478 return ERROR_SUCCESS;
1480 comp->Action = msi_get_component_action( package, comp );
1481 if (!verify_comp_for_removal(comp, install_mode))
1483 TRACE("Skipping removal due to install mode\n");
1484 return ERROR_SUCCESS;
1486 if (comp->assembly && !comp->assembly->application)
1488 return ERROR_SUCCESS;
1490 if (comp->Attributes & msidbComponentAttributesPermanent)
1492 TRACE("permanent component, not removing file\n");
1493 return ERROR_SUCCESS;
1496 dir = msi_dup_property(package->db, dirprop);
1497 if (!dir)
1499 WARN("directory property has no value\n");
1500 return ERROR_SUCCESS;
1502 size = 0;
1503 if ((filename = strdupW( MSI_RecordGetString(row, 3) )))
1505 msi_reduce_to_long_filename( filename );
1506 size = lstrlenW( filename );
1508 size += lstrlenW(dir) + 2;
1509 path = msi_alloc(size * sizeof(WCHAR));
1510 if (!path)
1512 ret = ERROR_OUTOFMEMORY;
1513 goto done;
1516 if (filename)
1518 lstrcpyW(path, dir);
1519 PathAddBackslashW(path);
1520 lstrcatW(path, filename);
1522 TRACE("Deleting misc file: %s\n", debugstr_w(path));
1523 msi_delete_file( package, path );
1525 else
1527 TRACE("Removing misc directory: %s\n", debugstr_w(dir));
1528 msi_remove_directory( package, dir );
1531 done:
1532 uirow = MSI_CreateRecord( 9 );
1533 MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(row, 1) );
1534 MSI_RecordSetStringW( uirow, 9, dir );
1535 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1536 msiobj_release( &uirow->hdr );
1538 msi_free(filename);
1539 msi_free(path);
1540 msi_free(dir);
1541 return ret;
1544 static void remove_folder( MSIFOLDER *folder )
1546 FolderList *fl;
1548 LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
1550 remove_folder( fl->folder );
1552 if (!folder->persistent && folder->State != FOLDER_STATE_REMOVED)
1554 if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
1558 UINT ACTION_RemoveFiles( MSIPACKAGE *package )
1560 MSIQUERY *view;
1561 MSICOMPONENT *comp;
1562 MSIFILE *file;
1563 UINT r;
1565 if (package->script == SCRIPT_NONE)
1566 return msi_schedule_action(package, SCRIPT_INSTALL, L"RemoveFiles");
1568 r = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `RemoveFile`", &view);
1569 if (r == ERROR_SUCCESS)
1571 r = MSI_IterateRecords(view, NULL, ITERATE_RemoveFiles, package);
1572 msiobj_release(&view->hdr);
1573 if (r != ERROR_SUCCESS)
1574 return r;
1577 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
1579 MSIRECORD *uirow;
1580 VS_FIXEDFILEINFO *ver;
1582 comp = file->Component;
1583 msi_file_update_ui( package, file, L"RemoveFiles" );
1585 comp->Action = msi_get_component_action( package, comp );
1586 if (comp->Action != INSTALLSTATE_ABSENT || comp->Installed == INSTALLSTATE_SOURCE)
1587 continue;
1589 if (comp->assembly && !comp->assembly->application)
1590 continue;
1592 if (comp->Attributes & msidbComponentAttributesPermanent)
1594 TRACE("permanent component, not removing file\n");
1595 continue;
1598 if (file->Version)
1600 ver = msi_get_disk_file_version( package, file->TargetPath );
1601 if (ver && msi_compare_file_versions( ver, file->Version ) > 0)
1603 TRACE("newer version detected, not removing file\n");
1604 msi_free( ver );
1605 continue;
1607 msi_free( ver );
1610 if (file->state == msifs_installed)
1611 WARN("removing installed file %s\n", debugstr_w(file->TargetPath));
1613 TRACE("removing %s\n", debugstr_w(file->File) );
1615 msi_set_file_attributes( package, file->TargetPath, FILE_ATTRIBUTE_NORMAL );
1616 if (!msi_delete_file( package, file->TargetPath ))
1618 WARN( "failed to delete %s (%lu)\n", debugstr_w(file->TargetPath), GetLastError() );
1620 file->state = msifs_missing;
1622 uirow = MSI_CreateRecord( 9 );
1623 MSI_RecordSetStringW( uirow, 1, file->FileName );
1624 MSI_RecordSetStringW( uirow, 9, comp->Directory );
1625 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
1626 msiobj_release( &uirow->hdr );
1629 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1631 comp->Action = msi_get_component_action( package, comp );
1632 if (comp->Action != INSTALLSTATE_ABSENT) continue;
1634 if (comp->Attributes & msidbComponentAttributesPermanent)
1636 TRACE("permanent component, not removing directory\n");
1637 continue;
1639 if (comp->assembly && !comp->assembly->application)
1640 msi_uninstall_assembly( package, comp );
1641 else
1643 MSIFOLDER *folder = msi_get_loaded_folder( package, comp->Directory );
1644 if (folder) remove_folder( folder );
1647 return ERROR_SUCCESS;