ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

files.c
Go to the documentation of this file.
00001 /*
00002  * Implementation of the Microsoft Installer (msi.dll)
00003  *
00004  * Copyright 2005 Aric Stewart for CodeWeavers
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  */
00020 
00021 
00022 /*
00023  * Actions dealing with files:
00024  *
00025  * InstallFiles
00026  * DuplicateFiles
00027  * MoveFiles
00028  * PatchFiles
00029  * RemoveDuplicateFiles
00030  * RemoveFiles
00031  */
00032 
00033 #include <stdarg.h>
00034 
00035 #include "windef.h"
00036 #include "winbase.h"
00037 #include "winerror.h"
00038 #include "wine/debug.h"
00039 #include "fdi.h"
00040 #include "msi.h"
00041 #include "msidefs.h"
00042 #include "msipriv.h"
00043 #include "winuser.h"
00044 #include "winreg.h"
00045 #include "shlwapi.h"
00046 #include "wine/unicode.h"
00047 
00048 WINE_DEFAULT_DEBUG_CHANNEL(msi);
00049 
00050 static HMODULE hmspatcha;
00051 static BOOL (WINAPI *ApplyPatchToFileW)(LPCWSTR, LPCWSTR, LPCWSTR, ULONG);
00052 
00053 static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action )
00054 {
00055     MSIRECORD *uirow;
00056 
00057     uirow = MSI_CreateRecord( 9 );
00058     MSI_RecordSetStringW( uirow, 1, f->FileName );
00059     MSI_RecordSetStringW( uirow, 9, f->Component->Directory );
00060     MSI_RecordSetInteger( uirow, 6, f->FileSize );
00061     msi_ui_actiondata( package, action, uirow );
00062     msiobj_release( &uirow->hdr );
00063     msi_ui_progress( package, 2, f->FileSize, 0, 0 );
00064 }
00065 
00066 static msi_file_state calculate_install_state( MSIPACKAGE *package, MSIFILE *file )
00067 {
00068     MSICOMPONENT *comp = file->Component;
00069     VS_FIXEDFILEINFO *file_version;
00070     WCHAR *font_version;
00071     msi_file_state state;
00072     DWORD file_size;
00073 
00074     comp->Action = msi_get_component_action( package, comp );
00075     if (comp->Action != INSTALLSTATE_LOCAL || (comp->assembly && comp->assembly->installed))
00076     {
00077         TRACE("file %s is not scheduled for install\n", debugstr_w(file->File));
00078         return msifs_skipped;
00079     }
00080     if ((comp->assembly && !comp->assembly->application && !comp->assembly->installed) ||
00081         GetFileAttributesW( file->TargetPath ) == INVALID_FILE_ATTRIBUTES)
00082     {
00083         TRACE("file %s is missing\n", debugstr_w(file->File));
00084         return msifs_missing;
00085     }
00086     if (file->Version)
00087     {
00088         if ((file_version = msi_get_disk_file_version( file->TargetPath )))
00089         {
00090             TRACE("new %s old %u.%u.%u.%u\n", debugstr_w(file->Version),
00091                   HIWORD(file_version->dwFileVersionMS),
00092                   LOWORD(file_version->dwFileVersionMS),
00093                   HIWORD(file_version->dwFileVersionLS),
00094                   LOWORD(file_version->dwFileVersionLS));
00095 
00096             if (msi_compare_file_versions( file_version, file->Version ) < 0)
00097                 state = msifs_overwrite;
00098             else
00099             {
00100                 TRACE("destination file version equal or greater, not overwriting\n");
00101                 state = msifs_present;
00102             }
00103             msi_free( file_version );
00104             return state;
00105         }
00106         else if ((font_version = msi_font_version_from_file( file->TargetPath )))
00107         {
00108             TRACE("new %s old %s\n", debugstr_w(file->Version), debugstr_w(font_version));
00109 
00110             if (msi_compare_font_versions( font_version, file->Version ) < 0)
00111                 state = msifs_overwrite;
00112             else
00113             {
00114                 TRACE("destination file version equal or greater, not overwriting\n");
00115                 state = msifs_present;
00116             }
00117             msi_free( font_version );
00118             return state;
00119         }
00120     }
00121     if ((file_size = msi_get_disk_file_size( file->TargetPath )) != file->FileSize)
00122     {
00123         return msifs_overwrite;
00124     }
00125     if (file->hash.dwFileHashInfoSize)
00126     {
00127         if (msi_file_hash_matches( file ))
00128         {
00129             TRACE("file hashes match, not overwriting\n");
00130             return msifs_hashmatch;
00131         }
00132         else
00133         {
00134             TRACE("file hashes do not match, overwriting\n");
00135             return msifs_overwrite;
00136         }
00137     }
00138     /* assume present */
00139     return msifs_present;
00140 }
00141 
00142 static void schedule_install_files(MSIPACKAGE *package)
00143 {
00144     MSIFILE *file;
00145 
00146     LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
00147     {
00148         MSICOMPONENT *comp = file->Component;
00149 
00150         file->state = calculate_install_state( package, file );
00151         if (file->state == msifs_overwrite && (comp->Attributes & msidbComponentAttributesNeverOverwrite))
00152         {
00153             TRACE("not overwriting %s\n", debugstr_w(file->TargetPath));
00154             file->state = msifs_skipped;
00155         }
00156     }
00157 }
00158 
00159 static UINT copy_file(MSIFILE *file, LPWSTR source)
00160 {
00161     BOOL ret;
00162 
00163     ret = CopyFileW(source, file->TargetPath, FALSE);
00164     if (!ret)
00165         return GetLastError();
00166 
00167     SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL);
00168 
00169     file->state = msifs_installed;
00170     return ERROR_SUCCESS;
00171 }
00172 
00173 static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source)
00174 {
00175     UINT gle;
00176 
00177     TRACE("Copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath));
00178 
00179     gle = copy_file(file, source);
00180     if (gle == ERROR_SUCCESS)
00181         return gle;
00182 
00183     if (gle == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite)
00184     {
00185         TRACE("overwriting existing file\n");
00186         return ERROR_SUCCESS;
00187     }
00188     else if (gle == ERROR_ACCESS_DENIED)
00189     {
00190         SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL);
00191 
00192         gle = copy_file(file, source);
00193         TRACE("Overwriting existing file: %d\n", gle);
00194     }
00195     if (gle == ERROR_SHARING_VIOLATION || gle == ERROR_USER_MAPPED_FILE)
00196     {
00197         WCHAR *tmpfileW, *pathW, *p;
00198         DWORD len;
00199 
00200         TRACE("file in use, scheduling rename operation\n");
00201 
00202         if (!(pathW = strdupW( file->TargetPath ))) return ERROR_OUTOFMEMORY;
00203         if ((p = strrchrW(pathW, '\\'))) *p = 0;
00204         len = strlenW( pathW ) + 16;
00205         if (!(tmpfileW = msi_alloc(len * sizeof(WCHAR))))
00206         {
00207             msi_free( pathW );
00208             return ERROR_OUTOFMEMORY;
00209         }
00210         if (!GetTempFileNameW( pathW, szMsi, 0, tmpfileW )) tmpfileW[0] = 0;
00211         msi_free( pathW );
00212 
00213         if (CopyFileW(source, tmpfileW, FALSE) &&
00214             MoveFileExW(file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) &&
00215             MoveFileExW(tmpfileW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT))
00216         {
00217             file->state = msifs_installed;
00218             package->need_reboot_at_end = 1;
00219             gle = ERROR_SUCCESS;
00220         }
00221         else
00222         {
00223             gle = GetLastError();
00224             WARN("failed to schedule rename operation: %d)\n", gle);
00225             DeleteFileW( tmpfileW );
00226         }
00227         msi_free(tmpfileW);
00228     }
00229 
00230     return gle;
00231 }
00232 
00233 static UINT msi_create_directory( MSIPACKAGE *package, const WCHAR *dir )
00234 {
00235     MSIFOLDER *folder;
00236     const WCHAR *install_path;
00237 
00238     install_path = msi_get_target_folder( package, dir );
00239     if (!install_path) return ERROR_FUNCTION_FAILED;
00240 
00241     folder = msi_get_loaded_folder( package, dir );
00242     if (folder->State == FOLDER_STATE_UNINITIALIZED)
00243     {
00244         msi_create_full_path( install_path );
00245         folder->State = FOLDER_STATE_CREATED;
00246     }
00247     return ERROR_SUCCESS;
00248 }
00249 
00250 static MSIFILE *find_file( MSIPACKAGE *package, const WCHAR *filename )
00251 {
00252     MSIFILE *file;
00253 
00254     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
00255     {
00256         if (file->state != msifs_installed && !strcmpiW( filename, file->File )) return file;
00257     }
00258     return NULL;
00259 }
00260 
00261 static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
00262                             LPWSTR *path, DWORD *attrs, PVOID user)
00263 {
00264     static MSIFILE *f = NULL;
00265     UINT_PTR disk_id = (UINT_PTR)user;
00266 
00267     if (action == MSICABEXTRACT_BEGINEXTRACT)
00268     {
00269         if (!(f = find_file( package, file )))
00270         {
00271             TRACE("unknown file in cabinet (%s)\n", debugstr_w(file));
00272             return FALSE;
00273         }
00274         if (f->disk_id != disk_id || (f->state != msifs_missing && f->state != msifs_overwrite))
00275             return FALSE;
00276 
00277         if (!f->Component->assembly || f->Component->assembly->application)
00278         {
00279             msi_create_directory(package, f->Component->Directory);
00280         }
00281         *path = strdupW(f->TargetPath);
00282         *attrs = f->Attributes;
00283     }
00284     else if (action == MSICABEXTRACT_FILEEXTRACTED)
00285     {
00286         f->state = msifs_installed;
00287         f = NULL;
00288     }
00289 
00290     return TRUE;
00291 }
00292 
00293 WCHAR *msi_resolve_file_source( MSIPACKAGE *package, MSIFILE *file )
00294 {
00295     WCHAR *p, *path;
00296 
00297     TRACE("Working to resolve source of file %s\n", debugstr_w(file->File));
00298 
00299     if (file->IsCompressed) return NULL;
00300 
00301     p = msi_resolve_source_folder( package, file->Component->Directory, NULL );
00302     path = msi_build_directory_name( 2, p, file->ShortName );
00303 
00304     if (file->LongName && GetFileAttributesW( path ) == INVALID_FILE_ATTRIBUTES)
00305     {
00306         msi_free( path );
00307         path = msi_build_directory_name( 2, p, file->LongName );
00308     }
00309     msi_free( p );
00310     TRACE("file %s source resolves to %s\n", debugstr_w(file->File), debugstr_w(path));
00311     return path;
00312 }
00313 
00314 /*
00315  * ACTION_InstallFiles()
00316  * 
00317  * For efficiency, this is done in two passes:
00318  * 1) Correct all the TargetPaths and determine what files are to be installed.
00319  * 2) Extract Cabinets and copy files.
00320  */
00321 UINT ACTION_InstallFiles(MSIPACKAGE *package)
00322 {
00323     MSIMEDIAINFO *mi;
00324     MSICOMPONENT *comp;
00325     UINT rc = ERROR_SUCCESS;
00326     MSIFILE *file;
00327 
00328     schedule_install_files(package);
00329     mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
00330 
00331     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
00332     {
00333         msi_file_update_ui( package, file, szInstallFiles );
00334 
00335         rc = msi_load_media_info( package, file->Sequence, mi );
00336         if (rc != ERROR_SUCCESS)
00337         {
00338             ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc);
00339             return ERROR_FUNCTION_FAILED;
00340         }
00341         if (!file->Component->Enabled) continue;
00342 
00343         if (file->state != msifs_hashmatch &&
00344             file->state != msifs_skipped &&
00345             (file->state != msifs_present || !msi_get_property_int( package->db, szInstalled, 0 )) &&
00346             (rc = ready_media( package, file->IsCompressed, mi )))
00347         {
00348             ERR("Failed to ready media for %s\n", debugstr_w(file->File));
00349             goto done;
00350         }
00351 
00352         if (file->state != msifs_missing && !mi->is_continuous && file->state != msifs_overwrite)
00353             continue;
00354 
00355         if (file->Sequence > mi->last_sequence || mi->is_continuous ||
00356             (file->IsCompressed && !mi->is_extracted))
00357         {
00358             MSICABDATA data;
00359 
00360             data.mi = mi;
00361             data.package = package;
00362             data.cb = installfiles_cb;
00363             data.user = (PVOID)(UINT_PTR)mi->disk_id;
00364 
00365             if (file->IsCompressed &&
00366                 !msi_cabextract(package, mi, &data))
00367             {
00368                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
00369                 rc = ERROR_INSTALL_FAILURE;
00370                 goto done;
00371             }
00372         }
00373 
00374         if (!file->IsCompressed)
00375         {
00376             WCHAR *source = msi_resolve_file_source(package, file);
00377 
00378             TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath));
00379 
00380             if (!file->Component->assembly || file->Component->assembly->application)
00381             {
00382                 msi_create_directory(package, file->Component->Directory);
00383             }
00384             rc = copy_install_file(package, file, source);
00385             if (rc != ERROR_SUCCESS)
00386             {
00387                 ERR("Failed to copy %s to %s (%d)\n", debugstr_w(source),
00388                     debugstr_w(file->TargetPath), rc);
00389                 rc = ERROR_INSTALL_FAILURE;
00390                 msi_free(source);
00391                 goto done;
00392             }
00393             msi_free(source);
00394         }
00395         else if (file->state != msifs_installed && !(file->Attributes & msidbFileAttributesPatchAdded))
00396         {
00397             ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->TargetPath));
00398             rc = ERROR_INSTALL_FAILURE;
00399             goto done;
00400         }
00401     }
00402     msi_init_assembly_caches( package );
00403     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
00404     {
00405         comp->Action = msi_get_component_action( package, comp );
00406         if (comp->Action == INSTALLSTATE_LOCAL && comp->assembly && !comp->assembly->installed)
00407         {
00408             rc = msi_install_assembly( package, comp );
00409             if (rc != ERROR_SUCCESS)
00410             {
00411                 ERR("Failed to install assembly\n");
00412                 rc = ERROR_INSTALL_FAILURE;
00413                 break;
00414             }
00415         }
00416     }
00417     msi_destroy_assembly_caches( package );
00418 
00419 done:
00420     msi_free_media_info(mi);
00421     return rc;
00422 }
00423 
00424 static BOOL load_mspatcha(void)
00425 {
00426     hmspatcha = LoadLibraryA("mspatcha.dll");
00427     if (!hmspatcha)
00428     {
00429         ERR("Failed to load mspatcha.dll: %d\n", GetLastError());
00430         return FALSE;
00431     }
00432 
00433     ApplyPatchToFileW = (void*)GetProcAddress(hmspatcha, "ApplyPatchToFileW");
00434     if(!ApplyPatchToFileW)
00435     {
00436         ERR("GetProcAddress(ApplyPatchToFileW) failed: %d.\n", GetLastError());
00437         return FALSE;
00438     }
00439 
00440     return TRUE;
00441 }
00442 
00443 static void unload_mspatch(void)
00444 {
00445     FreeLibrary(hmspatcha);
00446 }
00447 
00448 static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
00449                           LPWSTR *path, DWORD *attrs, PVOID user)
00450 {
00451     static MSIFILEPATCH *p = NULL;
00452     static WCHAR patch_path[MAX_PATH] = {0};
00453     static WCHAR temp_folder[MAX_PATH] = {0};
00454 
00455     if (action == MSICABEXTRACT_BEGINEXTRACT)
00456     {
00457         if (temp_folder[0] == '\0')
00458             GetTempPathW(MAX_PATH, temp_folder);
00459 
00460         p = msi_get_loaded_filepatch(package, file);
00461         if (!p)
00462         {
00463             TRACE("unknown file in cabinet (%s)\n", debugstr_w(file));
00464             return FALSE;
00465         }
00466         GetTempFileNameW(temp_folder, NULL, 0, patch_path);
00467 
00468         *path = strdupW(patch_path);
00469         *attrs = p->File->Attributes;
00470     }
00471     else if (action == MSICABEXTRACT_FILEEXTRACTED)
00472     {
00473         WCHAR patched_file[MAX_PATH];
00474         BOOL br;
00475 
00476         GetTempFileNameW(temp_folder, NULL, 0, patched_file);
00477 
00478         br = ApplyPatchToFileW(patch_path, p->File->TargetPath, patched_file, 0);
00479         if (br)
00480         {
00481             /* FIXME: baseline cache */
00482 
00483             DeleteFileW( p->File->TargetPath );
00484             MoveFileW( patched_file, p->File->TargetPath );
00485 
00486             p->IsApplied = TRUE;
00487         }
00488         else
00489             ERR("Failed patch %s: %d.\n", debugstr_w(p->File->TargetPath), GetLastError());
00490 
00491         DeleteFileW(patch_path);
00492         p = NULL;
00493     }
00494 
00495     return TRUE;
00496 }
00497 
00498 UINT ACTION_PatchFiles( MSIPACKAGE *package )
00499 {
00500     MSIFILEPATCH *patch;
00501     MSIMEDIAINFO *mi;
00502     UINT rc = ERROR_SUCCESS;
00503     BOOL mspatcha_loaded = FALSE;
00504 
00505     TRACE("%p\n", package);
00506 
00507     mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
00508 
00509     LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
00510     {
00511         MSIFILE *file = patch->File;
00512         MSICOMPONENT *comp = file->Component;
00513 
00514         rc = msi_load_media_info( package, patch->Sequence, mi );
00515         if (rc != ERROR_SUCCESS)
00516         {
00517             ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc);
00518             return ERROR_FUNCTION_FAILED;
00519         }
00520         comp->Action = msi_get_component_action( package, comp );
00521         if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) continue;
00522 
00523         if (!patch->IsApplied)
00524         {
00525             MSICABDATA data;
00526 
00527             rc = ready_media( package, TRUE, mi );
00528             if (rc != ERROR_SUCCESS)
00529             {
00530                 ERR("Failed to ready media for %s\n", debugstr_w(file->File));
00531                 goto done;
00532             }
00533 
00534             if (!mspatcha_loaded && !load_mspatcha())
00535             {
00536                 rc = ERROR_FUNCTION_FAILED;
00537                 goto done;
00538             }
00539             mspatcha_loaded = TRUE;
00540 
00541             data.mi = mi;
00542             data.package = package;
00543             data.cb = patchfiles_cb;
00544             data.user = (PVOID)(UINT_PTR)mi->disk_id;
00545 
00546             if (!msi_cabextract(package, mi, &data))
00547             {
00548                 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
00549                 rc = ERROR_INSTALL_FAILURE;
00550                 goto done;
00551             }
00552         }
00553 
00554         if (!patch->IsApplied && !(patch->Attributes & msidbPatchAttributesNonVital))
00555         {
00556             ERR("Failed to apply patch to file: %s\n", debugstr_w(file->File));
00557             rc = ERROR_INSTALL_FAILURE;
00558             goto done;
00559         }
00560     }
00561 
00562 done:
00563     msi_free_media_info(mi);
00564     if (mspatcha_loaded)
00565         unload_mspatch();
00566     return rc;
00567 }
00568 
00569 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
00570 
00571 typedef struct
00572 {
00573     struct list entry;
00574     LPWSTR sourcename;
00575     LPWSTR destname;
00576     LPWSTR source;
00577     LPWSTR dest;
00578 } FILE_LIST;
00579 
00580 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
00581 {
00582     BOOL ret;
00583 
00584     if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
00585         GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
00586     {
00587         WARN("Source or dest is directory, not moving\n");
00588         return FALSE;
00589     }
00590 
00591     if (options == msidbMoveFileOptionsMove)
00592     {
00593         TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
00594         ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
00595         if (!ret)
00596         {
00597             WARN("MoveFile failed: %d\n", GetLastError());
00598             return FALSE;
00599         }
00600     }
00601     else
00602     {
00603         TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
00604         ret = CopyFileW(source, dest, FALSE);
00605         if (!ret)
00606         {
00607             WARN("CopyFile failed: %d\n", GetLastError());
00608             return FALSE;
00609         }
00610     }
00611 
00612     return TRUE;
00613 }
00614 
00615 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
00616 {
00617     LPWSTR path, ptr;
00618     DWORD dirlen, pathlen;
00619 
00620     ptr = strrchrW(wildcard, '\\');
00621     dirlen = ptr - wildcard + 1;
00622 
00623     pathlen = dirlen + lstrlenW(filename) + 1;
00624     path = msi_alloc(pathlen * sizeof(WCHAR));
00625 
00626     lstrcpynW(path, wildcard, dirlen + 1);
00627     lstrcatW(path, filename);
00628 
00629     return path;
00630 }
00631 
00632 static void free_file_entry(FILE_LIST *file)
00633 {
00634     msi_free(file->source);
00635     msi_free(file->dest);
00636     msi_free(file);
00637 }
00638 
00639 static void free_list(FILE_LIST *list)
00640 {
00641     while (!list_empty(&list->entry))
00642     {
00643         FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
00644 
00645         list_remove(&file->entry);
00646         free_file_entry(file);
00647     }
00648 }
00649 
00650 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
00651 {
00652     FILE_LIST *new, *file;
00653     LPWSTR ptr, filename;
00654     DWORD size;
00655 
00656     new = msi_alloc_zero(sizeof(FILE_LIST));
00657     if (!new)
00658         return FALSE;
00659 
00660     new->source = strdupW(source);
00661     ptr = strrchrW(dest, '\\') + 1;
00662     filename = strrchrW(new->source, '\\') + 1;
00663 
00664     new->sourcename = filename;
00665 
00666     if (*ptr)
00667         new->destname = ptr;
00668     else
00669         new->destname = new->sourcename;
00670 
00671     size = (ptr - dest) + lstrlenW(filename) + 1;
00672     new->dest = msi_alloc(size * sizeof(WCHAR));
00673     if (!new->dest)
00674     {
00675         free_file_entry(new);
00676         return FALSE;
00677     }
00678 
00679     lstrcpynW(new->dest, dest, ptr - dest + 1);
00680     lstrcatW(new->dest, filename);
00681 
00682     if (list_empty(&files->entry))
00683     {
00684         list_add_head(&files->entry, &new->entry);
00685         return TRUE;
00686     }
00687 
00688     LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
00689     {
00690         if (strcmpW( source, file->source ) < 0)
00691         {
00692             list_add_before(&file->entry, &new->entry);
00693             return TRUE;
00694         }
00695     }
00696 
00697     list_add_after(&file->entry, &new->entry);
00698     return TRUE;
00699 }
00700 
00701 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
00702 {
00703     WIN32_FIND_DATAW wfd;
00704     HANDLE hfile;
00705     LPWSTR path;
00706     BOOL res;
00707     FILE_LIST files, *file;
00708     DWORD size;
00709 
00710     hfile = FindFirstFileW(source, &wfd);
00711     if (hfile == INVALID_HANDLE_VALUE) return FALSE;
00712 
00713     list_init(&files.entry);
00714 
00715     for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
00716     {
00717         if (is_dot_dir(wfd.cFileName)) continue;
00718 
00719         path = wildcard_to_file(source, wfd.cFileName);
00720         if (!path)
00721         {
00722             res = FALSE;
00723             goto done;
00724         }
00725 
00726         add_wildcard(&files, path, dest);
00727         msi_free(path);
00728     }
00729 
00730     /* no files match the wildcard */
00731     if (list_empty(&files.entry))
00732         goto done;
00733 
00734     /* only the first wildcard match gets renamed to dest */
00735     file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
00736     size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
00737     file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
00738     if (!file->dest)
00739     {
00740         res = FALSE;
00741         goto done;
00742     }
00743 
00744     /* file->dest may be shorter after the reallocation, so add a NULL
00745      * terminator.  This is needed for the call to strrchrW, as there will no
00746      * longer be a NULL terminator within the bounds of the allocation in this case.
00747      */
00748     file->dest[size - 1] = '\0';
00749     lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
00750 
00751     while (!list_empty(&files.entry))
00752     {
00753         file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
00754 
00755         msi_move_file(file->source, file->dest, options);
00756 
00757         list_remove(&file->entry);
00758         free_file_entry(file);
00759     }
00760 
00761     res = TRUE;
00762 
00763 done:
00764     free_list(&files);
00765     FindClose(hfile);
00766     return res;
00767 }
00768 
00769 void msi_reduce_to_long_filename( WCHAR *filename )
00770 {
00771     WCHAR *p = strchrW( filename, '|' );
00772     if (p) memmove( filename, p + 1, (strlenW( p + 1 ) + 1) * sizeof(WCHAR) );
00773 }
00774 
00775 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
00776 {
00777     MSIPACKAGE *package = param;
00778     MSIRECORD *uirow;
00779     MSICOMPONENT *comp;
00780     LPCWSTR sourcename, component;
00781     LPWSTR sourcedir, destname = NULL, destdir = NULL, source = NULL, dest = NULL;
00782     int options;
00783     DWORD size;
00784     BOOL ret, wildcards;
00785 
00786     component = MSI_RecordGetString(rec, 2);
00787     comp = msi_get_loaded_component(package, component);
00788     if (!comp)
00789         return ERROR_SUCCESS;
00790 
00791     comp->Action = msi_get_component_action( package, comp );
00792     if (comp->Action != INSTALLSTATE_LOCAL)
00793     {
00794         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
00795         return ERROR_SUCCESS;
00796     }
00797 
00798     sourcename = MSI_RecordGetString(rec, 3);
00799     options = MSI_RecordGetInteger(rec, 7);
00800 
00801     sourcedir = msi_dup_property(package->db, MSI_RecordGetString(rec, 5));
00802     if (!sourcedir)
00803         goto done;
00804 
00805     destdir = msi_dup_property(package->db, MSI_RecordGetString(rec, 6));
00806     if (!destdir)
00807         goto done;
00808 
00809     if (!sourcename)
00810     {
00811         if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
00812             goto done;
00813 
00814         source = strdupW(sourcedir);
00815         if (!source)
00816             goto done;
00817     }
00818     else
00819     {
00820         size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
00821         source = msi_alloc(size * sizeof(WCHAR));
00822         if (!source)
00823             goto done;
00824 
00825         lstrcpyW(source, sourcedir);
00826         if (source[lstrlenW(source) - 1] != '\\')
00827             lstrcatW(source, szBackSlash);
00828         lstrcatW(source, sourcename);
00829     }
00830 
00831     wildcards = strchrW(source, '*') || strchrW(source, '?');
00832 
00833     if (MSI_RecordIsNull(rec, 4))
00834     {
00835         if (!wildcards)
00836         {
00837             destname = strdupW(sourcename);
00838             if (!destname)
00839                 goto done;
00840         }
00841     }
00842     else
00843     {
00844         destname = strdupW(MSI_RecordGetString(rec, 4));
00845         if (destname) msi_reduce_to_long_filename(destname);
00846     }
00847 
00848     size = 0;
00849     if (destname)
00850         size = lstrlenW(destname);
00851 
00852     size += lstrlenW(destdir) + 2;
00853     dest = msi_alloc(size * sizeof(WCHAR));
00854     if (!dest)
00855         goto done;
00856 
00857     lstrcpyW(dest, destdir);
00858     if (dest[lstrlenW(dest) - 1] != '\\')
00859         lstrcatW(dest, szBackSlash);
00860 
00861     if (destname)
00862         lstrcatW(dest, destname);
00863 
00864     if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
00865     {
00866         if (!(ret = msi_create_full_path(destdir)))
00867         {
00868             WARN("failed to create directory %u\n", GetLastError());
00869             goto done;
00870         }
00871     }
00872 
00873     if (!wildcards)
00874         msi_move_file(source, dest, options);
00875     else
00876         move_files_wildcard(source, dest, options);
00877 
00878 done:
00879     uirow = MSI_CreateRecord( 9 );
00880     MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(rec, 1) );
00881     MSI_RecordSetInteger( uirow, 6, 1 ); /* FIXME */
00882     MSI_RecordSetStringW( uirow, 9, destdir );
00883     msi_ui_actiondata( package, szMoveFiles, uirow );
00884     msiobj_release( &uirow->hdr );
00885 
00886     msi_free(sourcedir);
00887     msi_free(destdir);
00888     msi_free(destname);
00889     msi_free(source);
00890     msi_free(dest);
00891 
00892     return ERROR_SUCCESS;
00893 }
00894 
00895 UINT ACTION_MoveFiles( MSIPACKAGE *package )
00896 {
00897     static const WCHAR query[] = {
00898         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
00899         '`','M','o','v','e','F','i','l','e','`',0};
00900     MSIQUERY *view;
00901     UINT rc;
00902 
00903     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
00904     if (rc != ERROR_SUCCESS)
00905         return ERROR_SUCCESS;
00906 
00907     rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
00908     msiobj_release(&view->hdr);
00909     return rc;
00910 }
00911 
00912 static WCHAR *get_duplicate_filename( MSIPACKAGE *package, MSIRECORD *row, const WCHAR *file_key, const WCHAR *src )
00913 {
00914     DWORD len;
00915     WCHAR *dst_name, *dst_path, *dst;
00916 
00917     if (MSI_RecordIsNull( row, 4 ))
00918     {
00919         len = strlenW( src ) + 1;
00920         if (!(dst_name = msi_alloc( len * sizeof(WCHAR)))) return NULL;
00921         strcpyW( dst_name, strrchrW( src, '\\' ) + 1 );
00922     }
00923     else
00924     {
00925         MSI_RecordGetStringW( row, 4, NULL, &len );
00926         if (!(dst_name = msi_alloc( ++len * sizeof(WCHAR) ))) return NULL;
00927         MSI_RecordGetStringW( row, 4, dst_name, &len );
00928         msi_reduce_to_long_filename( dst_name );
00929     }
00930 
00931     if (MSI_RecordIsNull( row, 5 ))
00932     {
00933         WCHAR *p;
00934         dst_path = strdupW( src );
00935         p = strrchrW( dst_path, '\\' );
00936         if (p) *p = 0;
00937     }
00938     else
00939     {
00940         const WCHAR *dst_key = MSI_RecordGetString( row, 5 );
00941 
00942         dst_path = strdupW( msi_get_target_folder( package, dst_key ) );
00943         if (!dst_path)
00944         {
00945             /* try a property */
00946             dst_path = msi_dup_property( package->db, dst_key );
00947             if (!dst_path)
00948             {
00949                 FIXME("Unable to get destination folder, try AppSearch properties\n");
00950                 msi_free( dst_name );
00951                 return NULL;
00952             }
00953         }
00954     }
00955 
00956     dst = msi_build_directory_name( 2, dst_path, dst_name );
00957     msi_create_full_path( dst_path );
00958 
00959     msi_free( dst_name );
00960     msi_free( dst_path );
00961     return dst;
00962 }
00963 
00964 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
00965 {
00966     MSIPACKAGE *package = param;
00967     LPWSTR dest;
00968     LPCWSTR file_key, component;
00969     MSICOMPONENT *comp;
00970     MSIRECORD *uirow;
00971     MSIFILE *file;
00972 
00973     component = MSI_RecordGetString(row,2);
00974     comp = msi_get_loaded_component(package, component);
00975     if (!comp)
00976         return ERROR_SUCCESS;
00977 
00978     comp->Action = msi_get_component_action( package, comp );
00979     if (comp->Action != INSTALLSTATE_LOCAL)
00980     {
00981         TRACE("component not scheduled for installation %s\n", debugstr_w(component));
00982         return ERROR_SUCCESS;
00983     }
00984 
00985     file_key = MSI_RecordGetString(row,3);
00986     if (!file_key)
00987     {
00988         ERR("Unable to get file key\n");
00989         return ERROR_FUNCTION_FAILED;
00990     }
00991 
00992     file = msi_get_loaded_file( package, file_key );
00993     if (!file)
00994     {
00995         ERR("Original file unknown %s\n", debugstr_w(file_key));
00996         return ERROR_SUCCESS;
00997     }
00998 
00999     dest = get_duplicate_filename( package, row, file_key, file->TargetPath );
01000     if (!dest)
01001     {
01002         WARN("Unable to get duplicate filename\n");
01003         return ERROR_SUCCESS;
01004     }
01005 
01006     TRACE("Duplicating file %s to %s\n", debugstr_w(file->TargetPath), debugstr_w(dest));
01007 
01008     if (!CopyFileW( file->TargetPath, dest, TRUE ))
01009     {
01010         WARN("Failed to copy file %s -> %s (%u)\n",
01011              debugstr_w(file->TargetPath), debugstr_w(dest), GetLastError());
01012     }
01013 
01014     FIXME("We should track these duplicate files as well\n");   
01015 
01016     uirow = MSI_CreateRecord( 9 );
01017     MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) );
01018     MSI_RecordSetInteger( uirow, 6, file->FileSize );
01019     MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) );
01020     msi_ui_actiondata( package, szDuplicateFiles, uirow );
01021     msiobj_release( &uirow->hdr );
01022 
01023     msi_free(dest);
01024     return ERROR_SUCCESS;
01025 }
01026 
01027 UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
01028 {
01029     static const WCHAR query[] = {
01030         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
01031         '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
01032     MSIQUERY *view;
01033     UINT rc;
01034 
01035     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
01036     if (rc != ERROR_SUCCESS)
01037         return ERROR_SUCCESS;
01038 
01039     rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
01040     msiobj_release(&view->hdr);
01041     return rc;
01042 }
01043 
01044 static UINT ITERATE_RemoveDuplicateFiles( MSIRECORD *row, LPVOID param )
01045 {
01046     MSIPACKAGE *package = param;
01047     LPWSTR dest;
01048     LPCWSTR file_key, component;
01049     MSICOMPONENT *comp;
01050     MSIRECORD *uirow;
01051     MSIFILE *file;
01052 
01053     component = MSI_RecordGetString( row, 2 );
01054     comp = msi_get_loaded_component( package, component );
01055     if (!comp)
01056         return ERROR_SUCCESS;
01057 
01058     comp->Action = msi_get_component_action( package, comp );
01059     if (comp->Action != INSTALLSTATE_ABSENT)
01060     {
01061         TRACE("component not scheduled for removal %s\n", debugstr_w(component));
01062         return ERROR_SUCCESS;
01063     }
01064 
01065     file_key = MSI_RecordGetString( row, 3 );
01066     if (!file_key)
01067     {
01068         ERR("Unable to get file key\n");
01069         return ERROR_FUNCTION_FAILED;
01070     }
01071 
01072     file = msi_get_loaded_file( package, file_key );
01073     if (!file)
01074     {
01075         ERR("Original file unknown %s\n", debugstr_w(file_key));
01076         return ERROR_SUCCESS;
01077     }
01078 
01079     dest = get_duplicate_filename( package, row, file_key, file->TargetPath );
01080     if (!dest)
01081     {
01082         WARN("Unable to get duplicate filename\n");
01083         return ERROR_SUCCESS;
01084     }
01085 
01086     TRACE("Removing duplicate %s of %s\n", debugstr_w(dest), debugstr_w(file->TargetPath));
01087 
01088     if (!DeleteFileW( dest ))
01089     {
01090         WARN("Failed to delete duplicate file %s (%u)\n", debugstr_w(dest), GetLastError());
01091     }
01092 
01093     uirow = MSI_CreateRecord( 9 );
01094     MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) );
01095     MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) );
01096     msi_ui_actiondata( package, szRemoveDuplicateFiles, uirow );
01097     msiobj_release( &uirow->hdr );
01098 
01099     msi_free(dest);
01100     return ERROR_SUCCESS;
01101 }
01102 
01103 UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
01104 {
01105     static const WCHAR query[] = {
01106         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
01107         '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
01108     MSIQUERY *view;
01109     UINT rc;
01110 
01111     rc = MSI_DatabaseOpenViewW( package->db, query, &view );
01112     if (rc != ERROR_SUCCESS)
01113         return ERROR_SUCCESS;
01114 
01115     rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveDuplicateFiles, package );
01116     msiobj_release( &view->hdr );
01117     return rc;
01118 }
01119 
01120 static BOOL verify_comp_for_removal(MSICOMPONENT *comp, UINT install_mode)
01121 {
01122     /* special case */
01123     if (comp->Action != INSTALLSTATE_SOURCE &&
01124         comp->Attributes & msidbComponentAttributesSourceOnly &&
01125         (install_mode == msidbRemoveFileInstallModeOnRemove ||
01126          install_mode == msidbRemoveFileInstallModeOnBoth)) return TRUE;
01127 
01128     switch (comp->Action)
01129     {
01130     case INSTALLSTATE_LOCAL:
01131     case INSTALLSTATE_SOURCE:
01132         if (install_mode == msidbRemoveFileInstallModeOnInstall ||
01133             install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE;
01134         break;
01135     case INSTALLSTATE_ABSENT:
01136         if (install_mode == msidbRemoveFileInstallModeOnRemove ||
01137             install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE;
01138         break;
01139     default: break;
01140     }
01141     return FALSE;
01142 }
01143 
01144 static UINT ITERATE_RemoveFiles(MSIRECORD *row, LPVOID param)
01145 {
01146     MSIPACKAGE *package = param;
01147     MSICOMPONENT *comp;
01148     MSIRECORD *uirow;
01149     LPCWSTR component, dirprop;
01150     UINT install_mode;
01151     LPWSTR dir = NULL, path = NULL, filename = NULL;
01152     DWORD size;
01153     UINT ret = ERROR_SUCCESS;
01154 
01155     component = MSI_RecordGetString(row, 2);
01156     dirprop = MSI_RecordGetString(row, 4);
01157     install_mode = MSI_RecordGetInteger(row, 5);
01158 
01159     comp = msi_get_loaded_component(package, component);
01160     if (!comp)
01161         return ERROR_SUCCESS;
01162 
01163     comp->Action = msi_get_component_action( package, comp );
01164     if (!verify_comp_for_removal(comp, install_mode))
01165     {
01166         TRACE("Skipping removal due to install mode\n");
01167         return ERROR_SUCCESS;
01168     }
01169     if (comp->assembly && !comp->assembly->application)
01170     {
01171         return ERROR_SUCCESS;
01172     }
01173     if (comp->Attributes & msidbComponentAttributesPermanent)
01174     {
01175         TRACE("permanent component, not removing file\n");
01176         return ERROR_SUCCESS;
01177     }
01178 
01179     dir = msi_dup_property(package->db, dirprop);
01180     if (!dir)
01181     {
01182         WARN("directory property has no value\n");
01183         return ERROR_SUCCESS;
01184     }
01185     size = 0;
01186     if ((filename = strdupW( MSI_RecordGetString(row, 3) )))
01187     {
01188         msi_reduce_to_long_filename( filename );
01189         size = lstrlenW( filename );
01190     }
01191     size += lstrlenW(dir) + 2;
01192     path = msi_alloc(size * sizeof(WCHAR));
01193     if (!path)
01194     {
01195         ret = ERROR_OUTOFMEMORY;
01196         goto done;
01197     }
01198 
01199     if (filename)
01200     {
01201         lstrcpyW(path, dir);
01202         PathAddBackslashW(path);
01203         lstrcatW(path, filename);
01204 
01205         TRACE("Deleting misc file: %s\n", debugstr_w(path));
01206         DeleteFileW(path);
01207     }
01208     else
01209     {
01210         TRACE("Removing misc directory: %s\n", debugstr_w(dir));
01211         RemoveDirectoryW(dir);
01212     }
01213 
01214 done:
01215     uirow = MSI_CreateRecord( 9 );
01216     MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(row, 1) );
01217     MSI_RecordSetStringW( uirow, 9, dir );
01218     msi_ui_actiondata( package, szRemoveFiles, uirow );
01219     msiobj_release( &uirow->hdr );
01220 
01221     msi_free(filename);
01222     msi_free(path);
01223     msi_free(dir);
01224     return ret;
01225 }
01226 
01227 static void remove_folder( MSIFOLDER *folder )
01228 {
01229     FolderList *fl;
01230 
01231     LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
01232     {
01233         remove_folder( fl->folder );
01234     }
01235     if (!folder->persistent && folder->State != FOLDER_STATE_REMOVED)
01236     {
01237         if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
01238     }
01239 }
01240 
01241 UINT ACTION_RemoveFiles( MSIPACKAGE *package )
01242 {
01243     static const WCHAR query[] = {
01244         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
01245         '`','R','e','m','o','v','e','F','i','l','e','`',0};
01246     MSIQUERY *view;
01247     MSICOMPONENT *comp;
01248     MSIFILE *file;
01249     UINT r;
01250 
01251     r = MSI_DatabaseOpenViewW(package->db, query, &view);
01252     if (r == ERROR_SUCCESS)
01253     {
01254         r = MSI_IterateRecords(view, NULL, ITERATE_RemoveFiles, package);
01255         msiobj_release(&view->hdr);
01256         if (r != ERROR_SUCCESS)
01257             return r;
01258     }
01259 
01260     LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
01261     {
01262         MSIRECORD *uirow;
01263         VS_FIXEDFILEINFO *ver;
01264 
01265         comp = file->Component;
01266         msi_file_update_ui( package, file, szRemoveFiles );
01267 
01268         comp->Action = msi_get_component_action( package, comp );
01269         if (comp->Action != INSTALLSTATE_ABSENT || comp->Installed == INSTALLSTATE_SOURCE)
01270             continue;
01271 
01272         if (comp->assembly && !comp->assembly->application)
01273             continue;
01274 
01275         if (comp->Attributes & msidbComponentAttributesPermanent)
01276         {
01277             TRACE("permanent component, not removing file\n");
01278             continue;
01279         }
01280 
01281         if (file->Version)
01282         {
01283             ver = msi_get_disk_file_version( file->TargetPath );
01284             if (ver && msi_compare_file_versions( ver, file->Version ) > 0)
01285             {
01286                 TRACE("newer version detected, not removing file\n");
01287                 msi_free( ver );
01288                 continue;
01289             }
01290             msi_free( ver );
01291         }
01292 
01293         if (file->state == msifs_installed)
01294             WARN("removing installed file %s\n", debugstr_w(file->TargetPath));
01295 
01296         TRACE("removing %s\n", debugstr_w(file->File) );
01297 
01298         SetFileAttributesW( file->TargetPath, FILE_ATTRIBUTE_NORMAL );
01299         if (!DeleteFileW( file->TargetPath ))
01300         {
01301             WARN("failed to delete %s (%u)\n",  debugstr_w(file->TargetPath), GetLastError());
01302         }
01303         file->state = msifs_missing;
01304 
01305         uirow = MSI_CreateRecord( 9 );
01306         MSI_RecordSetStringW( uirow, 1, file->FileName );
01307         MSI_RecordSetStringW( uirow, 9, comp->Directory );
01308         msi_ui_actiondata( package, szRemoveFiles, uirow );
01309         msiobj_release( &uirow->hdr );
01310     }
01311 
01312     msi_init_assembly_caches( package );
01313     LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
01314     {
01315         comp->Action = msi_get_component_action( package, comp );
01316         if (comp->Action != INSTALLSTATE_ABSENT) continue;
01317 
01318         if (comp->Attributes & msidbComponentAttributesPermanent)
01319         {
01320             TRACE("permanent component, not removing directory\n");
01321             continue;
01322         }
01323         if (comp->assembly && !comp->assembly->application)
01324             msi_uninstall_assembly( package, comp );
01325         else
01326         {
01327             MSIFOLDER *folder = msi_get_loaded_folder( package, comp->Directory );
01328             remove_folder( folder );
01329         }
01330     }
01331     msi_destroy_assembly_caches( package );
01332     return ERROR_SUCCESS;
01333 }

Generated on Fri May 25 2012 04:20:46 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.