Leaked source code of windows server 2003
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.

1060 lines
33 KiB

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