#include "stdinc.h" #include "macros.h" #include "common.h" #include "idp.h" #include "sxsutil.h" #include "fusionbuffer.h" #include "fusionheap.h" #define FUSION_WIN32_ASSEMBLY_DUP_FILEKEY_PREFIX L"winsxs_" #define MSI_ASSEMBLYCACHE_DIRECTORY_KEYNAME L"MsiFusionWin32AssemblyCache" #define MSI_ASSEMBLYCACHE_DIRECTORY L"winsxs" #define MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY_KEYNAME L"MsiFusionWin32AssemblyManifestCache" #define MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY L"Manifests" #define MSI_ASSEMBLY_MANIFEST_COMPONENT_KEYNAME L"MsiFusionWin32AssemblyManifestComponent" #define MANIFEST_FILE_EXT L".Manifest" #define CATALOG_FILE_EXT L".cat" /////////////////////////////////////////////////////////////////////////////// // Assumption : at this moment,we asusme that DuplicateFile table EXISTS ///////////////////////////////////////////////////////////////////////////////// HRESULT CA_DuplicationWin32AssemblyFiles_Callback(const CA_ENM_ASSEMBLY_CALLBACK_INFO * info) { HRESULT hr = S_OK; CStringBuffer sbDupFileKey; CStringBuffer sbDupFileName; CSmallStringBuffer sbExt; bool fManifest = false; bool fCatalog = false; PARAMETER_CHECK_NTC(info != NULL); PARAMETER_CHECK_NTC(info->pszFileID != NULL); PARAMETER_CHECK_NTC(info->pszFileName); sbDupFileKey.Win32Assign(FUSION_WIN32_ASSEMBLY_DUP_FILEKEY_PREFIX, wcslen(FUSION_WIN32_ASSEMBLY_DUP_FILEKEY_PREFIX)); sbDupFileKey.Win32Append(info->pszFileID, wcslen(info->pszFileID)); IFFALSE_EXIT(sbDupFileName.Win32Assign(info->pszFileName, wcslen(info->pszFileName))); IFFALSE_EXIT(sbDupFileName.Win32GetPathExtension(sbExt)); // //rename manifest file and catalog file // if ((FusionpCompareStrings(sbExt, sbExt.Cch(), L"man", wcslen(L"man"), true) == 0) || (FusionpCompareStrings(sbExt, sbExt.Cch(), L"manifest", wcslen(L"manifest"), true) == 0)) { fManifest = true; } else if ((FusionpCompareStrings(sbExt, sbExt.Cch(), L"cat", wcslen(L"cat"), true) == 0) || (FusionpCompareStrings(sbExt, sbExt.Cch(), L"catalog", wcslen(L"catalog"), true) == 0)) { fCatalog = true; } if (fManifest || fCatalog) { PARAMETER_CHECK_NTC(info->pszAssemblyUniqueDir != NULL); IFFALSE_EXIT(sbDupFileName.Win32Assign(info->pszAssemblyUniqueDir, wcslen(info->pszAssemblyUniqueDir))); // reset the extension of manifest file and catalog file in order to keep the same as XP IFFALSE_EXIT(sbDupFileName.Win32Append(fManifest? MANIFEST_FILE_EXT : CATALOG_FILE_EXT, fManifest? wcslen(MANIFEST_FILE_EXT) : wcslen(CATALOG_FILE_EXT))); }else { PARAMETER_CHECK_NTC(info->pszFileName != NULL); IFFALSE_EXIT(sbDupFileName.Win32Assign(info->pszFileName, wcslen(info->pszFileName))); } // if it is a .manifest file or it is a catalog file, put it into winsxs\manifests folder, // otherwise, put it into winsxs\x86_...._12345678 IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, info->hdb, OPT_DUPLICATEFILE, NUMBER_OF_PARAM_TO_INSERT_TABLE_DUPLICATEFILE, MAKE_PCWSTR(sbDupFileKey), MAKE_PCWSTR(info->pszComponentID), MAKE_PCWSTR(info->pszFileID), MAKE_PCWSTR(sbDupFileName), (fManifest | fCatalog) ? MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY_KEYNAME : MAKE_PCWSTR(info->pszDestFolderID))); Exit: return hr; } HRESULT GetXPInstalledDirectory(const CA_ENM_ASSEMBLY_CALLBACK_INFO * info, CStringBuffer & sbAsmDir) { HRESULT hr = S_OK; WCHAR sqlbuf[CA_MAX_BUF]; PMSIHANDLE hView = NULL; PMSIHANDLE hRecord = NULL; WCHAR bufName[CA_MAX_BUF]; DWORD cchName; WCHAR bufValue[CA_MAX_BUF]; DWORD cchValue; BOOL fWin32, fWin32Policy; CStringBuffer sbPathBuffer; UINT iRet; ASSEMBLY_IDENTITY_ATTRIBUTE Attribute; PASSEMBLY_IDENTITY AssemblyIdentity = NULL; PARAMETER_CHECK_NTC(info != NULL); PARAMETER_CHECK_NTC(info->pszComponentID != NULL); PARAMETER_CHECK_NTC(info->hdb != NULL); IFFALSE_EXIT(::SxsCreateAssemblyIdentity(0, ASSEMBLY_IDENTITY_TYPE_DEFINITION, &AssemblyIdentity, 0, NULL)); swprintf(sqlbuf, L"SELECT Name, Value FROM MsiAssemblyName WHERE Component_='%s'", info->pszComponentID); IF_NOTSUCCESS_SET_HRERR_EXIT(::MsiDatabaseOpenViewW(info->hdb, sqlbuf, &hView)); IF_NOTSUCCESS_SET_HRERR_EXIT(::MsiViewExecute(hView, 0)); for (;;) { // // for each entry in MsiAssembly Table // iRet = MsiViewFetch(hView, &hRecord); if (iRet == ERROR_NO_MORE_ITEMS) break; if (iRet != ERROR_SUCCESS ) SET_HRERR_AND_EXIT(iRet); cchName = NUMBER_OF(bufName); cchValue = NUMBER_OF(bufValue); IF_NOTSUCCESS_SET_HRERR_EXIT(MsiRecordGetStringW(hRecord, 1, bufName, &cchName)); IF_NOTSUCCESS_SET_HRERR_EXIT(MsiRecordGetStringW(hRecord, 2, bufValue, &cchValue)); Attribute.Flags = 0; Attribute.NamespaceCch = 0; Attribute.Namespace = NULL; Attribute.NameCch = cchName; Attribute.Name = bufName; Attribute.ValueCch = cchValue; Attribute.Value = bufValue; //BUGBUG // Fusion win32 required that the attribute name is case-sensitive, so, for assembly name, // it should appear as "name" in MsiAssemblyName, however, for some historical reason, // it appears as "Name", so, we have to force it to the right thing for win32. // // for other attribute in MsiAssemblyName table, there is no such problem, // //BUGBUG if ((Attribute.NameCch == 4) && (_wcsicmp(Attribute.Name, L"name") == 0)) { Attribute.Name = L"name"; } IFFALSE_EXIT(::SxsInsertAssemblyIdentityAttribute(0, AssemblyIdentity, &Attribute)); } IFFALSE_EXIT(SxsHashAssemblyIdentity(0, AssemblyIdentity, NULL)); // // generate the path, something like x86_ms-sxstest-sfp_75e377300ab7b886_1.0.0.0_en_04f354da // IFFAILED_EXIT(ca_SxspDetermineAssemblyType(AssemblyIdentity, fWin32, fWin32Policy)); IFFAILED_EXIT(ca_SxspGenerateSxsPath( SXSP_GENERATE_SXS_PATH_FLAG_OMIT_ROOT | (fWin32Policy ? SXSP_GENERATE_SXS_PATH_FLAG_OMIT_VERSION : 0), SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY, NULL, 0, AssemblyIdentity, sbPathBuffer)); IFFALSE_EXIT(sbAsmDir.Win32Assign(sbPathBuffer)); hr = S_OK; Exit: SxsDestroyAssemblyIdentity(AssemblyIdentity); return hr; } HRESULT IsCertainRecordExistInDirectoryTable(const MSIHANDLE & hdb, PCWSTR DirectoryKey, BOOL & fExist) { HRESULT hr = S_OK; PMSIHANDLE hRecord = NULL; PMSIHANDLE hView = NULL; WCHAR sqlBuf[MAX_PATH]; fExist = FALSE; swprintf(sqlBuf, ca_sqlQuery[CA_SQL_QUERY_DIRECTORY], DirectoryKey); IF_NOTSUCCESS_SET_HRERR_EXIT(::MsiDatabaseOpenViewW(hdb, sqlBuf, &hView)); IF_NOTSUCCESS_SET_HRERR_EXIT(::MsiViewExecute(hView, 0)); UINT iRet = MsiViewFetch(hView, &hRecord); if (iRet == ERROR_SUCCESS) { fExist = TRUE; } else if (iRet == ERROR_NO_MORE_ITEMS) { fExist = FALSE; } else SET_HRERR_AND_EXIT(iRet); hr = S_OK; Exit: return hr; } // // add entry to Directory Table and CreateFolder Table // HRESULT AddFusionAssemblyDirectories(const CA_ENM_ASSEMBLY_CALLBACK_INFO * info, CStringBuffer & sbDestFolderID) { HRESULT hr = S_OK; BOOL fRecordExist = FALSE; PARAMETER_CHECK_NTC(info != NULL); PARAMETER_CHECK_NTC(info->pszAssemblyUniqueDir != NULL); PARAMETER_CHECK_NTC(info->hdb != NULL); PARAMETER_CHECK_NTC(info->pszComponentID != NULL); // // TODO: here we could make the DirectoryID more unique // IFFALSE_EXIT(sbDestFolderID.Win32Assign(info->pszAssemblyUniqueDir, wcslen(info->pszAssemblyUniqueDir))); IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, info->hdb, OPT_DIRECTORY, NUMBER_OF_PARAM_TO_INSERT_TABLE_DIRECTORY, MAKE_PCWSTR(sbDestFolderID), MAKE_PCWSTR(MSI_ASSEMBLYCACHE_DIRECTORY_KEYNAME), MAKE_PCWSTR(info->pszAssemblyUniqueDir))); // // insert entry to CreateFolder Table // IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, info->hdb, OPT_CREATEFOLDER, NUMBER_OF_PARAM_TO_INSERT_TABLE_CREATEFOLDER, MAKE_PCWSTR(sbDestFolderID), MAKE_PCWSTR(info->pszComponentID))); hr = S_OK; Exit: return hr; } HRESULT CheckWhetherUserWantMigrate(const CA_ENM_ASSEMBLY_CALLBACK_INFO * info, BOOL & fMigrateDenied) { HRESULT hr = S_OK; WCHAR pwszSQL[MAX_PATH]; PMSIHANDLE hView = NULL; PMSIHANDLE hRecord = NULL; UINT err, iRet; fMigrateDenied = FALSE; // NTRAID#NTBUG9 - 589779 - 2002/03/26 - xiaoyuw // should be replaced with _snwprintf swprintf(pwszSQL, L"SELECT `fMigrate` FROM `%s` WHERE `Component_` = '%s'", WIN32_ASSEMBLY_MIGRATE_TABLE, info->pszComponentID); IF_NOTSUCCESS_SET_HRERR_EXIT(::MsiDatabaseOpenViewW(info->hdb, pwszSQL, &hView)); IF_NOTSUCCESS_SET_HRERR_EXIT(::MsiViewExecute(hView, NULL)); err = ::MsiViewFetch(hView, &hRecord); switch (err) { case ERROR_NO_MORE_ITEMS: // not exist in the table, default is migrate-enabled break; case ERROR_SUCCESS: iRet = MsiRecordGetInteger(hRecord, 1); if ( iRet == 0 ) fMigrateDenied = TRUE; break; default: SET_HRERR_AND_EXIT(err); } Exit: return hr; } // // CA for Fusion Win32 Assembly installation on downlevel(only) : // (1)set entries for each assembly file in DuplicateFile Table // (2)after all done, set a RegKey so this would not be done everytime // HRESULT __stdcall CA_DuplicationWin32Assembly_Callback(CA_ENM_ASSEMBLY_CALLBACK_INFO * info) { HRESULT hr = S_OK; CStringBuffer sbDestFolder; CStringBuffer sbDestFolderID; BOOL fExist = FALSE; BOOL fMigrateDenied = FALSE; PARAMETER_CHECK_NTC((info->dwFlags == 0) ||(info->dwFlags == CA_ENM_ASSEMBLY_CALLBACK_INFO_FLAG_IGNORE_MIGRATE_DENY_CHECK)); PARAMETER_CHECK_NTC(info->hInstall != NULL); PARAMETER_CHECK_NTC(info->pszComponentID != NULL); PARAMETER_CHECK_NTC(info->pszManifestFileID != NULL); if (! (info->dwFlags & CA_ENM_ASSEMBLY_CALLBACK_INFO_FLAG_IGNORE_MIGRATE_DENY_CHECK)) { IFFAILED_EXIT(CheckWhetherUserWantMigrate(info, fMigrateDenied)); if (fMigrateDenied) goto Exit; } // // get sxs component directory in the format of x86_name_publicKeyToken_1.0.0.0_en_hashvalue // IFFAILED_EXIT(GetXPInstalledDirectory(info, sbDestFolder)); info->pszAssemblyUniqueDir = sbDestFolder; // // Create an entry for this dir in Directory Table, return DirectoryID in Directory Table // IFFAILED_EXIT(AddFusionAssemblyDirectories(info, sbDestFolderID)); info->pszDestFolderID = sbDestFolderID; IFFAILED_EXIT(MSI_EnumComponentFiles(info, CA_DuplicationWin32AssemblyFiles_Callback)); Exit: return hr; } HRESULT __stdcall CustomAction_CopyFusionWin32AsmIntoAsmCache(MSIHANDLE hInstall) { HRESULT hr = S_OK; BOOL fExist; MSIHANDLE hdb = NULL; #if DBG MessageBoxA(NULL, "Enjoy the Debug", "ca_dup", MB_OK); #endif // Before enumerate all the assemblies, do common work right here .... // // (1) insert MsiAsmcache into Directory Table if not present // hdb = MsiGetActiveDatabase(hInstall); INTERNAL_ERROR_CHECK_NTC(hdb != NULL); IFFAILED_EXIT(IsCertainRecordExistInDirectoryTable(hdb, L"WindowsFolder", fExist)); if (fExist == FALSE) { IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, hdb, OPT_DIRECTORY, NUMBER_OF_PARAM_TO_INSERT_TABLE_DIRECTORY, MAKE_PCWSTR(L"WindowsFolder"), MAKE_PCWSTR(L"TARGETDIR"), MAKE_PCWSTR("."))); } // adding winsxs into Directory Table IFFAILED_EXIT(IsCertainRecordExistInDirectoryTable(hdb, MSI_ASSEMBLYCACHE_DIRECTORY_KEYNAME, fExist)); if (fExist == FALSE) { IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, hdb, OPT_DIRECTORY, NUMBER_OF_PARAM_TO_INSERT_TABLE_DIRECTORY, MAKE_PCWSTR(MSI_ASSEMBLYCACHE_DIRECTORY_KEYNAME), MAKE_PCWSTR(L"WindowsFolder"), MAKE_PCWSTR(MSI_ASSEMBLYCACHE_DIRECTORY))); } // adding winsxs\manifests into Directory table IFFAILED_EXIT(IsCertainRecordExistInDirectoryTable(hdb, MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY_KEYNAME, fExist)); if (fExist == FALSE) { IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, hdb, OPT_DIRECTORY, NUMBER_OF_PARAM_TO_INSERT_TABLE_DIRECTORY, MAKE_PCWSTR(MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY_KEYNAME), MAKE_PCWSTR(MSI_ASSEMBLYCACHE_DIRECTORY_KEYNAME), MAKE_PCWSTR(MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY))); } // create a component associated with this Directory too IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, hdb, OPT_COMPONENT, NUMBER_OF_PARAM_TO_INSERT_TABLE_COMPONENT, MSI_ASSEMBLY_MANIFEST_COMPONENT_KEYNAME, MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY_KEYNAME)); //create the folder : %windir%\winsxs\manifest IFFAILED_EXIT(ExecuteInsertTableSQL(TEMPORARY_DB_OPT, hdb, OPT_CREATEFOLDER, NUMBER_OF_PARAM_TO_INSERT_TABLE_CREATEFOLDER, MSI_ASSEMBLY_MANIFEST_CACHE_DIRECTORY_KEYNAME, MSI_ASSEMBLY_MANIFEST_COMPONENT_KEYNAME)); // //Enumerate all msi assembly in MsiAssemblyTable // IFFAILED_EXIT(MSI_EnumWinFuseAssembly(ENUM_ASSEMBLY_FLAG_CHECK_ASSEMBLY_ONLY, hInstall, CA_DuplicationWin32Assembly_Callback)); Exit: if (hdb) MsiCloseHandle(hdb); return hr; } BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { if (fdwReason == DLL_PROCESS_ATTACH) { FusionpInitializeHeap(NULL); } else if (fdwReason == DLL_PROCESS_DETACH) { // NTRAID#NTBUG9 - 589779 - 2002/03/26 - xiaoyuw // FusionpUninitializeHeap should be called when dll is detached FusionpUninitializeHeap(); } return TRUE; }