Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1101 lines
33 KiB

  1. #include "stdinc.h"
  2. #include "xmlparser.hxx"
  3. #include "FusionEventLog.h"
  4. #include "hashfile.h"
  5. #include "CAssemblyRecoveryInfo.h"
  6. #include "recover.h"
  7. #include "sxsprotect.h"
  8. #include "fusionheap.h"
  9. #include "fusionparser.h"
  10. #include "protectionui.h"
  11. #include "parsing.h"
  12. #include "msi.h"
  13. #define WINSXS_INSTALLATION_INFO_REGKEY ( ASSEMBLY_REGISTRY_ROOT L"\\Installations")
  14. class CSetErrorMode
  15. {
  16. public:
  17. CSetErrorMode(UINT uMode) { m_uiPreviousMode = ::SetErrorMode(uMode); }
  18. ~CSetErrorMode() { ::SetErrorMode(m_uiPreviousMode); }
  19. private:
  20. UINT m_uiPreviousMode;
  21. CSetErrorMode();
  22. CSetErrorMode(const CSetErrorMode &r);
  23. void operator =(const CSetErrorMode &r);
  24. };
  25. BOOL
  26. SxspOpenAssemblyInstallationKey(
  27. DWORD dwFlags,
  28. DWORD dwAccess,
  29. CRegKey &rhkAssemblyInstallation
  30. )
  31. {
  32. FN_PROLOG_WIN32
  33. rhkAssemblyInstallation = CRegKey::GetInvalidValue();
  34. PARAMETER_CHECK(dwFlags == 0);
  35. IFREGFAILED_ORIGINATE_AND_EXIT(
  36. ::RegCreateKeyExW(
  37. HKEY_LOCAL_MACHINE,
  38. WINSXS_INSTALLATION_INFO_REGKEY,
  39. 0, NULL,
  40. 0,
  41. dwAccess | FUSIONP_KEY_WOW64_64KEY,
  42. NULL,
  43. &rhkAssemblyInstallation ,
  44. NULL));
  45. FN_EPILOG
  46. }
  47. BOOL
  48. SxspSaveAssemblyRegistryData(
  49. DWORD Flags,
  50. IN PCASSEMBLY_IDENTITY pcAssemblyIdentity
  51. )
  52. {
  53. FN_PROLOG_WIN32
  54. CFusionRegKey hkAllInstallInfo;
  55. CFusionRegKey hkAssemblyKey;
  56. CSmallStringBuffer buffRegKeyName;
  57. CStringBuffer buffTargetFile;
  58. BOOL fTempFlag;
  59. CTokenPrivilegeAdjuster Adjuster;
  60. PARAMETER_CHECK(Flags == 0);
  61. IFW32FALSE_EXIT(Adjuster.Initialize());
  62. IFW32FALSE_EXIT(Adjuster.AddPrivilege(L"SeBackupPrivilege"));
  63. IFW32FALSE_EXIT(Adjuster.EnablePrivileges());
  64. IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(0, KEY_ALL_ACCESS, hkAllInstallInfo));
  65. IFW32FALSE_EXIT(SxspGenerateAssemblyNameInRegistry(pcAssemblyIdentity, buffRegKeyName));
  66. //
  67. // Generate the path of this target file. This could move into SxspGenerateSxsPath,
  68. // but that's really gross looking and error prone... the last thing I want to do
  69. // is break it..
  70. //
  71. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffTargetFile));
  72. IFW32FALSE_EXIT(buffTargetFile.Win32AppendPathElement(
  73. REGISTRY_BACKUP_ROOT_DIRECTORY_NAME,
  74. REGISTRY_BACKUP_ROOT_DIRECTORY_NAME_CCH ));
  75. //
  76. // Ensure the target path is there, don't fail if the path exists
  77. //
  78. IFW32FALSE_EXIT_UNLESS2( CreateDirectoryW(buffTargetFile, NULL),
  79. { ERROR_ALREADY_EXISTS },
  80. fTempFlag );
  81. IFW32FALSE_EXIT(buffTargetFile.Win32AppendPathElement(buffRegKeyName));
  82. //
  83. // Now open the target subkey
  84. //
  85. IFW32FALSE_EXIT(hkAllInstallInfo.OpenSubKey(
  86. hkAssemblyKey,
  87. buffRegKeyName,
  88. KEY_ALL_ACCESS,
  89. REG_OPTION_BACKUP_RESTORE ) );
  90. //
  91. // Ensure the target path is there
  92. //
  93. //
  94. // And blort it out
  95. //
  96. DeleteFileW(buffTargetFile);
  97. IFW32FALSE_EXIT(hkAssemblyKey.Save(buffTargetFile));
  98. IFW32FALSE_EXIT(Adjuster.DisablePrivileges());
  99. FN_EPILOG
  100. }
  101. //
  102. // BUGBUG: The BBT folk need the 'codebase' key to be at the top level.
  103. // Why we're shipping metadata that's only required for an internal
  104. // build tool is beyond my meager understanding.
  105. // - jonwis 07/11/2002
  106. //
  107. #define SXS_BBT_REG_HACK (TRUE)
  108. BOOL
  109. SxspAddAssemblyInstallationInfo(
  110. DWORD dwFlags,
  111. IN CAssemblyRecoveryInfo& AssemblyInfo,
  112. IN const CCodebaseInformation& rcCodebase
  113. )
  114. /*++
  115. Called by SxsInstallAssemblyW to add the codebase and prompt information to the
  116. registry for future use with SxspGetAssemblyInstallationInfoW.
  117. --*/
  118. {
  119. FN_PROLOG_WIN32
  120. CFusionRegKey hkAllInstallationInfo;
  121. CFusionRegKey hkSingleAssemblyInfo;
  122. CStringBuffer buffRegKeyName;
  123. const CSecurityMetaData &rcsmdAssemblySecurityData = AssemblyInfo.GetSecurityInformation();
  124. const CBaseStringBuffer &rcbuffAssemblyIdentity = rcsmdAssemblySecurityData.GetTextualIdentity();
  125. DWORD dwDisposition;
  126. ::FusionpDbgPrintEx(
  127. FUSION_DBG_LEVEL_INSTALLATION,
  128. "SXS: %s - starting\n", __FUNCTION__);
  129. PARAMETER_CHECK((dwFlags & ~(SXSP_ADD_ASSEMBLY_INSTALLATION_INFO_FLAG_REFRESH)) == 0);
  130. //
  131. // Create or open the top-level key - take our name and append the
  132. // key to it.
  133. //
  134. IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(0, KEY_CREATE_SUB_KEY, hkAllInstallationInfo));
  135. //
  136. // Convert back to an identity so we can figure out where to install this data to
  137. //
  138. IFW32FALSE_EXIT(::SxspGenerateAssemblyNameInRegistry(rcbuffAssemblyIdentity, buffRegKeyName));
  139. IFW32FALSE_EXIT(
  140. hkAllInstallationInfo.OpenOrCreateSubKey(
  141. hkSingleAssemblyInfo,
  142. buffRegKeyName,
  143. KEY_WRITE | KEY_READ | FUSIONP_KEY_WOW64_64KEY,
  144. 0,
  145. &dwDisposition));
  146. ULONG WriteRegFlags;
  147. WriteRegFlags = 0;
  148. if (dwFlags & SXSP_ADD_ASSEMBLY_INSTALLATION_INFO_FLAG_REFRESH)
  149. {
  150. WriteRegFlags |= SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_TO_REGISTRY_KEY_FLAG_REFRESH;
  151. #if DBG
  152. ::FusionpDbgPrintEx(
  153. FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INSTALLATION,
  154. "SXS.DLL: %s - propping recovery flag to WritePrimaryAssemblyInfoToRegistryKey\n",
  155. __FUNCTION__);
  156. #endif
  157. }
  158. IFW32FALSE_EXIT(AssemblyInfo.PrepareForWriting());
  159. IFW32FALSE_EXIT(AssemblyInfo.WritePrimaryAssemblyInfoToRegistryKey(WriteRegFlags, hkSingleAssemblyInfo));
  160. IFW32FALSE_EXIT(AssemblyInfo.WriteSecondaryAssemblyInfoIntoRegistryKey( hkSingleAssemblyInfo ) );
  161. //
  162. // If we got this far, then we've got all the right moves.
  163. //
  164. //
  165. // Are we still being broken for BBT? If so, then write the codebase generated for this
  166. // installation back into the "Codebase" value of the single-assembly-info key. This
  167. // ensures last-installer-wins semantic.
  168. //
  169. #if SXS_BBT_REG_HACK
  170. if ((dwFlags & SXSP_ADD_ASSEMBLY_INSTALLATION_INFO_FLAG_REFRESH) == 0)
  171. {
  172. IFW32FALSE_EXIT(hkSingleAssemblyInfo.SetValue(
  173. CSMD_TOPLEVEL_CODEBASE,
  174. rcCodebase.GetCodebase()));
  175. }
  176. else
  177. {
  178. #if DBG
  179. ::FusionpDbgPrintEx(
  180. FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INSTALLATION,
  181. "SXS.DLL: %s - refresh/wfp/sfc not writing top level codebase\n",
  182. __FUNCTION__);
  183. #endif
  184. }
  185. #endif
  186. FN_EPILOG
  187. }
  188. BOOL
  189. SxspLookForCDROMLocalPathForURL(
  190. IN const CBaseStringBuffer &rbuffURL,
  191. OUT CBaseStringBuffer &rbuffLocalPath
  192. )
  193. {
  194. FN_PROLOG_WIN32
  195. BOOL fFoundMedia = FALSE;
  196. CSmallStringBuffer sbIdentData1, sbIdentData2;
  197. CSmallStringBuffer buffDriveStrings;
  198. CSmallStringBuffer buffTemp;
  199. CStringBufferAccessor acc;
  200. SIZE_T HeadLength = 0;
  201. PCWSTR wcsCursor = NULL;
  202. ULONG ulSerialNumber = 0;
  203. WCHAR rgchVolumeName[MAX_PATH];
  204. SIZE_T i;
  205. PCWSTR pszSource = rbuffURL;
  206. SIZE_T cchTemp;
  207. enum CDRomSearchType
  208. {
  209. CDRST_Tagfile,
  210. CDRST_SerialNumber,
  211. CDRST_VolumeName
  212. } SearchType;
  213. rbuffLocalPath.Clear();
  214. #define ENTRY(_x, _st) { _x, NUMBER_OF(_x) - 1, _st },
  215. const static struct
  216. {
  217. PCWSTR pszPrefix;
  218. SIZE_T cchPrefix;
  219. CDRomSearchType SearchType;
  220. } s_rgMap[] =
  221. {
  222. ENTRY(L"tagfile", CDRST_Tagfile)
  223. ENTRY(L"serialnumber", CDRST_SerialNumber)
  224. ENTRY(L"volumename", CDRST_VolumeName)
  225. };
  226. #undef ENTRY
  227. SearchType = CDRST_Tagfile; // arbitrary initialization to make compiler happy about init only
  228. // occurring in the for loop
  229. for (i=0; i<NUMBER_OF(s_rgMap); i++)
  230. {
  231. if (::_wcsnicmp(s_rgMap[i].pszPrefix, rbuffURL, s_rgMap[i].cchPrefix) == 0)
  232. {
  233. HeadLength = s_rgMap[i].cchPrefix;
  234. SearchType = s_rgMap[i].SearchType;
  235. break;
  236. }
  237. }
  238. // If it wasn't in the map, it's a bogus cdrom: url so we just skip it.
  239. if (i == NUMBER_OF(s_rgMap))
  240. {
  241. #if DBG
  242. ::FusionpDbgPrintEx(
  243. FUSION_DBG_LEVEL_WFP,
  244. "SXS.DLL: %s() - no prefix found, skipping CDROM Drive %ls\n",
  245. __FUNCTION__,
  246. static_cast<PCWSTR>(rbuffURL));
  247. #endif
  248. FN_SUCCESSFUL_EXIT();
  249. }
  250. //
  251. // Get the type of identifier here, and then move the cursor past them and
  252. // the slashes in the url.
  253. //
  254. pszSource += HeadLength;
  255. pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
  256. //
  257. // Spin past slashes, assign chunklets
  258. //
  259. IFW32FALSE_EXIT(sbIdentData1.Win32Assign(pszSource, wcscspn(pszSource, CUnicodeCharTraits::PathSeparators())));
  260. pszSource += sbIdentData1.Cch();
  261. pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
  262. //
  263. // If this is a tagfile, also get another blobbet of data off the string
  264. //
  265. if (SearchType == CDRST_Tagfile)
  266. {
  267. IFW32FALSE_EXIT(sbIdentData2.Win32Assign(pszSource, wcscspn(pszSource, CUnicodeCharTraits::PathSeparators())));
  268. pszSource += sbIdentData2.Cch();
  269. pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
  270. }
  271. else if (SearchType == CDRST_SerialNumber)
  272. {
  273. IFW32FALSE_EXIT(CFusionParser::ParseULONG(
  274. ulSerialNumber,
  275. sbIdentData1,
  276. sbIdentData1.Cch(),
  277. 16));
  278. }
  279. // Find the CDROM drives...
  280. IFW32ZERO_ORIGINATE_AND_EXIT(cchTemp = ::GetLogicalDriveStringsW(0, NULL));
  281. IFW32FALSE_EXIT(buffDriveStrings.Win32ResizeBuffer(cchTemp + 1, eDoNotPreserveBufferContents));
  282. acc.Attach(&buffDriveStrings);
  283. IFW32ZERO_ORIGINATE_AND_EXIT(
  284. ::GetLogicalDriveStringsW(
  285. acc.GetBufferCchAsDWORD(),
  286. acc));
  287. acc.Detach();
  288. wcsCursor = buffDriveStrings;
  289. //
  290. // Look at all the found drive letters
  291. //
  292. while ((wcsCursor != NULL) &&
  293. (wcsCursor[0] != L'\0') &&
  294. !fFoundMedia)
  295. {
  296. DWORD dwSerialNumber;
  297. const UINT uiDriveType = ::GetDriveTypeW(wcsCursor);
  298. if (uiDriveType != DRIVE_CDROM)
  299. {
  300. wcsCursor += (::wcslen(wcsCursor) + 1);
  301. continue;
  302. }
  303. CSetErrorMode sem(SEM_FAILCRITICALERRORS);
  304. bool fNotReady;
  305. IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS2(
  306. ::GetVolumeInformationW(
  307. wcsCursor,
  308. rgchVolumeName,
  309. NUMBER_OF(rgchVolumeName),
  310. &dwSerialNumber,
  311. NULL,
  312. NULL,
  313. NULL,
  314. 0),
  315. LIST_2(ERROR_NOT_READY, ERROR_CRC),
  316. fNotReady);
  317. if (fNotReady)
  318. {
  319. ::FusionpDbgPrintEx(
  320. FUSION_DBG_LEVEL_WFP,
  321. "SXS.DLL: %s() - CDROM Drive %ls has no media present or had read errors; skipping\n",
  322. __FUNCTION__,
  323. wcsCursor);
  324. // skip past this drive
  325. wcsCursor += (::wcslen(wcsCursor) + 1);
  326. continue;
  327. }
  328. switch (SearchType)
  329. {
  330. case CDRST_Tagfile:
  331. {
  332. CFusionFile FileHandle;
  333. CStringBufferAccessor acc;
  334. DWORD dwTextLength;
  335. bool fNoFile;
  336. CHAR rgchBuffer[32];
  337. IFW32FALSE_EXIT_UNLESS2(
  338. FileHandle.Win32CreateFile(
  339. sbIdentData1,
  340. GENERIC_READ,
  341. FILE_SHARE_READ,
  342. OPEN_EXISTING),
  343. LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_NOT_READY),
  344. fNoFile);
  345. if (fNoFile)
  346. {
  347. ::FusionpDbgPrintEx(
  348. FUSION_DBG_LEVEL_WFP,
  349. "SXS.DLL: %s() - CDROM Drive %ls could not open tag file \"%ls\"; skipping\n",
  350. __FUNCTION__,
  351. wcsCursor,
  352. static_cast<PCWSTR>(sbIdentData1));
  353. // skip past this drive
  354. wcsCursor += (::wcslen(wcsCursor) + 1);
  355. continue;
  356. }
  357. buffTemp.Clear();
  358. for (;;)
  359. {
  360. IFW32FALSE_ORIGINATE_AND_EXIT(
  361. ::ReadFile(
  362. FileHandle,
  363. rgchBuffer,
  364. sizeof(rgchBuffer),
  365. &dwTextLength,
  366. NULL));
  367. IFW32FALSE_EXIT(buffTemp.Win32Append(rgchBuffer, dwTextLength));
  368. if ((dwTextLength != sizeof(rgchBuffer)) ||
  369. (buffTemp.Cch() > sbIdentData2.Cch()))
  370. break;
  371. }
  372. fFoundMedia = (::FusionpCompareStrings(buffTemp, sbIdentData2, true) == 0);
  373. break;
  374. }
  375. case CDRST_SerialNumber:
  376. fFoundMedia = (dwSerialNumber == ulSerialNumber);
  377. break;
  378. case CDRST_VolumeName:
  379. fFoundMedia = (::FusionpCompareStrings(rgchVolumeName, ::wcslen(rgchVolumeName), sbIdentData1, true) == 0);
  380. break;
  381. default:
  382. INTERNAL_ERROR_CHECK(false);
  383. break;
  384. }
  385. if (!fFoundMedia)
  386. wcsCursor += ::wcslen(wcsCursor) + 1;
  387. }
  388. if (fFoundMedia)
  389. {
  390. IFW32FALSE_EXIT(buffTemp.Win32Assign(wcsCursor, ::wcslen(wcsCursor)));
  391. IFW32FALSE_EXIT(buffTemp.Win32AppendPathElement(pszSource, ::wcslen(pszSource)));
  392. IFW32FALSE_EXIT(rbuffLocalPath.Win32Assign(buffTemp));
  393. }
  394. FN_EPILOG
  395. }
  396. BOOL
  397. SxspResolveWinSourceMediaURL(
  398. const CBaseStringBuffer &rbuffCodebaseInfo,
  399. CBaseStringBuffer &rbuffLocalPath
  400. )
  401. {
  402. FN_PROLOG_WIN32
  403. CSmallStringBuffer buffWindowsInstallSource;
  404. CSmallStringBuffer buffLocalPathTemp;
  405. #if DBG
  406. CSmallStringBuffer buffLocalPathCodebasePrefix;
  407. #endif
  408. DWORD dwWin32Error;
  409. DWORD dwFileAttributes;
  410. const static PCWSTR AssemblySourceStrings[] = {
  411. WINSXS_INSTALL_SVCPACK_REGKEY,
  412. WINSXS_INSTALL_SOURCEPATH_REGKEY
  413. };
  414. SIZE_T iWhichSource = 0;
  415. bool fFoundCodebase = false;
  416. CFusionRegKey hkSetupInfo;
  417. DWORD dwWasFromCDRom = 0;
  418. rbuffLocalPath.Clear();
  419. IFREGFAILED_ORIGINATE_AND_EXIT(
  420. ::RegOpenKeyExW(
  421. HKEY_LOCAL_MACHINE,
  422. WINSXS_INSTALL_SOURCE_BASEDIR,
  423. 0,
  424. KEY_READ | FUSIONP_KEY_WOW64_64KEY,
  425. &hkSetupInfo));
  426. if (!::FusionpRegQueryDwordValueEx(
  427. 0,
  428. hkSetupInfo,
  429. WINSXS_INSTALL_SOURCE_IS_CDROM,
  430. &dwWasFromCDRom))
  431. {
  432. dwWasFromCDRom = 0;
  433. }
  434. for (iWhichSource = 0; (!fFoundCodebase) && iWhichSource < NUMBER_OF(AssemblySourceStrings); iWhichSource++)
  435. {
  436. IFW32FALSE_EXIT(
  437. ::FusionpRegQuerySzValueEx(
  438. FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING,
  439. hkSetupInfo,
  440. AssemblySourceStrings[iWhichSource],
  441. buffWindowsInstallSource));
  442. //
  443. // This really _really_ should not be empty. If it is, then someone
  444. // went and fiddled with the registry on us.
  445. //
  446. if (buffWindowsInstallSource.Cch() == 0)
  447. {
  448. ::FusionpDbgPrintEx(
  449. FUSION_DBG_LEVEL_WFP,
  450. "SXS: %s - skipping use of source string \"%ls\" in registry because either missing or null value\n",
  451. __FUNCTION__,
  452. AssemblySourceStrings[iWhichSource]);
  453. continue;
  454. }
  455. ::FusionpDbgPrintEx(
  456. FUSION_DBG_LEVEL_WFP,
  457. "SXS: %s - WFP probing windows source location \"%ls\"\n",
  458. __FUNCTION__,
  459. static_cast<PCWSTR>(buffWindowsInstallSource));
  460. //
  461. // If this was from a CD, then spin through the list of CD's in the system
  462. // and see if we can match the codebase against the root dir of the CD
  463. //
  464. if (dwWasFromCDRom)
  465. {
  466. CSmallStringBuffer buffDriveStrings;
  467. CStringBufferAccessor acc;
  468. PCWSTR pszCursor;
  469. SIZE_T cchTemp;
  470. IFW32ZERO_EXIT(cchTemp = ::GetLogicalDriveStringsW(0, NULL));
  471. IFW32FALSE_EXIT(buffDriveStrings.Win32ResizeBuffer(cchTemp + 1, eDoNotPreserveBufferContents));
  472. acc.Attach(&buffDriveStrings);
  473. IFW32ZERO_EXIT(
  474. ::GetLogicalDriveStringsW(
  475. acc.GetBufferCchAsDWORD(),
  476. acc.GetBufferPtr()));
  477. acc.Detach();
  478. pszCursor = buffDriveStrings;
  479. while (pszCursor[0] != L'\0')
  480. {
  481. if (::GetDriveTypeW(pszCursor) == DRIVE_CDROM)
  482. {
  483. ::FusionpDbgPrintEx(
  484. FUSION_DBG_LEVEL_WFP,
  485. "SXS: %s - Scanning CDROM drive \"%ls\" for windows source media\n",
  486. __FUNCTION__,
  487. pszCursor);
  488. IFW32FALSE_EXIT(buffLocalPathTemp.Win32Assign(pszCursor, ::wcslen(pszCursor)));
  489. IFW32FALSE_EXIT(buffLocalPathTemp.Win32AppendPathElement(rbuffCodebaseInfo));
  490. IFW32FALSE_EXIT(
  491. ::SxspGetFileAttributesW(
  492. buffLocalPathTemp,
  493. dwFileAttributes,
  494. dwWin32Error,
  495. 4,
  496. ERROR_FILE_NOT_FOUND,
  497. ERROR_PATH_NOT_FOUND,
  498. ERROR_NOT_READY,
  499. ERROR_ACCESS_DENIED));
  500. if (dwWin32Error == ERROR_SUCCESS)
  501. {
  502. #if DBG
  503. buffLocalPathCodebasePrefix.Win32Assign(pszCursor, ::wcslen(pszCursor));
  504. #endif
  505. fFoundCodebase = true;
  506. break;
  507. }
  508. ::FusionpDbgPrintEx(
  509. FUSION_DBG_LEVEL_WFP,
  510. "SXS: %s - Could not find key file \"%ls\"; moving on to next drive\n",
  511. __FUNCTION__,
  512. static_cast<PCWSTR>(buffLocalPathTemp));
  513. }
  514. pszCursor += ::wcslen(pszCursor) + 1;
  515. }
  516. if (fFoundCodebase)
  517. break;
  518. ::FusionpDbgPrintEx(
  519. FUSION_DBG_LEVEL_WFP,
  520. "SXS: %s - Could not find any CDROMs with key file \"%ls\"\n",
  521. __FUNCTION__,
  522. static_cast<PCWSTR>(rbuffCodebaseInfo));
  523. buffLocalPathTemp.Clear();
  524. }
  525. else
  526. {
  527. //
  528. // This wasn't a CD-rom installation, so prepend the install source path to
  529. // the string that was passed in.
  530. //
  531. IFW32FALSE_EXIT(buffLocalPathTemp.Win32Assign(buffWindowsInstallSource));
  532. IFW32FALSE_EXIT(buffLocalPathTemp.Win32AppendPathElement(rbuffCodebaseInfo));
  533. ::FusionpDbgPrintEx(
  534. FUSION_DBG_LEVEL_WFP,
  535. "SXS: %s - trying to access windows source file \"%ls\"\n",
  536. __FUNCTION__,
  537. static_cast<PCWSTR>(buffLocalPathTemp));
  538. IFW32FALSE_EXIT(
  539. ::SxspGetFileAttributesW(
  540. buffLocalPathTemp,
  541. dwFileAttributes,
  542. dwWin32Error,
  543. 4,
  544. ERROR_FILE_NOT_FOUND,
  545. ERROR_PATH_NOT_FOUND,
  546. ERROR_NOT_READY,
  547. ERROR_ACCESS_DENIED));
  548. if (dwWin32Error == ERROR_SUCCESS)
  549. {
  550. #if DBG
  551. buffLocalPathCodebasePrefix.Win32Assign(buffWindowsInstallSource);
  552. #endif
  553. fFoundCodebase = true;
  554. break;
  555. }
  556. ::FusionpDbgPrintEx(
  557. FUSION_DBG_LEVEL_WFP,
  558. "SXS: %s - Unable to find key file \"%ls\"; win32 status = %lu\n",
  559. __FUNCTION__,
  560. static_cast<PCWSTR>(buffLocalPathTemp),
  561. dwWin32Error);
  562. buffLocalPathTemp.Clear();
  563. }
  564. if (fFoundCodebase)
  565. break;
  566. }
  567. IFW32FALSE_EXIT(rbuffLocalPath.Win32Assign(buffLocalPathTemp));
  568. #if DBG
  569. ::FusionpDbgPrintEx(
  570. FUSION_DBG_LEVEL_WFP,
  571. "SXS: %s - buffLocalPathCodebasePrefix \"%ls\" and returning rbuffLocalPath \"%ls\"\n",
  572. __FUNCTION__,
  573. static_cast<PCWSTR>(buffLocalPathCodebasePrefix),
  574. static_cast<PCWSTR>(rbuffLocalPath)
  575. );
  576. #endif
  577. FN_EPILOG
  578. }
  579. #define SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI (0x00000001)
  580. BOOL
  581. SxspRepeatUntilLocalPathAvailable(
  582. IN ULONG Flags,
  583. IN const CAssemblyRecoveryInfo &rRecoveryInfo,
  584. IN const CCodebaseInformation *pCodeBaseIn,
  585. IN SxsWFPResolveCodebase CodebaseType,
  586. IN const CBaseStringBuffer &rbuffCodebaseInfo,
  587. OUT CBaseStringBuffer &rbuffLocalPath,
  588. OUT BOOL &fRetryPressed
  589. )
  590. {
  591. BOOL fSuccess = FALSE;
  592. FN_TRACE_WIN32(fSuccess);
  593. BOOL fCodebaseOk = FALSE;
  594. CSmallStringBuffer buffFinalLocalPath;
  595. DWORD dwAttributes;
  596. PARAMETER_CHECK(pCodeBaseIn != NULL);
  597. ::FusionpDbgPrintEx(
  598. FUSION_DBG_LEVEL_WFP,
  599. "SXS: %s - got codebase \"%ls\"\n",
  600. __FUNCTION__,
  601. static_cast<PCWSTR>(pCodeBaseIn->GetCodebase()));
  602. rbuffLocalPath.Clear();
  603. fRetryPressed = FALSE;
  604. PARAMETER_CHECK(
  605. (CodebaseType == CODEBASE_RESOLVED_URLHEAD_FILE) ||
  606. (CodebaseType == CODEBASE_RESOLVED_URLHEAD_WINSOURCE) ||
  607. (CodebaseType == CODEBASE_RESOLVED_URLHEAD_CDROM));
  608. PARAMETER_CHECK((Flags & ~(SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI)) == 0);
  609. #if DBG
  610. ::FusionpDbgPrintEx(
  611. FUSION_DBG_LEVEL_WFP,
  612. "SXS: %s() CodebaseType : %s (0x%lx)\n",
  613. __FUNCTION__,
  614. (CodebaseType == CODEBASE_RESOLVED_URLHEAD_FILE) ? "file"
  615. : (CodebaseType == CODEBASE_RESOLVED_URLHEAD_WINSOURCE) ? "winsource"
  616. : (CodebaseType == CODEBASE_RESOLVED_URLHEAD_CDROM) ? "cdrom"
  617. : "",
  618. static_cast<ULONG>(CodebaseType)
  619. );
  620. ::FusionpDbgPrintEx(
  621. FUSION_DBG_LEVEL_WFP,
  622. "SXS: %s() rbuffCodebaseInfo : %ls\n",
  623. __FUNCTION__,
  624. static_cast<PCWSTR>(rbuffCodebaseInfo)
  625. );
  626. #endif
  627. for (;;)
  628. {
  629. bool fNotFound = true;
  630. // First, let's see if we have to do any trickery.
  631. switch (CodebaseType)
  632. {
  633. case CODEBASE_RESOLVED_URLHEAD_CDROM:
  634. IFW32FALSE_EXIT(
  635. ::SxspLookForCDROMLocalPathForURL(
  636. rbuffCodebaseInfo,
  637. buffFinalLocalPath));
  638. ::FusionpDbgPrintEx(
  639. FUSION_DBG_LEVEL_WFP,
  640. "SXS: %s - cdrom: URL resolved to \"%ls\"\n",
  641. __FUNCTION__,
  642. static_cast<PCWSTR>(buffFinalLocalPath));
  643. break;
  644. case CODEBASE_RESOLVED_URLHEAD_WINSOURCE:
  645. IFW32FALSE_EXIT(
  646. ::SxspResolveWinSourceMediaURL(
  647. rbuffCodebaseInfo,
  648. buffFinalLocalPath));
  649. ::FusionpDbgPrintEx(
  650. FUSION_DBG_LEVEL_WFP,
  651. "SXS: %s - windows source URL resolved to \"%ls\"\n",
  652. __FUNCTION__,
  653. static_cast<PCWSTR>(buffFinalLocalPath));
  654. break;
  655. case CODEBASE_RESOLVED_URLHEAD_FILE:
  656. IFW32FALSE_EXIT(buffFinalLocalPath.Win32Assign(rbuffCodebaseInfo));
  657. ::FusionpDbgPrintEx(
  658. FUSION_DBG_LEVEL_WFP,
  659. "SXS: %s - file: URL resolved to \"%ls\"\n",
  660. __FUNCTION__,
  661. static_cast<PCWSTR>(buffFinalLocalPath));
  662. break;
  663. }
  664. if (buffFinalLocalPath.Cch() != 0)
  665. {
  666. DWORD dwWin32Error = NO_ERROR;
  667. IFW32FALSE_EXIT(
  668. ::SxspGetFileAttributesW(
  669. buffFinalLocalPath,
  670. dwAttributes,
  671. dwWin32Error,
  672. 5,
  673. ERROR_PATH_NOT_FOUND,
  674. ERROR_FILE_NOT_FOUND,
  675. ERROR_BAD_NET_NAME,
  676. ERROR_BAD_NETPATH,
  677. ERROR_ACCESS_DENIED));
  678. if (dwWin32Error == ERROR_SUCCESS)
  679. break;
  680. }
  681. if ((Flags & SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI) == 0)
  682. {
  683. buffFinalLocalPath.Clear();
  684. break;
  685. }
  686. //
  687. // Nope, didn't find it (or the codebase specified is gone. Ask the user
  688. // to insert media or whatnot so we can find it again.
  689. //
  690. if (fNotFound)
  691. {
  692. CSXSMediaPromptDialog PromptBox;
  693. CSXSMediaPromptDialog::DialogResults result;
  694. IFW32FALSE_EXIT(PromptBox.Initialize(pCodeBaseIn));
  695. IFW32FALSE_EXIT(PromptBox.ShowSelf(result));
  696. if (result == CSXSMediaPromptDialog::DialogCancelled)
  697. {
  698. ::FusionpDbgPrintEx(
  699. FUSION_DBG_LEVEL_WFP,
  700. "SXS: %s - user cancelled media prompt dialog\n",
  701. __FUNCTION__);
  702. buffFinalLocalPath.Clear();
  703. break;
  704. }
  705. // Otherwise, try again!
  706. fRetryPressed = TRUE;
  707. break;
  708. }
  709. }
  710. IFW32FALSE_EXIT(rbuffLocalPath.Win32Assign(buffFinalLocalPath));
  711. #if DBG
  712. ::FusionpDbgPrintEx(
  713. FUSION_DBG_LEVEL_WFP,
  714. "SXS: %s - returning rbuffLocalPath \"%ls\"\n",
  715. __FUNCTION__,
  716. static_cast<PCWSTR>(rbuffLocalPath)
  717. );
  718. #endif
  719. FN_EPILOG
  720. }
  721. BOOL
  722. SxspAskDarwinDoReinstall(
  723. IN PCWSTR buffLocalPath)
  724. {
  725. BOOL fSuccess = FALSE;
  726. FN_TRACE_WIN32(fSuccess);
  727. UINT (WINAPI * pfnMsiProvideAssemblyW)(
  728. LPCWSTR wzAssemblyName,
  729. LPCWSTR szAppContext,
  730. DWORD dwInstallMode,
  731. DWORD dwUnused,
  732. LPWSTR lpPathBuf,
  733. DWORD *pcchPathBuf) = NULL;
  734. INSTALLUILEVEL (WINAPI * pfnMsiSetInternalUI)(
  735. INSTALLUILEVEL dwUILevel, // UI level
  736. HWND *phWnd) // handle of owner window
  737. = NULL;
  738. INSTALLUILEVEL OldInstallUILevel;
  739. CDynamicLinkLibrary hMSIDll;
  740. //
  741. // We should hoist the load/unload out of the loop.
  742. //
  743. IFW32FALSE_ORIGINATE_AND_EXIT(hMSIDll.Win32LoadLibrary(L"msi.dll"));
  744. IFW32NULL_ORIGINATE_AND_EXIT(hMSIDll.Win32GetProcAddress("MsiProvideAssemblyW", &pfnMsiProvideAssemblyW));
  745. IFW32NULL_ORIGINATE_AND_EXIT(hMSIDll.Win32GetProcAddress("MsiSetInternalUI", &pfnMsiSetInternalUI));
  746. // No real failure from this API...
  747. OldInstallUILevel = (*pfnMsiSetInternalUI)(INSTALLUILEVEL_NONE, NULL);
  748. IFREGFAILED_ORIGINATE_AND_EXIT((*pfnMsiProvideAssemblyW)(buffLocalPath, NULL, REINSTALLMODE_FILEREPLACE, MSIASSEMBLYINFO_WIN32ASSEMBLY, NULL, NULL));
  749. // and restore it
  750. (*pfnMsiSetInternalUI)(OldInstallUILevel, NULL);
  751. fSuccess = TRUE;
  752. Exit:
  753. return fSuccess;
  754. }
  755. BOOL
  756. SxspRecoverAssembly(
  757. IN const CAssemblyRecoveryInfo &AsmRecoveryInfo,
  758. IN CRecoveryCopyQueue *pRecoveryQueue,
  759. OUT SxsRecoveryResult &rStatus
  760. )
  761. {
  762. BOOL fSuccess = FALSE;
  763. FN_TRACE_WIN32(fSuccess);
  764. CSmallStringBuffer sbPerTypeCodebaseString;
  765. SxsWFPResolveCodebase CodebaseType;
  766. SXS_INSTALLW Install = { sizeof(SXS_INSTALLW) };
  767. Install.dwFlags |= SXS_INSTALL_FLAG_REPLACE_EXISTING;
  768. bool fNotFound = false;
  769. CCodebaseInformationList::ConstIterator CodebaseIterator;
  770. const CCodebaseInformationList& CodebaseList = AsmRecoveryInfo.GetCodeBaseList();
  771. ULONG RetryNumber = 0;
  772. BOOL fRetryPressed = FALSE;
  773. ULONG RetryPressedCount = 0;
  774. rStatus = Recover_Unknown;
  775. //
  776. // As long as they hit retry, keep putting up the ui, cycling through the paths.
  777. //
  778. for (RetryNumber = 0 ; (rStatus != Recover_OK) && RetryNumber != 3 ; RetryNumber += (fRetryPressed ? 0 : 1))
  779. {
  780. for (CodebaseIterator = CodebaseList.Begin() ; (rStatus != Recover_OK) && CodebaseIterator != CodebaseList.End() ; ++CodebaseIterator)
  781. {
  782. fRetryPressed = FALSE;
  783. //
  784. // eg:
  785. // xcopy /fiver \\winbuilds\release\main\usa\latest.idw\x86fre\pro\i386 x:\blah\blah\i386
  786. //
  787. // buffLocalPath x:\blah\blah\i386\asms\1000\msft\windows\gdiplus\gdiplus.man
  788. // buffLocalPathCodebasePrefix x:\blah\blah
  789. // buffCodebaseMetaPrefix x-ms-windows://
  790. // buffCodebaseTail \i386\asms\1000\msft\windows\gdiplus\gdiplus.man.
  791. //
  792. // Install.lpCodeBaseUrl x:\blah\blah
  793. // Install.lpManifestPath x:\blah\blah\i386\asms\1000\msft\windows\gdiplus\gdiplus.man
  794. //
  795. CSmallStringBuffer buffLocalPath;
  796. CSmallStringBuffer buffCodebaseTail;
  797. ::FusionpDbgPrintEx(
  798. FUSION_DBG_LEVEL_WFP,
  799. "SXS: %s - beginning recovery of assembly directory \"%ls\"\n",
  800. __FUNCTION__,
  801. static_cast<PCWSTR>(AsmRecoveryInfo.GetAssemblyDirectoryName()));
  802. //
  803. // Go try and get the codebase resolved
  804. //
  805. rStatus = Recover_Unknown;
  806. IFW32FALSE_EXIT(
  807. ::SxspDetermineCodebaseType(
  808. // this should be cached in m_CodebaseInfo.
  809. CodebaseIterator->GetCodebase(),
  810. CodebaseType,
  811. &buffCodebaseTail));
  812. if (CodebaseType == CODEBASE_RESOLVED_URLHEAD_UNKNOWN)
  813. {
  814. ::FusionpDbgPrintEx(
  815. FUSION_DBG_LEVEL_WFP,
  816. "SXS: %s - Couldn't figure out what to do with codebase \"%ls\"; skipping\n",
  817. __FUNCTION__,
  818. static_cast<PCWSTR>(CodebaseIterator->GetCodebase()));
  819. rStatus = Recover_SourceMissing;
  820. continue;
  821. }
  822. if (!::SxspRepeatUntilLocalPathAvailable(
  823. (RetryNumber == 2 && (CodebaseIterator == (CodebaseList.Begin() + (RetryPressedCount % CodebaseList.GetSize()))))
  824. ? SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI : 0,
  825. AsmRecoveryInfo, &*CodebaseIterator, CodebaseType, buffCodebaseTail, buffLocalPath, fRetryPressed))
  826. {
  827. continue;
  828. }
  829. if (fRetryPressed)
  830. RetryPressedCount += 1;
  831. if (buffLocalPath.Cch() == 0 )
  832. {
  833. ::FusionpDbgPrintEx(
  834. FUSION_DBG_LEVEL_WFP,
  835. "SXS: %s - unable to resolve codebase \"%ls\" to a local path\n",
  836. __FUNCTION__,
  837. static_cast<PCWSTR>(CodebaseIterator->GetCodebase()));
  838. rStatus = Recover_ManifestMissing;
  839. continue;
  840. }
  841. Install.lpManifestPath = buffLocalPath;
  842. Install.dwFlags |= SXS_INSTALL_FLAG_REFRESH;
  843. IFW32FALSE_EXIT_UNLESS2(
  844. ::SxsInstallW(&Install),
  845. LIST_2(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND),
  846. fNotFound);
  847. if (fNotFound)
  848. {
  849. rStatus = Recover_ManifestMissing; // may also be a file in the assembly missing
  850. ::FusionpDbgPrintEx(
  851. FUSION_DBG_LEVEL_WFP,
  852. "SXS: %s - installation from %ls failed with win32 last error = %ld\n",
  853. __FUNCTION__,
  854. static_cast<PCWSTR>(buffLocalPath),
  855. ::FusionpGetLastWin32Error());
  856. continue;
  857. }
  858. else
  859. {
  860. rStatus = Recover_OK;
  861. break;
  862. }
  863. }
  864. //
  865. // Last chance - try MSI reinstallation
  866. //
  867. if ( rStatus != Recover_OK )
  868. {
  869. BOOL fMsiKnowsAssembly = FALSE;
  870. const CBaseStringBuffer &rcbuffIdentity = AsmRecoveryInfo.GetSecurityInformation().GetTextualIdentity();
  871. IFW32FALSE_EXIT(SxspDoesMSIStillNeedAssembly( rcbuffIdentity, fMsiKnowsAssembly));
  872. if ( fMsiKnowsAssembly && ::SxspAskDarwinDoReinstall(rcbuffIdentity))
  873. {
  874. rStatus = Recover_OK;
  875. break;
  876. }
  877. }
  878. }
  879. fSuccess = TRUE;
  880. Exit:
  881. CSxsPreserveLastError ple;
  882. //
  883. // Here we have to check something. If the assembly wasn't able to be reinstalled,
  884. // then we do the following:
  885. //
  886. // 1. Rename away old assembly directory to .old or similar
  887. // 2. Log a message to the event log
  888. //
  889. DWORD dwMessageToPrint = 0;
  890. if (rStatus != Recover_OK)
  891. {
  892. dwMessageToPrint = MSG_SXS_SFC_ASSEMBLY_RESTORE_FAILED;
  893. }
  894. else
  895. {
  896. dwMessageToPrint = MSG_SXS_SFC_ASSEMBLY_RESTORE_SUCCESS;
  897. }
  898. ::FusionpDbgPrintEx(
  899. FUSION_DBG_LEVEL_WFP,
  900. "SXS.DLL: %s: Recovery of assembly \"%ls\" resulted in fSuccess=%d rStatus=%d\n",
  901. __FUNCTION__,
  902. static_cast<PCWSTR>(AsmRecoveryInfo.GetAssemblyDirectoryName()),
  903. fSuccess,
  904. rStatus);
  905. ::FusionpLogError(
  906. dwMessageToPrint,
  907. CUnicodeString(AsmRecoveryInfo.GetAssemblyDirectoryName()));
  908. ple.Restore();
  909. return fSuccess;
  910. }