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.

1261 lines
38 KiB

  1. #include "stdinc.h"
  2. #include "fusionheap.h"
  3. #include "fusionbuffer.h"
  4. #include "fusionparser.h"
  5. #include "parsing.h"
  6. #include "strongname.h"
  7. #include "CAssemblyRecoveryInfo.h"
  8. #include "hashfile.h"
  9. #include "FusionHandle.h"
  10. #include "Util.h"
  11. #include "Sxsp.h"
  12. #include "recover.h"
  13. SMARTTYPE(CAssemblyRecoveryInfo);
  14. static
  15. BOOL
  16. pMapShortNameToAssembly(
  17. IN OUT CBaseStringBuffer &rbuffAssemblyName,
  18. IN const CRegKey &hkInstallInfoKey,
  19. OUT CRegKey &hRequestedAsm,
  20. IN REGSAM rsReadRights = KEY_READ
  21. )
  22. {
  23. FN_PROLOG_WIN32
  24. CSmallStringBuffer buffManifestName;
  25. CSmallStringBuffer buffCatalogName;
  26. DWORD dwIndex = 0;
  27. PCWSTR pszValueName = NULL;
  28. hRequestedAsm = CRegKey::GetInvalidValue();
  29. IFW32FALSE_EXIT(buffManifestName.Win32Assign(rbuffAssemblyName));
  30. IFW32FALSE_EXIT(buffManifestName.Win32Append(L".man", 4));
  31. IFW32FALSE_EXIT(buffCatalogName.Win32Assign(rbuffAssemblyName));
  32. IFW32FALSE_EXIT(buffCatalogName.Win32Append(L".cat", 4));
  33. //
  34. // Look for this under the CSMD_TOPLEVEL_SHORTNAME first
  35. //
  36. for (;;)
  37. {
  38. CStringBuffer buffKeyName;
  39. CSmallStringBuffer buffAcquiredShortName;
  40. BOOL fTempBoolean;
  41. CRegKey hAsm;
  42. IFW32FALSE_EXIT(hkInstallInfoKey.EnumKey(
  43. dwIndex++,
  44. buffKeyName,
  45. NULL,
  46. &fTempBoolean));
  47. if (fTempBoolean)
  48. break;
  49. IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey(hAsm, buffKeyName, rsReadRights));
  50. //
  51. // Get the value of the key
  52. //
  53. IFW32FALSE_EXIT(
  54. ::FusionpRegQuerySzValueEx(
  55. FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING,
  56. hAsm,
  57. CSMD_TOPLEVEL_SHORTNAME,
  58. buffAcquiredShortName));
  59. //
  60. // If the key was there to be read:
  61. //
  62. if (buffAcquiredShortName.Cch() != 0)
  63. {
  64. if (::FusionpCompareStrings(
  65. buffAcquiredShortName,
  66. buffAcquiredShortName.Cch(),
  67. rbuffAssemblyName,
  68. rbuffAssemblyName.Cch(),
  69. true) == 0)
  70. {
  71. IFW32FALSE_EXIT(rbuffAssemblyName.Win32Assign(buffKeyName));
  72. IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey( hRequestedAsm, buffKeyName, rsReadRights ) );
  73. break;
  74. }
  75. }
  76. //
  77. // Get the value of the key
  78. //
  79. IFW32FALSE_EXIT(
  80. ::FusionpRegQuerySzValueEx(
  81. FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING,
  82. hAsm,
  83. CSMD_TOPLEVEL_SHORTMANIFEST,
  84. buffAcquiredShortName));
  85. //
  86. // If the key was there to be read:
  87. //
  88. if (buffAcquiredShortName.Cch() != 0)
  89. {
  90. if (::FusionpCompareStrings(
  91. buffAcquiredShortName,
  92. buffAcquiredShortName.Cch(),
  93. buffManifestName,
  94. buffManifestName.Cch(),
  95. true) == 0)
  96. {
  97. IFW32FALSE_EXIT(rbuffAssemblyName.Win32Assign(buffKeyName));
  98. IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey(hRequestedAsm, buffKeyName, rsReadRights));
  99. break;
  100. }
  101. }
  102. //
  103. // Get the value of the key
  104. //
  105. IFW32FALSE_EXIT(
  106. ::FusionpRegQuerySzValueEx(
  107. FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING,
  108. hAsm,
  109. CSMD_TOPLEVEL_SHORTCATALOG,
  110. buffAcquiredShortName));
  111. //
  112. // If the key was there to be read:
  113. //
  114. if (buffAcquiredShortName.Cch() != 0)
  115. {
  116. if (::FusionpCompareStrings(
  117. buffAcquiredShortName,
  118. buffAcquiredShortName.Cch(),
  119. buffCatalogName,
  120. buffCatalogName.Cch(),
  121. true) == 0)
  122. {
  123. IFW32FALSE_EXIT(rbuffAssemblyName.Win32Assign(buffKeyName));
  124. IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey(hRequestedAsm, buffKeyName, rsReadRights));
  125. break;
  126. }
  127. }
  128. }
  129. FN_EPILOG
  130. }
  131. BOOL
  132. CAssemblyRecoveryInfo::ResolveCDRomURL(
  133. PCWSTR pszSource,
  134. CBaseStringBuffer &rsbDestination
  135. ) const
  136. {
  137. BOOL fSuccess = TRUE, fFoundMedia = FALSE;
  138. CStringBuffer sbIdentKind, sbIdentData1, sbIdentData2;
  139. CSmallStringBuffer buffDriveStrings;
  140. CStringBufferAccessor acc;
  141. SIZE_T HeadLength;
  142. PCWSTR wcsCursor;
  143. ULONG ulSerialNumber = 0;
  144. WCHAR rgchVolumeName[MAX_PATH];
  145. CDRomSearchType SearchType;
  146. FN_TRACE_WIN32(fSuccess);
  147. PARAMETER_CHECK(pszSource != NULL);
  148. if (!_wcsnicmp(pszSource, URLHEAD_CDROM_TYPE_TAG, URLHEAD_LENGTH_CDROM_TYPE_TAG))
  149. {
  150. HeadLength = URLHEAD_LENGTH_CDROM_TYPE_TAG;
  151. SearchType = CDRST_Tagfile;
  152. }
  153. else if (!_wcsnicmp(
  154. pszSource,
  155. URLHEAD_CDROM_TYPE_SERIALNUMBER,
  156. URLHEAD_LENGTH_CDROM_TYPE_SERIALNUMBER))
  157. {
  158. HeadLength = URLHEAD_LENGTH_CDROM_TYPE_SERIALNUMBER;
  159. SearchType = CDRST_SerialNumber;
  160. }
  161. else if (!_wcsnicmp(
  162. pszSource,
  163. URLHEAD_CDROM_TYPE_VOLUMENAME,
  164. URLHEAD_LENGTH_CDROM_TYPE_VOLUMENAME))
  165. {
  166. HeadLength = URLHEAD_LENGTH_CDROM_TYPE_VOLUMENAME;
  167. SearchType = CDRST_VolumeName;
  168. }
  169. else
  170. {
  171. ::FusionpSetLastWin32Error(ERROR_INVALID_PARAMETER);
  172. goto Exit;
  173. }
  174. //
  175. // Get the type of identifier here, and then move the cursor past them and
  176. // the slashes in the url.
  177. //
  178. IFW32FALSE_EXIT(sbIdentKind.Win32Assign(pszSource, HeadLength));
  179. pszSource += HeadLength;
  180. pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
  181. //
  182. // Spin past slashes, assign chunklets
  183. //
  184. IFW32FALSE_EXIT(sbIdentData1.Win32Assign(pszSource, wcscspn(pszSource, CUnicodeCharTraits::PathSeparators())));
  185. pszSource += sbIdentData1.Cch();
  186. pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
  187. //
  188. // If this is a tagfile, also get another blobbet of data off the string
  189. //
  190. if (SearchType == CDRST_Tagfile)
  191. {
  192. IFW32FALSE_EXIT(sbIdentData2.Win32Assign(pszSource, wcscspn(pszSource, CUnicodeCharTraits::PathSeparators())));
  193. pszSource += sbIdentData2.Cch();
  194. pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
  195. }
  196. else if (SearchType == CDRST_SerialNumber)
  197. {
  198. IFW32FALSE_EXIT(CFusionParser::ParseULONG(
  199. ulSerialNumber,
  200. sbIdentData1,
  201. sbIdentData1.Cch(),
  202. 16));
  203. }
  204. //
  205. // Now let's do ... interesting ... things to the CD-roms.
  206. //
  207. IFW32FALSE_EXIT(buffDriveStrings.Win32ResizeBuffer(GetLogicalDriveStringsW(0, NULL) + 1, eDoNotPreserveBufferContents));
  208. acc.Attach(&buffDriveStrings);
  209. IFW32FALSE_ORIGINATE_AND_EXIT(
  210. ::GetLogicalDriveStringsW(
  211. static_cast<DWORD>(acc.GetBufferCch()),
  212. acc));
  213. acc.Detach();
  214. wcsCursor = buffDriveStrings;
  215. //
  216. // Look at all the found drive letters
  217. //
  218. while (wcsCursor && *wcsCursor && !fFoundMedia)
  219. {
  220. DWORD dwSerialNumber;
  221. const DWORD dwDriveType = ::GetDriveTypeW(wcsCursor);
  222. if (dwDriveType == DRIVE_CDROM)
  223. {
  224. //
  225. // I argue that a failure in GetVolumeInformationW isn't "bad enough"
  226. // to kill the call to this function. Instead, it should just skip
  227. // the check of the failed drive letter, as it does here.
  228. //
  229. if(!::GetVolumeInformationW(
  230. wcsCursor,
  231. rgchVolumeName,
  232. NUMBER_OF(rgchVolumeName),
  233. &dwSerialNumber,
  234. NULL,
  235. NULL,
  236. NULL,
  237. 0))
  238. {
  239. #if DBG
  240. ::FusionpDbgPrintEx(
  241. FUSION_DBG_LEVEL_WFP,
  242. "SXS.DLL: %s() - Failed getting volume information for drive letter %ls (Win32 Error = %ld), skipping\n",
  243. __FUNCTION__,
  244. wcsCursor,
  245. ::FusionpGetLastWin32Error());
  246. #endif
  247. continue;
  248. }
  249. switch (SearchType)
  250. {
  251. case CDRST_Tagfile:
  252. {
  253. CFusionFile FileHandle;
  254. CHAR chBuffer[MAX_PATH];
  255. CStringBuffer sbContents;
  256. DWORD dwTextLength;
  257. if (FileHandle.Win32CreateFile(sbIdentData1, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING))
  258. {
  259. IFW32FALSE_ORIGINATE_AND_EXIT(
  260. ::ReadFile(
  261. FileHandle,
  262. chBuffer, NUMBER_OF(chBuffer),
  263. &dwTextLength, NULL));
  264. IFW32FALSE_EXIT(sbContents.Win32Assign(chBuffer, dwTextLength));
  265. fFoundMedia = !_wcsnicmp(sbContents, sbIdentData2, sbIdentData2.Cch());
  266. }
  267. }
  268. break;
  269. case CDRST_SerialNumber:
  270. fFoundMedia = (dwSerialNumber == ulSerialNumber);
  271. break;
  272. case CDRST_VolumeName:
  273. fFoundMedia = (::FusionpStrCmpI(rgchVolumeName, sbIdentData1) == 0);
  274. break;
  275. default:
  276. break;
  277. }
  278. }
  279. if (!fFoundMedia)
  280. wcsCursor += ::wcslen(wcsCursor) + 1;
  281. }
  282. if (fFoundMedia)
  283. {
  284. IFW32FALSE_EXIT(rsbDestination.Win32Assign(wcsCursor, ::wcslen(wcsCursor)));
  285. IFW32FALSE_EXIT(rsbDestination.Win32AppendPathElement(pszSource, ::wcslen(pszSource)));
  286. }
  287. fSuccess = TRUE;
  288. Exit:
  289. //
  290. // Failure indicated by an empty destination.
  291. //
  292. if (!fSuccess)
  293. {
  294. rsbDestination.Clear();
  295. }
  296. return fSuccess;
  297. }
  298. BOOL
  299. CAssemblyRecoveryInfo::ResolveWinSourceMediaURL(
  300. PCWSTR wcsUrlTrailer,
  301. CBaseStringBuffer &rsbDestination
  302. ) const
  303. {
  304. CStringBuffer buffWindowsInstallSource;
  305. const static PCWSTR AssemblySourceStrings[] = {
  306. WINSXS_INSTALL_SVCPACK_REGKEY,
  307. WINSXS_INSTALL_SOURCEPATH_REGKEY
  308. };
  309. SIZE_T iWhichSource = 0;
  310. BOOL fSuccess = TRUE, fFoundCodebase = FALSE;
  311. CFusionRegKey hkSetupInfo;
  312. DWORD dwWasFromCDRom = 0;
  313. FN_TRACE_WIN32(fSuccess);
  314. PARAMETER_CHECK(wcsUrlTrailer != NULL);
  315. IFREGFAILED_ORIGINATE_AND_EXIT(
  316. ::RegOpenKeyExW(
  317. HKEY_LOCAL_MACHINE,
  318. WINSXS_INSTALL_SOURCE_BASEDIR,
  319. 0,
  320. KEY_READ | FUSIONP_KEY_WOW64_64KEY,
  321. &hkSetupInfo));
  322. if (!::FusionpRegQueryDwordValueEx(
  323. 0,
  324. hkSetupInfo,
  325. WINSXS_INSTALL_SOURCE_IS_CDROM,
  326. &dwWasFromCDRom))
  327. {
  328. dwWasFromCDRom = 0;
  329. }
  330. while (iWhichSource < NUMBER_OF(AssemblySourceStrings))
  331. {
  332. IFW32FALSE_EXIT(
  333. ::FusionpRegQuerySzValueEx(
  334. FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING,
  335. hkSetupInfo,
  336. AssemblySourceStrings[iWhichSource],
  337. buffWindowsInstallSource));
  338. //
  339. // This really _really_ should not be empty. If it is, then someone
  340. // went and fiddled with the registry on us.
  341. //
  342. ASSERT(buffWindowsInstallSource.Cch() != 0);
  343. if (buffWindowsInstallSource.Cch() == 0)
  344. {
  345. iWhichSource++;
  346. continue;
  347. }
  348. //
  349. // If this was from a CD, then spin through the list of CD's in the system
  350. // and see if we can match the codebase against the root dir of the CD
  351. //
  352. if (dwWasFromCDRom)
  353. {
  354. CSmallStringBuffer buffDriveStrings;
  355. CStringBufferAccessor acc;
  356. PCWSTR pszCursor;
  357. DWORD dwSize;
  358. IFW32FALSE_EXIT(
  359. buffDriveStrings.Win32ResizeBuffer(
  360. dwSize = (::GetLogicalDriveStringsW(0, NULL) + 1),
  361. eDoNotPreserveBufferContents));
  362. acc.Attach(&buffDriveStrings);
  363. ::GetLogicalDriveStringsW(
  364. static_cast<DWORD>(acc.GetBufferCch()),
  365. acc);
  366. acc.Detach();
  367. pszCursor = buffDriveStrings;
  368. while (*pszCursor)
  369. {
  370. if (::GetDriveTypeW(pszCursor) == DRIVE_CDROM)
  371. {
  372. DWORD dwAttributes;
  373. DWORD dwWin32Error;
  374. IFW32FALSE_EXIT(rsbDestination.Win32Assign(pszCursor, ::wcslen(pszCursor)));
  375. IFW32FALSE_EXIT(rsbDestination.Win32AppendPathElement(wcsUrlTrailer, ::wcslen(wcsUrlTrailer)));
  376. IFW32FALSE_EXIT(
  377. ::SxspGetFileAttributesW(
  378. rsbDestination,
  379. dwAttributes,
  380. dwWin32Error,
  381. 3,
  382. ERROR_FILE_NOT_FOUND,
  383. ERROR_PATH_NOT_FOUND,
  384. ERROR_NOT_READY,
  385. ERROR_ACCESS_DENIED));
  386. if (dwWin32Error == ERROR_SUCCESS)
  387. {
  388. fFoundCodebase = TRUE;
  389. FN_SUCCESSFUL_EXIT();
  390. }
  391. }
  392. pszCursor += ::wcslen(pszCursor) + 1;
  393. }
  394. }
  395. //
  396. // This wasn't a CD-rom installation, so prepend the install source path to
  397. // the string that was passed in.
  398. //
  399. else
  400. {
  401. IFW32FALSE_EXIT(rsbDestination.Win32Assign(buffWindowsInstallSource, buffWindowsInstallSource.Cch()));
  402. IFW32FALSE_EXIT(rsbDestination.Win32AppendPathElement(wcsUrlTrailer, ::wcslen(wcsUrlTrailer)));
  403. if (::GetFileAttributesW(rsbDestination) != -1)
  404. {
  405. fFoundCodebase = TRUE;
  406. fSuccess = TRUE;
  407. goto Exit;
  408. }
  409. }
  410. iWhichSource++;
  411. }
  412. fSuccess = TRUE;
  413. Exit:
  414. if (!fFoundCodebase)
  415. {
  416. rsbDestination.Clear();
  417. }
  418. return fSuccess;
  419. }
  420. BOOL
  421. CAssemblyRecoveryInfo::AssociateWithAssembly(
  422. IN OUT CBaseStringBuffer &rsbSourceAssemblyName,
  423. bool &rfNoAssembly
  424. )
  425. {
  426. //
  427. // First check the manifest that the assembly came out of. If it still exists,
  428. // then nifty, go and load the info from it. Otherwise, we should look in the
  429. // registry for information instead.
  430. //
  431. FN_PROLOG_WIN32
  432. CFusionRegKey hInstallInfoKey;
  433. CFusionRegKey hRequestedAsm;
  434. rfNoAssembly = true;
  435. //
  436. // First attempt - try to load it directly off the root installation key.
  437. //
  438. if (!m_fLoadedAndReady)
  439. {
  440. IFW32FALSE_EXIT(this->Initialize());
  441. IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(0, KEY_READ, hInstallInfoKey));
  442. IFW32FALSE_EXIT(
  443. hInstallInfoKey.OpenSubKey(
  444. hRequestedAsm,
  445. rsbSourceAssemblyName,
  446. KEY_READ));
  447. //
  448. // This direct entry wasn't found, so let's go see if we can map it back to some
  449. // other assembly name using the shortname stuff.
  450. //
  451. if (hRequestedAsm == CRegKey::GetInvalidValue())
  452. {
  453. IFW32FALSE_EXIT(
  454. ::pMapShortNameToAssembly(
  455. rsbSourceAssemblyName, // if this is a short name of an assembly, this function would set this to be real assembly name if match is found
  456. hInstallInfoKey,
  457. hRequestedAsm));
  458. //
  459. // Still not found? Darn.
  460. //
  461. if (hRequestedAsm == CRegKey::GetInvalidValue())
  462. FN_SUCCESSFUL_EXIT();
  463. }
  464. IFW32FALSE_EXIT(this->m_sbAssemblyDirectoryName.Win32Assign(rsbSourceAssemblyName));
  465. IFW32FALSE_EXIT(this->m_SecurityMetaData.LoadFromRegistryKey(hRequestedAsm));
  466. this->m_fLoadedAndReady = TRUE;
  467. }
  468. //
  469. // Only set the "no assembly" if we were able to associate (before or
  470. // above.)
  471. //
  472. if ( this->m_fLoadedAndReady )
  473. rfNoAssembly = false;
  474. FN_EPILOG
  475. }
  476. BOOL
  477. SxspDetermineCodebaseType(
  478. IN const CBaseStringBuffer &rcbuffUrlString,
  479. OUT SxsWFPResolveCodebase &rcbaseType,
  480. OUT CBaseStringBuffer *pbuffRemainder
  481. )
  482. {
  483. FN_PROLOG_WIN32
  484. PCWSTR pcwszStringTop = rcbuffUrlString;
  485. SIZE_T cch = rcbuffUrlString.Cch();
  486. SIZE_T i;
  487. CSmallStringBuffer buffTemp; // may be used if we mangle the URL text more
  488. rcbaseType = CODEBASE_RESOLVED_URLHEAD_UNKNOWN;
  489. if (pbuffRemainder != NULL)
  490. pbuffRemainder->Clear();
  491. #define ENTRY(_x) { URLHEAD_ ## _x, NUMBER_OF(URLHEAD_ ## _x) - 1, NUMBER_OF(URLHEAD_ ## _x) - 1, CODEBASE_RESOLVED_URLHEAD_ ## _x },
  492. static const struct
  493. {
  494. PCWSTR pszPrefix;
  495. SIZE_T cchPrefix;
  496. SIZE_T cchAdvance;
  497. SxsWFPResolveCodebase cbaseType;
  498. } s_rgMap[] =
  499. {
  500. ENTRY(FILE)
  501. ENTRY(WINSOURCE)
  502. ENTRY(CDROM)
  503. ENTRY(HTTP)
  504. };
  505. #undef ENTRY
  506. for (i=0; i<NUMBER_OF(s_rgMap); i++)
  507. {
  508. if (_wcsnicmp(pcwszStringTop, s_rgMap[i].pszPrefix, s_rgMap[i].cchPrefix) == 0)
  509. {
  510. pcwszStringTop += s_rgMap[i].cchAdvance;
  511. cch -= s_rgMap[i].cchAdvance;
  512. rcbaseType = s_rgMap[i].cbaseType;
  513. break;
  514. }
  515. }
  516. // If there wasn't an entry, we'll assume it's a simple file path.
  517. if (i == NUMBER_OF(s_rgMap))
  518. {
  519. rcbaseType = CODEBASE_RESOLVED_URLHEAD_FILE;
  520. }
  521. else
  522. {
  523. // If it was a real file: codebase, there's ambiguity about whether there is supposed
  524. // to be 0, 1, 2 or 3 slashes, so we'll just absorb up to 3 slashes to get to what hopefully
  525. // is then a local path. e.g.
  526. //
  527. // file:c:\foo\bar.manifest
  528. // file://c:\foo\bar.manifest
  529. // file:///c:\foo\bar.manifest
  530. //
  531. // all turn into c:\foo\bar.manifest. The URL standard seems clear that non-absolute
  532. // URLs are interpreted in the context of their containing document. In the case
  533. // of a free-standing codebase, that would seem to mean that the hostname field is
  534. // required, where the general form is (by my reading):
  535. //
  536. // file:[//[hostname]]/path
  537. //
  538. // it kind of makes sense to imagine that file:/c:\foo.manifest is reasonable; the only
  539. // useful context to get the hostname from is the local machine. file:///c:\foo.manifest
  540. // meets the standards for URLs not contained in a web document. file:c:\foo.manifest
  541. // also makes sense if you believe the point of the slash is separate the hostname specification
  542. // from the host-specific part of the URL, since if you're happy omitting the hostname
  543. // part, there's nothing to separate. (Note that really file:c:\foo.manifest should
  544. // be considered relative to the current document since it doesn't have the slash at the
  545. // front of the name, but even less than we have a current hostname, we certainly
  546. // don't have a point in the filesystem hierarchy that it makes sense to consider "current".)
  547. //
  548. // file://c:\foo\bar.manifest seems to have become popular even though it doesn't
  549. // have any useful definition in any way shape or form. The two slashes should indicate
  550. // that the next thing should be hostname; instead we see c:\
  551. //
  552. // That's all just a long-winded justifcation for absorbing up to 3 slashes at the
  553. // beginning of the remainder of the string. If there are four or more, we'll let it fail
  554. // as a bad path later on.
  555. //
  556. // mgrier 6/27/2001
  557. if (rcbaseType == CODEBASE_RESOLVED_URLHEAD_FILE)
  558. {
  559. if ((cch > 0) && (pcwszStringTop[0] == L'/'))
  560. {
  561. cch--;
  562. pcwszStringTop++;
  563. }
  564. if ((cch > 0) && (pcwszStringTop[0] == L'/'))
  565. {
  566. cch--;
  567. pcwszStringTop++;
  568. }
  569. if ((cch > 0) && (pcwszStringTop[0] == L'/'))
  570. {
  571. cch--;
  572. pcwszStringTop++;
  573. }
  574. }
  575. else if (rcbaseType == CODEBASE_RESOLVED_URLHEAD_HTTP)
  576. {
  577. // Hey, on Whistler, we have the WebDAV redirector, so
  578. // we can turn this URL into a UNC path!
  579. bool fGeneratedUNCPath = false;
  580. IFW32FALSE_EXIT(buffTemp.Win32Assign(L"\\\\", 2));
  581. if (pcwszStringTop[0] == L'/')
  582. {
  583. if (pcwszStringTop[1] == L'/')
  584. {
  585. // http:// so far; the next thing must be a hostname!
  586. PCWSTR pszSlash = wcschr(pcwszStringTop + 2, L'/');
  587. if (pszSlash != NULL)
  588. {
  589. // //foo/bar (http: removed earlier...)
  590. // pcwszStringTop == [0]
  591. // pszSlash == [5]
  592. // cch == 9
  593. IFW32FALSE_EXIT(buffTemp.Win32Append(pcwszStringTop + 2, (pszSlash - pcwszStringTop) - 3));
  594. IFW32FALSE_EXIT(buffTemp.Win32Append(L"\\", 1));
  595. IFW32FALSE_EXIT(buffTemp.Win32Append(pszSlash + 1, cch - (pszSlash - pcwszStringTop) - 1));
  596. fGeneratedUNCPath = true;
  597. }
  598. }
  599. }
  600. if (fGeneratedUNCPath)
  601. {
  602. // poof, it's a file path
  603. pcwszStringTop = buffTemp;
  604. cch = buffTemp.Cch();
  605. rcbaseType = CODEBASE_RESOLVED_URLHEAD_FILE;
  606. }
  607. }
  608. }
  609. if (pbuffRemainder != NULL)
  610. {
  611. IFW32FALSE_EXIT(
  612. pbuffRemainder->Win32Assign(pcwszStringTop, cch));
  613. }
  614. #if DBG
  615. {
  616. CUnicodeString a(rcbuffUrlString, rcbuffUrlString.Cch());
  617. CUnicodeString b(rcbuffUrlString, (cch <= rcbuffUrlString.Cch()) ? (rcbuffUrlString.Cch() - cch) : 0);
  618. CUnicodeString c(pcwszStringTop, (cch <= ::wcslen(pcwszStringTop)) ? cch : 0);
  619. ::FusionpDbgPrintEx(
  620. FUSION_DBG_LEVEL_WFP,
  621. "SXS: %s - split \"%wZ\" into \"%wZ\" and \"%wZ\"\n",
  622. __FUNCTION__, &a, &b, &c);
  623. }
  624. #endif
  625. FN_EPILOG
  626. }
  627. BOOL
  628. CAssemblyRecoveryInfo::CopyValue(const CAssemblyRecoveryInfo& other)
  629. {
  630. BOOL bSuccess = FALSE;
  631. FN_TRACE_WIN32(bSuccess);
  632. if (&other != this)
  633. {
  634. IFW32FALSE_EXIT(m_sbAssemblyDirectoryName.Win32Assign(other.m_sbAssemblyDirectoryName));
  635. IFW32FALSE_EXIT(m_SecurityMetaData.Initialize(other.m_SecurityMetaData));
  636. m_fLoadedAndReady = other.m_fLoadedAndReady;
  637. }
  638. bSuccess = TRUE;
  639. Exit:
  640. if ( !bSuccess )
  641. {
  642. this->m_fLoadedAndReady = FALSE;
  643. }
  644. return bSuccess;
  645. }
  646. BOOL
  647. CAssemblyRecoveryInfo::SetAssemblyIdentity(
  648. IN PCASSEMBLY_IDENTITY pcidAssembly
  649. )
  650. {
  651. FN_PROLOG_WIN32
  652. SIZE_T cbEncodedTextSize;
  653. SIZE_T cbActualSize;
  654. CStringBuffer sbTextualEncoding;
  655. IFW32FALSE_EXIT( ::SxsComputeAssemblyIdentityEncodedSize(
  656. 0,
  657. pcidAssembly,
  658. NULL,
  659. SXS_ASSEMBLY_IDENTITY_ENCODING_DEFAULTGROUP_TEXTUAL,
  660. &cbEncodedTextSize ) );
  661. INTERNAL_ERROR_CHECK( ( cbEncodedTextSize % sizeof(WCHAR) ) == 0 );
  662. IFW32FALSE_EXIT( sbTextualEncoding.Win32ResizeBuffer(
  663. ( cbEncodedTextSize / sizeof(WCHAR) ) + 1,
  664. eDoNotPreserveBufferContents ) );
  665. {
  666. CStringBufferAccessor sba;
  667. sba.Attach( &sbTextualEncoding );
  668. IFW32FALSE_EXIT( ::SxsEncodeAssemblyIdentity(
  669. 0,
  670. pcidAssembly,
  671. NULL,
  672. SXS_ASSEMBLY_IDENTITY_ENCODING_DEFAULTGROUP_TEXTUAL,
  673. sba.GetBufferCb(),
  674. sba.GetBufferPtr(),
  675. &cbActualSize ) );
  676. INTERNAL_ERROR_CHECK( cbActualSize == cbEncodedTextSize );
  677. sba.GetBufferPtr()[cbActualSize / sizeof(WCHAR)] = L'\0';
  678. }
  679. IFW32FALSE_EXIT( this->SetAssemblyIdentity( sbTextualEncoding ) );
  680. FN_EPILOG
  681. }
  682. BOOL
  683. CAssemblyRecoveryInfo::PrepareForWriting()
  684. {
  685. FN_PROLOG_WIN32
  686. CSxsPointerWithNamedDestructor<ASSEMBLY_IDENTITY, SxsDestroyAssemblyIdentity> pIdentity;
  687. CSmallStringBuffer buffAsmRoot;
  688. CStringBuffer buffTemp1;
  689. CStringBuffer buffTemp2;
  690. const CBaseStringBuffer& OurTextualIdentity = m_SecurityMetaData.GetTextualIdentity();
  691. BOOL fIsPolicy;
  692. DWORD dwWin32Error;
  693. ::FusionpDbgPrintEx(
  694. FUSION_DBG_LEVEL_WFP,
  695. "SXS.DLL: %s - handling assembly \"%ls\"\n",
  696. __FUNCTION__,
  697. static_cast<PCWSTR>(OurTextualIdentity));
  698. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffAsmRoot));
  699. IFW32FALSE_EXIT(::SxspCreateAssemblyIdentityFromTextualString(OurTextualIdentity, &pIdentity));
  700. IFW32FALSE_EXIT(::SxspDetermineAssemblyType(pIdentity, fIsPolicy));
  701. //
  702. // It's likely that this short name hasn't been generated yet, mostly because the files
  703. // may not have been copied around just yet.
  704. //
  705. if (this->m_SecurityMetaData.GetInstalledDirShortName().Cch() == 0)
  706. {
  707. IFW32FALSE_EXIT(
  708. ::SxspGenerateSxsPath(
  709. 0,
  710. SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY | ( fIsPolicy ? SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY : 0 ),
  711. buffAsmRoot,
  712. buffAsmRoot.Cch(),
  713. pIdentity,
  714. buffTemp2));
  715. IFW32FALSE_EXIT(
  716. ::SxspGetShortPathName(
  717. buffTemp2,
  718. buffTemp1,
  719. dwWin32Error,
  720. 4,
  721. ERROR_PATH_NOT_FOUND, ERROR_FILE_NOT_FOUND, ERROR_BAD_NET_NAME, ERROR_BAD_NETPATH));
  722. if (dwWin32Error == ERROR_SUCCESS)
  723. {
  724. buffTemp1.RemoveTrailingPathSeparators();
  725. IFW32FALSE_EXIT(buffTemp1.Win32GetLastPathElement(buffTemp2));
  726. IFW32FALSE_EXIT(m_SecurityMetaData.SetInstalledDirShortName(buffTemp2));
  727. ::FusionpDbgPrintEx(
  728. FUSION_DBG_LEVEL_WFP,
  729. "SXS: %s - decided that the short dir name is \"%ls\"\n",
  730. __FUNCTION__,
  731. static_cast<PCWSTR>(buffTemp2));
  732. }
  733. else
  734. {
  735. ::FusionpDbgPrintEx(
  736. FUSION_DBG_LEVEL_WFP,
  737. "SXS: %s - unable to determine short name for \"%ls\"\n",
  738. __FUNCTION__,
  739. static_cast<PCWSTR>(buffTemp2));
  740. }
  741. }
  742. //
  743. // Get the public key token string
  744. //
  745. {
  746. PCWSTR wchString;
  747. SIZE_T cchString;
  748. CFusionByteArray baStrongNameBits;
  749. IFW32FALSE_EXIT(::SxspGetAssemblyIdentityAttributeValue(
  750. SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL,
  751. pIdentity,
  752. &s_IdentityAttribute_publicKeyToken,
  753. &wchString,
  754. &cchString));
  755. if (cchString != 0)
  756. {
  757. IFW32FALSE_EXIT(::SxspHashStringToBytes(wchString, cchString, baStrongNameBits));
  758. IFW32FALSE_EXIT(m_SecurityMetaData.SetSignerPublicKeyTokenBits(baStrongNameBits));
  759. }
  760. }
  761. //
  762. // And now the short name of the manifest and catalog
  763. //
  764. {
  765. CStringBuffer buffManifestPath;
  766. IFW32FALSE_EXIT(
  767. ::SxspCreateManifestFileNameFromTextualString(
  768. 0,
  769. SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST | ( fIsPolicy ? SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY : 0 ),
  770. buffAsmRoot,
  771. OurTextualIdentity,
  772. buffManifestPath));
  773. // Get the manifest short path first
  774. IFW32FALSE_EXIT(::SxspGetShortPathName(buffManifestPath, buffTemp1));
  775. IFW32FALSE_EXIT(buffTemp1.Win32GetLastPathElement(buffTemp2));
  776. ::FusionpDbgPrintEx(
  777. FUSION_DBG_LEVEL_WFP,
  778. "SXS: %s - manifest short path name determined to be \"%ls\"\n",
  779. __FUNCTION__,
  780. static_cast<PCWSTR>(buffTemp2));
  781. IFW32FALSE_EXIT(m_SecurityMetaData.SetShortManifestPath(buffTemp2));
  782. // Then swap extensions, get the catalog short path
  783. IFW32FALSE_EXIT(
  784. buffManifestPath.Win32ChangePathExtension(
  785. FILE_EXTENSION_CATALOG,
  786. FILE_EXTENSION_CATALOG_CCH,
  787. eAddIfNoExtension));
  788. IFW32FALSE_EXIT(::SxspGetShortPathName(buffManifestPath, buffTemp1));
  789. IFW32FALSE_EXIT(buffTemp1.Win32GetLastPathElement(buffTemp2));
  790. ::FusionpDbgPrintEx(
  791. FUSION_DBG_LEVEL_WFP,
  792. "SXS: %s - catalog short path name determined to be \"%ls\"\n",
  793. __FUNCTION__,
  794. static_cast<PCWSTR>(buffTemp2));
  795. IFW32FALSE_EXIT(m_SecurityMetaData.SetShortCatalogPath(buffTemp2));
  796. }
  797. FN_EPILOG
  798. }
  799. BOOL
  800. CAssemblyRecoveryInfo::WriteSecondaryAssemblyInfoIntoRegistryKey(
  801. CRegKey & rhkRegistryNode
  802. ) const
  803. {
  804. FN_PROLOG_WIN32
  805. IFW32FALSE_EXIT(m_SecurityMetaData.WriteSecondaryAssemblyInfoIntoRegistryKey(rhkRegistryNode));
  806. FN_EPILOG
  807. }
  808. BOOL
  809. CAssemblyRecoveryInfo::WritePrimaryAssemblyInfoToRegistryKey(
  810. ULONG Flags,
  811. CRegKey & rhkRegistryNode
  812. ) const
  813. {
  814. FN_PROLOG_WIN32
  815. PARAMETER_CHECK((Flags & ~(SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_TO_REGISTRY_KEY_FLAG_REFRESH)) == 0);
  816. ULONG Flags2 = 0;
  817. if (Flags & SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_TO_REGISTRY_KEY_FLAG_REFRESH)
  818. {
  819. Flags2 |= SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_INTO_REGISTRY_KEY_FLAG_REFRESH;
  820. #if DBG
  821. ::FusionpDbgPrintEx(
  822. FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INSTALLATION,
  823. "SXS.DLL: %s - propping recovery flag to WritePrimaryAssemblyInfoIntoRegistryKey\n",
  824. __FUNCTION__);
  825. #endif
  826. }
  827. IFW32FALSE_EXIT(m_SecurityMetaData.WritePrimaryAssemblyInfoIntoRegistryKey(Flags2, rhkRegistryNode));
  828. FN_EPILOG
  829. }
  830. BOOL
  831. CAssemblyRecoveryInfo::OpenInstallationSubKey(
  832. CFusionRegKey& hkSingleAssemblyInfo,
  833. DWORD OpenOrCreate,
  834. DWORD Access)
  835. {
  836. FN_PROLOG_WIN32
  837. CSmallStringBuffer buffRegKeyName;
  838. CFusionRegKey hkAllInstallationInfo;
  839. CSxsPointerWithNamedDestructor<ASSEMBLY_IDENTITY, SxsDestroyAssemblyIdentity> pAssemblyIdentity;
  840. IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(
  841. 0,
  842. OpenOrCreate,
  843. hkAllInstallationInfo));
  844. IFW32FALSE_EXIT( SxspCreateAssemblyIdentityFromTextualString(
  845. this->m_SecurityMetaData.GetTextualIdentity(),
  846. &pAssemblyIdentity ) );
  847. IFW32FALSE_EXIT( ::SxspGenerateSxsPath(
  848. SXSP_GENERATE_SXS_PATH_FLAG_OMIT_ROOT,
  849. SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY,
  850. NULL, 0,
  851. pAssemblyIdentity,
  852. buffRegKeyName ) );
  853. IFW32FALSE_EXIT( hkAllInstallationInfo.OpenSubKey(
  854. hkSingleAssemblyInfo,
  855. buffRegKeyName,
  856. Access,
  857. 0));
  858. FN_EPILOG
  859. }
  860. VOID
  861. CAssemblyRecoveryInfo::RestorePreviouslyExistingRegistryData()
  862. {
  863. FN_PROLOG_VOID
  864. if (m_fHadCatalog)
  865. {
  866. CFusionRegKey hkSingleAssemblyInfo;
  867. #if DBG
  868. ::FusionpDbgPrintEx(
  869. FUSION_DBG_LEVEL_INSTALLATION,
  870. "SXS.DLL: %s() - restoring registry data for %ls\n",
  871. __FUNCTION__,
  872. static_cast<PCWSTR>(this->m_SecurityMetaData.GetTextualIdentity()));
  873. #endif
  874. IFW32FALSE_EXIT(
  875. this->OpenInstallationSubKey(
  876. hkSingleAssemblyInfo,
  877. KEY_CREATE_SUB_KEY, KEY_WRITE | KEY_READ | FUSIONP_KEY_WOW64_64KEY));
  878. IFW32FALSE_EXIT(
  879. hkSingleAssemblyInfo.SetValue(
  880. CSMD_TOPLEVEL_CATALOG,
  881. static_cast<DWORD>(1)));
  882. }
  883. FN_EPILOG
  884. }
  885. BOOL
  886. CAssemblyRecoveryInfo::ClearExistingRegistryData()
  887. {
  888. //
  889. // Do not be so eager to delete registry metadata, so that
  890. // an assembly for which refresh failed might succeed if another
  891. // file change comes in, or sfc /scannow.
  892. //
  893. // As well, if a replace-existing install fails, don't destroy
  894. // the metadata for previously successfully installed instances
  895. // of the same assembly.
  896. //
  897. const static struct
  898. {
  899. PCWSTR Data;
  900. SIZE_T Length;
  901. }
  902. DeletableValues[] =
  903. {
  904. #define ENTRY(x) { x, NUMBER_OF(x) - 1 }
  905. ENTRY(CSMD_TOPLEVEL_CATALOG),
  906. #if 0
  907. ENTRY(CSMD_TOPLEVEL_SHORTNAME),
  908. ENTRY(CSMD_TOPLEVEL_SHORTCATALOG),
  909. ENTRY(CSMD_TOPLEVEL_SHORTMANIFEST),
  910. ENTRY(CSMD_TOPLEVEL_MANIFESTHASH),
  911. ENTRY(CSMD_TOPLEVEL_PUBLIC_KEY_TOKEN),
  912. ENTRY(CSMD_TOPLEVEL_IDENTITY),
  913. ENTRY(CSMD_TOPLEVEL_CODEBASE) // legacy, delete (actually not legacy anymore)
  914. #endif
  915. };
  916. #undef ENTRY
  917. static const PCWSTR DeletableKeys[] =
  918. {
  919. NULL,
  920. #if 0
  921. CSMD_TOPLEVEL_FILES,
  922. #endif
  923. };
  924. FN_PROLOG_WIN32
  925. CFusionRegKey hkSingleAssemblyInfo;
  926. IFW32FALSE_EXIT(this->OpenInstallationSubKey(hkSingleAssemblyInfo, KEY_CREATE_SUB_KEY, KEY_WRITE | KEY_READ | FUSIONP_KEY_WOW64_64KEY));
  927. //
  928. // We need to delete the installation information for a single assembly - everything
  929. // this class owns.
  930. //
  931. if ( hkSingleAssemblyInfo != CFusionRegKey::GetInvalidValue() )
  932. {
  933. ULONG ul = 0;
  934. //
  935. // Clear values
  936. //
  937. for ( ul = 0; ul < NUMBER_OF(DeletableValues); ul++ )
  938. {
  939. DWORD dwWin32Error = NO_ERROR;
  940. IFW32FALSE_EXIT(
  941. hkSingleAssemblyInfo.DeleteValue(
  942. DeletableValues[ul].Data,
  943. dwWin32Error,
  944. 2,
  945. ERROR_PATH_NOT_FOUND, ERROR_FILE_NOT_FOUND));
  946. if (dwWin32Error == NO_ERROR
  947. && !m_fHadCatalog
  948. && ::FusionpEqualStrings(
  949. DeletableValues[ul].Data,
  950. DeletableValues[ul].Length,
  951. CSMD_TOPLEVEL_CATALOG,
  952. NUMBER_OF(CSMD_TOPLEVEL_CATALOG) - 1,
  953. FALSE
  954. ))
  955. {
  956. m_fHadCatalog = true;
  957. }
  958. }
  959. //
  960. // Delete eligible keys
  961. //
  962. for ( ul = 0; ul < NUMBER_OF(DeletableKeys); ul++ )
  963. {
  964. if (DeletableKeys[ul] != NULL && DeletableKeys[ul][0] != L'\0')
  965. {
  966. CFusionRegKey hkTempKey;
  967. IFW32FALSE_EXIT(hkSingleAssemblyInfo.OpenSubKey(hkTempKey, DeletableKeys[ul], KEY_ALL_ACCESS, 0));
  968. if ( hkTempKey != CFusionRegKey::GetInvalidValue() )
  969. {
  970. IFW32FALSE_EXIT(hkTempKey.DestroyKeyTree());
  971. IFW32FALSE_EXIT(hkSingleAssemblyInfo.DeleteKey(DeletableKeys[ul]));
  972. }
  973. }
  974. }
  975. }
  976. FN_EPILOG
  977. }
  978. BOOL
  979. SxspLooksLikeAssemblyDirectoryName(
  980. const CBaseStringBuffer &rsbSupposedAsmDirectoryName,
  981. BOOL &rfLooksLikeAssemblyName
  982. )
  983. /*++
  984. Most of this was copied directly from SxspParseAssemblyReference, which
  985. is no longer valid, because it can't know how to turn the string back
  986. into the actual assembly reference just based on the hash value and
  987. public key of a string.
  988. --*/
  989. {
  990. BOOL fSuccess = FALSE;
  991. FN_TRACE_WIN32(fSuccess);
  992. PCWSTR pszCursor = NULL;
  993. PCWSTR wsNextBlock = NULL;
  994. SIZE_T cchSegment;
  995. ASSEMBLY_VERSION Version;
  996. static const WCHAR UNDERSCORE = L'_';
  997. bool fSyntaxValid = false;
  998. bool fAttributeValid = false;
  999. rfLooksLikeAssemblyName = FALSE;
  1000. pszCursor = rsbSupposedAsmDirectoryName;
  1001. //
  1002. // Processor architecture
  1003. //
  1004. if ((wsNextBlock = ::StringFindChar(pszCursor, UNDERSCORE)) == NULL)
  1005. FN_SUCCESSFUL_EXIT();
  1006. if ((cchSegment = (wsNextBlock - pszCursor)) == 0)
  1007. FN_SUCCESSFUL_EXIT();
  1008. IFW32FALSE_EXIT(::FusionpParseProcessorArchitecture(pszCursor, cchSegment, NULL, fAttributeValid));
  1009. if (!fAttributeValid)
  1010. FN_SUCCESSFUL_EXIT();
  1011. pszCursor = wsNextBlock + 1;
  1012. //
  1013. // Name
  1014. //
  1015. if ((wsNextBlock = StringFindChar(pszCursor, UNDERSCORE)) == NULL)
  1016. FN_SUCCESSFUL_EXIT();
  1017. if ((cchSegment = wsNextBlock - pszCursor) == 0)
  1018. FN_SUCCESSFUL_EXIT();
  1019. pszCursor = wsNextBlock + 1;
  1020. //
  1021. // Public key string
  1022. //
  1023. if ((wsNextBlock = StringFindChar(pszCursor, UNDERSCORE)) == NULL)
  1024. FN_SUCCESSFUL_EXIT();
  1025. if ((cchSegment = wsNextBlock - pszCursor) == 0)
  1026. FN_SUCCESSFUL_EXIT();
  1027. if ((::FusionpCompareStrings(
  1028. pszCursor,
  1029. cchSegment,
  1030. SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_PUBLICKEY_MISSING_VALUE,
  1031. NUMBER_OF(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_PUBLICKEY_MISSING_VALUE) - 1,
  1032. true) == 0) ||
  1033. !::SxspIsFullHexString(pszCursor, cchSegment))
  1034. FN_SUCCESSFUL_EXIT();
  1035. pszCursor = wsNextBlock + 1;
  1036. //
  1037. // Version string
  1038. //
  1039. if ((wsNextBlock = StringFindChar(pszCursor, UNDERSCORE)) == NULL)
  1040. FN_SUCCESSFUL_EXIT();
  1041. if ((cchSegment = wsNextBlock - pszCursor) == 0)
  1042. FN_SUCCESSFUL_EXIT();
  1043. IFW32FALSE_EXIT(CFusionParser::ParseVersion(Version, pszCursor, cchSegment, fSyntaxValid));
  1044. if (!fSyntaxValid)
  1045. FN_SUCCESSFUL_EXIT();
  1046. pszCursor = wsNextBlock + 1;
  1047. //
  1048. // Language ID
  1049. //
  1050. if ((wsNextBlock = ::StringFindChar(pszCursor, UNDERSCORE)) == NULL)
  1051. FN_SUCCESSFUL_EXIT();
  1052. if ((cchSegment = wsNextBlock - pszCursor) == 0)
  1053. FN_SUCCESSFUL_EXIT();
  1054. //
  1055. // BUGBUG (jonwis) - It seems that langids are no longer four-character hex
  1056. // string representations of shorts anymore. All we're checking at the moment
  1057. // is to see that the string isn't blank. Is this the Right Thing?
  1058. //
  1059. pszCursor = wsNextBlock + 1;
  1060. //
  1061. // Last block should just be the hash
  1062. //
  1063. if (!::SxspIsFullHexString(pszCursor, ::wcslen(pszCursor)))
  1064. FN_SUCCESSFUL_EXIT();
  1065. // We ran the gauntlet; all the segments of the path look good, let's use it.
  1066. rfLooksLikeAssemblyName = TRUE;
  1067. fSuccess = TRUE;
  1068. Exit:
  1069. return fSuccess;
  1070. }