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.

1662 lines
46 KiB

  1. #include "stdinc.h"
  2. #include "util.h"
  3. #include "xmlparser.hxx"
  4. #include "FusionEventLog.h"
  5. #include "parsing.h"
  6. #include "hashfile.h"
  7. #include "recover.h"
  8. #include "filestream.h"
  9. #include "CAssemblyRecoveryInfo.h"
  10. #include "sxsprotect.h"
  11. #include "strongname.h"
  12. #include "SxsExceptionHandling.h"
  13. #include "sxssfcscan.h"
  14. #define MANIFEST_FILE_EXTENSION (L".manifest")
  15. #define FILE_ELEMENT_NAME (L"file")
  16. #define HASH_ATTRIB_NAME (L"hash")
  17. #define FILE_ATTRIB_NAME (L"name")
  18. #define HASHALG_ATTRIB_NAME (L"hashalg")
  19. class CProtectionRequestList;
  20. static CProtectionRequestList* g_ProtectionRequestList = NULL;
  21. HANDLE g_hSxsLoginEvent = NULL;
  22. static BOOL s_fIsSfcAcceptingNotifications = TRUE;
  23. #if DBG
  24. #define SXS_SFC_EXCEPTION_SETTING (SXSP_EXCEPTION_FILTER())
  25. #else
  26. #define SXS_SFC_EXCEPTION_SETTING (EXCEPTION_EXECUTE_HANDLER)
  27. #endif
  28. BOOL
  29. CRecoveryJobTableEntry::Initialize()
  30. {
  31. FN_PROLOG_WIN32
  32. //
  33. // Creates an event that other callers should wait on - manual reset, not
  34. // currently signalled.
  35. //
  36. m_Subscriber = 0;
  37. m_fSuccessValue = FALSE;
  38. IFW32NULL_EXIT(m_EventInstallingAssemblyComplete = ::CreateEventW(NULL, TRUE, FALSE, NULL));
  39. FN_EPILOG
  40. }
  41. BOOL
  42. CRecoveryJobTableEntry::StartInstallation()
  43. {
  44. FN_PROLOG_WIN32
  45. //
  46. // Clear the event (if it wasn't already cleared.
  47. //
  48. IFW32FALSE_ORIGINATE_AND_EXIT(::ResetEvent(m_EventInstallingAssemblyComplete));
  49. FN_EPILOG
  50. }
  51. BOOL
  52. SxspEnsureCatalogStillPresentForManifest(
  53. IN const CBaseStringBuffer& buffManifestPath,
  54. OUT BOOL &rfStillPresent
  55. )
  56. {
  57. FN_PROLOG_WIN32
  58. rfStillPresent = FALSE;
  59. CStringBuffer buffCatalogPath;
  60. IFW32FALSE_EXIT(buffCatalogPath.Win32Assign(buffManifestPath));
  61. IFW32FALSE_EXIT(
  62. buffCatalogPath.Win32ChangePathExtension(
  63. FILE_EXTENSION_CATALOG,
  64. FILE_EXTENSION_CATALOG_CCH,
  65. eAddIfNoExtension));
  66. const DWORD dwCatalogAttribs = ::GetFileAttributesW(buffCatalogPath);
  67. if (dwCatalogAttribs != INVALID_FILE_ATTRIBUTES)
  68. {
  69. rfStillPresent = TRUE;
  70. }
  71. else
  72. {
  73. const DWORD dwLastError = ::FusionpGetLastWin32Error();
  74. switch (dwLastError)
  75. {
  76. default:
  77. ORIGINATE_WIN32_FAILURE_AND_EXIT(GetFileAttributesW, dwLastError);
  78. case ERROR_PATH_NOT_FOUND:
  79. case ERROR_FILE_NOT_FOUND:
  80. case ERROR_BAD_NET_NAME:
  81. case ERROR_BAD_NETPATH:
  82. ;
  83. }
  84. }
  85. FN_EPILOG
  86. }
  87. BOOL
  88. CRecoveryJobTableEntry::InstallationComplete(
  89. BOOL bDoneOk,
  90. SxsRecoveryResult Result,
  91. DWORD dwRecoveryLastError
  92. )
  93. {
  94. FN_PROLOG_WIN32
  95. m_Result = Result;
  96. m_fSuccessValue = bDoneOk;
  97. m_dwLastError = dwRecoveryLastError;
  98. //
  99. // This will tell all the people waiting that we're done and that they
  100. // should capture the exit code and exit out.
  101. //
  102. IFW32FALSE_ORIGINATE_AND_EXIT(::SetEvent(m_EventInstallingAssemblyComplete));
  103. //
  104. // We wait for all our subscribers to go away (ie: for them to capture an
  105. // install code and success value.)
  106. //
  107. while (m_Subscriber)
  108. {
  109. Sleep(50);
  110. }
  111. FN_EPILOG
  112. }
  113. BOOL
  114. CRecoveryJobTableEntry::WaitUntilCompleted(
  115. SxsRecoveryResult &rResult,
  116. BOOL &rbSucceededValue,
  117. DWORD &rdwErrorResult
  118. )
  119. {
  120. FN_PROLOG_WIN32
  121. DWORD WaitResult;
  122. //
  123. // Here we join up to the existing installation routine. We up the number
  124. // of people waiting before entering the wait. I wish there was a better
  125. // way of doing this, something like a built-in kernel object that we can
  126. // raise a count on (like a semaphore), and have another thread lower a
  127. // count on, and someone can wait on the internal count being zero. Yes,
  128. // I could implement this by hand using something with another event or
  129. // two, but that's not the point.
  130. //
  131. ::SxspInterlockedIncrement(&m_Subscriber);
  132. //
  133. // Hang about forever until another thread is done installing
  134. //
  135. IFW32FALSE_ORIGINATE_AND_EXIT((WaitResult = ::WaitForSingleObject(m_EventInstallingAssemblyComplete, INFINITE)) != WAIT_FAILED);
  136. //
  137. // Capture values once the installation is done, return them to the caller.
  138. //
  139. rResult = m_Result;
  140. rdwErrorResult = m_dwLastError;
  141. rbSucceededValue = m_fSuccessValue;
  142. //
  143. // And indicate that we're complete.
  144. //
  145. ::SxspInterlockedDecrement(&m_Subscriber);
  146. FN_EPILOG
  147. }
  148. CRecoveryJobTableEntry::~CRecoveryJobTableEntry()
  149. {
  150. //
  151. // We're done with the event, so close it (release refcount, we don't want lots of these
  152. // just sitting around.)
  153. //
  154. if ((m_EventInstallingAssemblyComplete != NULL) &&
  155. (m_EventInstallingAssemblyComplete != INVALID_HANDLE_VALUE))
  156. {
  157. ::CloseHandle(m_EventInstallingAssemblyComplete);
  158. m_EventInstallingAssemblyComplete = INVALID_HANDLE_VALUE;
  159. }
  160. }
  161. //
  162. // This is our holy grail of sxs protection lists. Don't fidget with
  163. // this listing. Note that we've also got only one. This is because
  164. // right now, there's only one entry to be had (a), and (b) because we
  165. // will fill in the sxs directory on the fly at runtime.
  166. //
  167. SXS_PROTECT_DIRECTORY s_SxsProtectionList[] =
  168. {
  169. {
  170. {0},
  171. 0,
  172. SXS_PROTECT_RECURSIVE,
  173. SXS_PROTECT_FILTER_DEFAULT
  174. }
  175. };
  176. const SIZE_T s_SxsProtectionListCount = NUMBER_OF(s_SxsProtectionList);
  177. BOOL SxspConstructProtectionList();
  178. BOOL WINAPI
  179. SxsProtectionGatherEntriesW(
  180. PCSXS_PROTECT_DIRECTORY *prgpProtectListing,
  181. SIZE_T *pcProtectEntries
  182. )
  183. /*++
  184. This is called by Sfc to gather the entries that we want them to watch.
  185. At the moment, it's a 'proprietary' listing of all the directories and
  186. flags we want them to set on their call to the directory-change watcher.
  187. Mayhaps we should work with them to find out exactly what they want us
  188. to pass (probably the name plus a PVOID, which is fine) and then go from
  189. there.
  190. --*/
  191. {
  192. BOOL fSuccess = FALSE;
  193. FN_TRACE_WIN32(fSuccess);
  194. CStringBuffer sbTemp;
  195. if (prgpProtectListing) *prgpProtectListing = NULL;
  196. if (pcProtectEntries) *pcProtectEntries = 0;
  197. PARAMETER_CHECK(prgpProtectListing);
  198. PARAMETER_CHECK(pcProtectEntries);
  199. //
  200. // This is try/caught because we don't want something here to
  201. // cause WinLogon to bugcheck the system when it AV's.
  202. //
  203. IFW32FALSE_EXIT(::SxspConstructProtectionList());
  204. //
  205. // We really have only one entry, so let's go and edit the first entry to
  206. // be the main sxs store directory.
  207. //
  208. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(sbTemp));
  209. wcsncpy(
  210. s_SxsProtectionList[0].pwszDirectory,
  211. sbTemp,
  212. NUMBER_OF(s_SxsProtectionList[0].pwszDirectory));
  213. //
  214. // Zero out the last character, just in case wcsncpy didn't do it for us
  215. //
  216. s_SxsProtectionList[0].pwszDirectory[NUMBER_OF(s_SxsProtectionList[0].pwszDirectory) - 1] = L'\0';
  217. //
  218. // Shh, don't tell anyone, but the cookie is actually this structure!
  219. //
  220. for (DWORD dw = 0; dw < s_SxsProtectionListCount; dw++)
  221. {
  222. s_SxsProtectionList[dw].pvCookie = &(s_SxsProtectionList[dw]);
  223. }
  224. *prgpProtectListing = s_SxsProtectionList;
  225. *pcProtectEntries = s_SxsProtectionListCount;
  226. fSuccess = TRUE;
  227. Exit:
  228. return fSuccess;
  229. }
  230. inline BOOL
  231. SxspExpandLongPath(
  232. IN OUT CBaseStringBuffer &rbuffPathToLong
  233. )
  234. /*++
  235. Takes in a short path (c:\foo\bar\bloben~1.zot) and sends back out the
  236. full path (c:\foo\bar\blobenheisen.zotamax) if possible. Returns FALSE if
  237. the path could not be expanded (most likely because the path on-disk is
  238. no longer available.)
  239. --*/
  240. {
  241. FN_PROLOG_WIN32
  242. CStringBuffer buffPathName;
  243. CStringBufferAccessor buffAccess;
  244. SIZE_T cchNeededChars;
  245. IFW32ZERO_EXIT(
  246. cchNeededChars = ::GetLongPathNameW(
  247. static_cast<PCWSTR>(rbuffPathToLong),
  248. NULL,
  249. 0));
  250. IFW32FALSE_EXIT(buffPathName.Win32ResizeBuffer(cchNeededChars, eDoNotPreserveBufferContents));
  251. buffAccess.Attach(&buffPathName);
  252. IFW32ZERO_EXIT(
  253. cchNeededChars = ::GetLongPathNameW(
  254. rbuffPathToLong,
  255. buffAccess,
  256. static_cast<DWORD>(buffAccess.GetBufferCch())));
  257. INTERNAL_ERROR_CHECK(cchNeededChars <= buffAccess.GetBufferCch());
  258. IFW32FALSE_EXIT(rbuffPathToLong.Win32Assign(buffPathName));
  259. FN_EPILOG
  260. }
  261. BOOL
  262. SxspResolveAssemblyManifestPath(
  263. const CBaseStringBuffer &rbuffAssemblyDirectoryName,
  264. CBaseStringBuffer &rbuffManifestPath
  265. )
  266. {
  267. FN_PROLOG_WIN32
  268. CStringBuffer buffAssemblyRootDir;
  269. BOOL fLooksLikeAssemblyName = FALSE;
  270. rbuffManifestPath.Clear();
  271. //
  272. // If the string doesn't look like an assembly name, then it's an
  273. // invalid parameter. This is somewhat heavy-handed, as the caller(s)
  274. // to this function will be entirely hosed if they haven't checked to
  275. // see if the string is really an assembly name. Make sure that all
  276. // clients of this,
  277. //
  278. IFW32FALSE_EXIT(::SxspLooksLikeAssemblyDirectoryName(rbuffAssemblyDirectoryName, fLooksLikeAssemblyName));
  279. PARAMETER_CHECK(fLooksLikeAssemblyName);
  280. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffAssemblyRootDir));
  281. IFW32FALSE_EXIT(rbuffManifestPath.Win32Format(
  282. L"%ls\\Manifests\\%ls.%ls",
  283. static_cast<PCWSTR>(buffAssemblyRootDir),
  284. static_cast<PCWSTR>(rbuffAssemblyDirectoryName),
  285. FILE_EXTENSION_MANIFEST));
  286. FN_EPILOG
  287. }
  288. CProtectionRequestRecord::CProtectionRequestRecord()
  289. : m_dwAction(0), m_pvProtection(NULL), m_ulInRecoveryMode(0),
  290. m_pParent(NULL),
  291. m_bIsManPathResolved(FALSE),
  292. m_bInitialized(FALSE)
  293. {
  294. }
  295. BOOL
  296. CProtectionRequestRecord::GetManifestPath(
  297. CBaseStringBuffer &rsbManPath
  298. )
  299. {
  300. BOOL bOk = FALSE;
  301. FN_TRACE_WIN32(bOk);
  302. rsbManPath.Clear();
  303. if (!m_bIsManPathResolved)
  304. {
  305. m_bIsManPathResolved =
  306. ::SxspResolveAssemblyManifestPath(
  307. m_sbAssemblyDirectoryName,
  308. m_sbManifestPath);
  309. }
  310. if (m_bIsManPathResolved)
  311. {
  312. IFW32FALSE_EXIT(rsbManPath.Win32Assign(m_sbManifestPath));
  313. }
  314. else
  315. {
  316. goto Exit;
  317. }
  318. bOk = TRUE;
  319. Exit:
  320. return bOk;
  321. }
  322. inline BOOL
  323. CProtectionRequestRecord::GetManifestContent(CSecurityMetaData *&pMetaData)
  324. {
  325. FN_PROLOG_WIN32
  326. FN_EPILOG
  327. }
  328. //
  329. // Close down this request record.
  330. //
  331. CProtectionRequestRecord::~CProtectionRequestRecord()
  332. {
  333. if (m_bInitialized)
  334. {
  335. ClearList();
  336. m_bInitialized = FALSE;
  337. }
  338. }
  339. inline VOID
  340. CProtectionRequestRecord::ClearList()
  341. {
  342. CStringListEntry *pTop;
  343. while (pTop = (CStringListEntry*)SxspInterlockedPopEntrySList(&m_ListHeader))
  344. {
  345. FUSION_DELETE_SINGLETON(pTop);
  346. }
  347. }
  348. inline BOOL
  349. CProtectionRequestRecord::AddSubFile(
  350. const CBaseStringBuffer &rbuffRelChange
  351. )
  352. {
  353. CStringListEntry *pairing = NULL;
  354. BOOL fSuccess = FALSE;
  355. FN_TRACE_WIN32(fSuccess);
  356. if (!SxspInterlockedCompareExchange(&m_ulInRecoveryMode, 0, 0))
  357. {
  358. IFALLOCFAILED_EXIT(pairing = new CStringListEntry);
  359. IFW32FALSE_EXIT(pairing->m_sbText.Win32Assign(rbuffRelChange));
  360. //
  361. // Add it to the list (atomically, to boot!)
  362. //
  363. ::SxspInterlockedPushEntrySList(&m_ListHeader, pairing);
  364. pairing = NULL;
  365. }
  366. fSuccess = TRUE;
  367. Exit:
  368. if (pairing)
  369. {
  370. //
  371. // The setup or something like that failed - release it here.
  372. //
  373. FUSION_DELETE_SINGLETON(pairing);
  374. }
  375. return fSuccess;
  376. }
  377. inline BOOL
  378. CProtectionRequestRecord::Initialize(
  379. const CBaseStringBuffer &rsbAssemblyDirectoryName,
  380. const CBaseStringBuffer &rsbKeyString,
  381. CProtectionRequestList* ParentList,
  382. PVOID pvRequestRecord,
  383. DWORD dwAction
  384. )
  385. {
  386. BOOL fSuccess = FALSE;
  387. FN_TRACE_WIN32(fSuccess);
  388. m_sbAssemblyDirectoryName.Clear();
  389. m_sbKeyValue.Clear();
  390. m_sbManifestPath.Clear();
  391. SxspInitializeSListHead(&m_ListHeader);
  392. PARAMETER_CHECK(ParentList != NULL);
  393. PARAMETER_CHECK(pvRequestRecord != NULL);
  394. m_pParent = ParentList;
  395. m_dwAction = dwAction;
  396. m_pvProtection = (PSXS_PROTECT_DIRECTORY)pvRequestRecord;
  397. IFW32FALSE_EXIT(m_sbAssemblyStore.Win32Assign(m_pvProtection->pwszDirectory, (m_pvProtection->pwszDirectory != NULL) ? ::wcslen(m_pvProtection->pwszDirectory) : 0));
  398. IFW32FALSE_EXIT(m_sbAssemblyDirectoryName.Win32Assign(rsbAssemblyDirectoryName));
  399. IFW32FALSE_EXIT(m_sbKeyValue.Win32Assign(rsbKeyString));
  400. fSuccess = TRUE;
  401. Exit:
  402. return fSuccess;
  403. }
  404. //
  405. // Bad form: This returns a BOOL to indicate whether or not this class
  406. // was able to pop something off the list. It's no good to ask "is the
  407. // list empty," since that's not provided with this list class.
  408. //
  409. inline BOOL
  410. CProtectionRequestRecord::PopNextFileChange(CBaseStringBuffer &Dest)
  411. {
  412. BOOL fFound = FALSE;
  413. CStringListEntry *pPairing;
  414. Dest.Clear();
  415. pPairing = (CStringListEntry*)SxspInterlockedPopEntrySList(&m_ListHeader);
  416. if (pPairing)
  417. {
  418. Dest.Win32Assign(pPairing->m_sbText);
  419. FUSION_DELETE_SINGLETON(pPairing);
  420. fFound = TRUE;
  421. }
  422. return fFound;
  423. }
  424. //
  425. // "If a thread dies in winlogon, and no kernel debugger is attached, does it
  426. // bugcheck the system?" - From 'The Zen of Dodgy Code'
  427. //
  428. DWORD
  429. CProtectionRequestList::ProtectionNormalThreadProc(PVOID pvParam)
  430. {
  431. BOOL fSuccess = FALSE;
  432. CProtectionRequestRecord *pRequestRecord = NULL;
  433. CProtectionRequestList *pThis = NULL;
  434. __try
  435. {
  436. pRequestRecord = static_cast<CProtectionRequestRecord*>(pvParam);
  437. if (pRequestRecord)
  438. {
  439. pThis = pRequestRecord->GetParent();
  440. }
  441. if (pThis)
  442. {
  443. fSuccess = pThis->ProtectionNormalThreadProcWrapped(pRequestRecord);
  444. }
  445. }
  446. __except(SXS_SFC_EXCEPTION_SETTING)
  447. {
  448. // Don't take down winlogon...
  449. }
  450. return static_cast<DWORD>(fSuccess);
  451. }
  452. BOOL
  453. CProtectionRequestList::Initialize()
  454. {
  455. BOOL fSuccess = FALSE;
  456. FN_TRACE_WIN32(fSuccess);
  457. ASSERT(m_pInternalList == NULL);
  458. ::InitializeCriticalSectionAndSpinCount(&m_cSection, INITIALIZE_CRITICAL_SECTION_AND_SPIN_COUNT_ALLOCATE_NOW);
  459. ::InitializeCriticalSectionAndSpinCount(&m_cInstallerCriticalSection, INITIALIZE_CRITICAL_SECTION_AND_SPIN_COUNT_ALLOCATE_NOW);
  460. IFW32FALSE_EXIT(::SxspAtExit(this));
  461. IFALLOCFAILED_EXIT(m_pInternalList = new COurInternalTable);
  462. IFW32FALSE_EXIT(m_pInternalList->Initialize());
  463. IFALLOCFAILED_EXIT(m_pInstallsTable = new CInstallsInProgressTable);
  464. IFW32FALSE_EXIT(m_pInstallsTable->Initialize());
  465. //
  466. // Manifest protection stuff
  467. //
  468. ::SxspInitializeSListHead(&m_ManifestEditList);
  469. m_hManifestEditHappened = ::CreateEventW(NULL, TRUE, FALSE, NULL);
  470. if (m_hManifestEditHappened == NULL)
  471. {
  472. TRACE_WIN32_FAILURE_ORIGINATION(CreateEventW);
  473. goto Exit;
  474. }
  475. ASSERT(m_pInternalList != NULL);
  476. ASSERT(m_pInstallsTable != NULL);
  477. fSuccess = TRUE;
  478. Exit:
  479. return fSuccess;
  480. }
  481. CProtectionRequestList::CProtectionRequestList()
  482. : m_pInternalList(NULL), m_pInstallsTable(NULL),
  483. m_hManifestEditHappened(INVALID_HANDLE_VALUE),
  484. m_ulIsAThreadServicingManifests(0)
  485. {
  486. }
  487. CProtectionRequestList::~CProtectionRequestList()
  488. {
  489. ::DeleteCriticalSection(&m_cSection);
  490. ::DeleteCriticalSection(&m_cInstallerCriticalSection);
  491. COurInternalTable *pTempListing = m_pInternalList;
  492. CInstallsInProgressTable *pInstalls = m_pInstallsTable;
  493. m_pInternalList = NULL;
  494. m_pInternalList = NULL;
  495. if (pTempListing != NULL)
  496. {
  497. pTempListing->Clear(this, &CProtectionRequestList::ClearProtectionItems);
  498. FUSION_DELETE_SINGLETON(pTempListing);
  499. }
  500. if (pInstalls != NULL)
  501. {
  502. pInstalls->ClearNoCallback();
  503. FUSION_DELETE_SINGLETON(pInstalls);
  504. }
  505. }
  506. BOOL
  507. CProtectionRequestList::IsSfcIgnoredStoreSubdir(PCWSTR wsz)
  508. {
  509. FN_TRACE();
  510. ASSERT(m_arrIgnorableSubdirs);
  511. for (SIZE_T i = 0; i < m_cIgnorableSubdirs; i++)
  512. {
  513. if (!::FusionpStrCmpI(m_arrIgnorableSubdirs[i], wsz))
  514. {
  515. return TRUE;
  516. }
  517. }
  518. return FALSE;
  519. }
  520. BOOL
  521. CProtectionRequestList::AttemptRemoveItem(CProtectionRequestRecord *AttemptRemoval)
  522. {
  523. //
  524. // This quickly indicates that the progress is complete and just returns to
  525. // the caller.
  526. //
  527. const CBaseStringBuffer &sbKey = AttemptRemoval->GetChangeBasePath();
  528. BOOL fSuccess = FALSE;
  529. CSxsLockCriticalSection lock(m_cSection);
  530. FN_TRACE_WIN32(fSuccess);
  531. PARAMETER_CHECK(AttemptRemoval != NULL);
  532. IFW32FALSE_EXIT(lock.Lock());
  533. //
  534. // This item is no longer in service. Please check the item and
  535. // try your call again. The nice thing is that Remove on CStringPtrTable
  536. // knows to delete the value lickety-split before returning. This isn't
  537. // such a bad thing, but it's ... different.
  538. //
  539. m_pInternalList->Remove(sbKey, NULL);
  540. fSuccess = TRUE;
  541. Exit:
  542. return fSuccess;
  543. }
  544. BOOL
  545. CProtectionRequestList::AddRequest(
  546. PSXS_PROTECT_DIRECTORY pProtect,
  547. PCWSTR pcwszDirName,
  548. SIZE_T cchName,
  549. DWORD dwAction
  550. )
  551. {
  552. BOOL fSuccess = FALSE;
  553. bool fIsManifestEdit = false;
  554. BOOL fIsIgnorable;
  555. BOOL fNewAddition = FALSE;
  556. CSmallStringBuffer sbTemp;
  557. CSmallStringBuffer sbAssemblyDirectoryName;
  558. CSmallStringBuffer sbRequestText;
  559. CSmallStringBuffer buffManifestsDirectoryName;
  560. CSmallStringBuffer buffManifestsShortDirectoryName;
  561. CProtectionRequestRecord *pRecord = NULL;
  562. CProtectionRequestRecord *ppFoundInTable = NULL;
  563. CSxsLockCriticalSection lock(m_cSection);
  564. FN_TRACE_WIN32(fSuccess);
  565. //
  566. // The key here is the first characters (up to the first slash) in the
  567. // notification name. If there's no slash in the notification name, then
  568. // we can ignore this change request, since nothing important happened.
  569. //
  570. IFW32FALSE_EXIT(sbTemp.Win32Assign(pProtect->pwszDirectory, (pProtect->pwszDirectory != NULL) ? ::wcslen(pProtect->pwszDirectory) : 0));
  571. IFW32FALSE_EXIT(sbRequestText.Win32Assign(pcwszDirName, cchName));
  572. IFW32FALSE_EXIT(sbRequestText.Win32GetFirstPathElement(sbAssemblyDirectoryName));
  573. fIsIgnorable = IsSfcIgnoredStoreSubdir(sbAssemblyDirectoryName);
  574. fIsManifestEdit = !::FusionpStrCmpI(sbAssemblyDirectoryName, MANIFEST_ROOT_DIRECTORY_NAME);
  575. if (!fIsManifestEdit)
  576. {
  577. DWORD dwTemp;
  578. CStringBufferAccessor acc;
  579. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffManifestsDirectoryName));
  580. IFW32FALSE_EXIT(buffManifestsDirectoryName.Win32AppendPathElement(MANIFEST_ROOT_DIRECTORY_NAME, NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) - 1));
  581. acc.Attach(&buffManifestsShortDirectoryName);
  582. IFW32ZERO_ORIGINATE_AND_EXIT(dwTemp = ::GetShortPathNameW(buffManifestsDirectoryName, acc.GetBufferPtr(), acc.GetBufferCchAsDWORD()));
  583. while (dwTemp >= acc.GetBufferCchAsDWORD())
  584. {
  585. acc.Detach();
  586. IFW32FALSE_EXIT(buffManifestsShortDirectoryName.Win32ResizeBuffer(dwTemp, eDoNotPreserveBufferContents));
  587. acc.Attach(&buffManifestsShortDirectoryName);
  588. IFW32ZERO_ORIGINATE_AND_EXIT(dwTemp = ::GetShortPathNameW(buffManifestsDirectoryName, acc.GetBufferPtr(), acc.GetBufferCchAsDWORD()));
  589. }
  590. acc.Detach();
  591. // Ok all that work and now we finally have the manifests directory name as a short string. Let's abuse buffManifestsShortDirectoryName
  592. // to just hold the short name of the manifests directory.
  593. IFW32FALSE_EXIT(buffManifestsShortDirectoryName.Win32GetLastPathElement(buffManifestsDirectoryName));
  594. if (::FusionpCompareStrings(
  595. buffManifestsDirectoryName,
  596. sbAssemblyDirectoryName,
  597. true) == 0)
  598. {
  599. // Convert the directory name to its proper long form
  600. IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32Assign(MANIFEST_ROOT_DIRECTORY_NAME, NUMBER_OF(MANIFEST_ROOT_DIRECTORY_NAME) - 1));
  601. fIsManifestEdit = true;
  602. }
  603. }
  604. if ((fIsIgnorable) && (!fIsManifestEdit))
  605. {
  606. #if DBG
  607. //
  608. // We get a lot of these.
  609. //
  610. if (::FusionpStrCmpI(sbAssemblyDirectoryName, L"InstallTemp") != 0)
  611. {
  612. ::FusionpDbgPrintEx(
  613. FUSION_DBG_LEVEL_WFP,
  614. "SXS.DLL: %s() - %ls is ignorable (%d)\n",
  615. __FUNCTION__,
  616. static_cast<PCWSTR>(sbAssemblyDirectoryName),
  617. fIsIgnorable
  618. );
  619. }
  620. #endif
  621. fSuccess = TRUE;
  622. goto Exit;
  623. }
  624. //
  625. // The "key" value here is the full path to the assembly that we're protecting.
  626. // This is what we'll store in the table.
  627. //
  628. IFW32FALSE_EXIT(sbTemp.Win32AppendPathElement(sbAssemblyDirectoryName));
  629. if (fIsManifestEdit)
  630. {
  631. CStringListEntry *pEntry = NULL;
  632. ULONG ulWasSomeoneServicing = 0;
  633. //
  634. // Create a new manifest edit slot, add it to the list of items that are being
  635. // serviced.
  636. //
  637. IFALLOCFAILED_EXIT(pEntry = new CStringListEntry);
  638. if (!pEntry->m_sbText.Win32Assign(sbRequestText))
  639. {
  640. TRACE_WIN32_FAILURE(m_sbText.Win32Assign);
  641. FUSION_DELETE_SINGLETON(pEntry);
  642. pEntry = NULL;
  643. goto Exit;
  644. }
  645. SxspInterlockedPushEntrySList(&m_ManifestEditList, pEntry);
  646. pEntry = NULL;
  647. //
  648. // Tell anyone that's listening that we have a new manifest edit here
  649. //
  650. SetEvent(m_hManifestEditHappened);
  651. //
  652. // See if someone is servicing the queue at the moment
  653. //
  654. ulWasSomeoneServicing = SxspInterlockedCompareExchange(&m_ulIsAThreadServicingManifests, 1, 0);
  655. if (!ulWasSomeoneServicing)
  656. {
  657. QueueUserWorkItem(ProtectionManifestThreadProc, (PVOID)this, WT_EXECUTEDEFAULT);
  658. }
  659. fSuccess = TRUE;
  660. goto Exit;
  661. }
  662. //
  663. // At this point, we need to see if the chunk that we identified is currently
  664. // in the list of things to be validated. If not, it gets added and a thread
  665. // is spun off to work on it. Otherwise, an entry may already exist in a
  666. // thread that's being serviced, and so it needs to be deleted.
  667. //
  668. IFW32FALSE_EXIT(lock.Lock());
  669. m_pInternalList->Find(sbTemp, ppFoundInTable);
  670. if (!ppFoundInTable)
  671. {
  672. IFALLOCFAILED_EXIT(pRecord = new CProtectionRequestRecord);
  673. #if DBG
  674. ::FusionpDbgPrintEx(
  675. FUSION_DBG_LEVEL_WFP,
  676. "SXS.DLL: %s() - Creating protection record for %ls:\n"
  677. "\tKey = %ls\n"
  678. "\tManifest? = %d\n"
  679. "\tProtectionRecord = %p\n"
  680. "\tAction = %d\n",
  681. __FUNCTION__,
  682. static_cast<PCWSTR>(sbAssemblyDirectoryName),
  683. static_cast<PCWSTR>(sbTemp),
  684. fIsManifestEdit,
  685. pProtect,
  686. dwAction);
  687. #endif
  688. IFW32FALSE_EXIT(pRecord->Initialize(
  689. sbAssemblyDirectoryName,
  690. sbTemp,
  691. this,
  692. pProtect,
  693. dwAction));
  694. //
  695. // Add this first request to be serviced, then spin a thread to start it.
  696. //
  697. m_pInternalList->Insert(sbTemp, pRecord);
  698. fNewAddition = TRUE;
  699. //
  700. // A little bookkeeping ... so we don't accidentally use it later.
  701. //
  702. ppFoundInTable = pRecord;
  703. pRecord = NULL;
  704. }
  705. //
  706. // If we actually got something into the table...
  707. //
  708. if (ppFoundInTable)
  709. {
  710. ppFoundInTable->AddSubFile(sbRequestText);
  711. //
  712. // If this is a new thing in the table (ie: we inserted it ourselves)
  713. // then we should go and spin up a thread for it.
  714. //
  715. if (fNewAddition)
  716. {
  717. QueueUserWorkItem(ProtectionNormalThreadProc, (PVOID)ppFoundInTable, WT_EXECUTEDEFAULT);
  718. }
  719. }
  720. fSuccess = TRUE;
  721. Exit:
  722. DWORD dwLastError = ::FusionpGetLastWin32Error();
  723. if (pRecord)
  724. {
  725. //
  726. // If this is still set, something bad happened in the process of trying to
  727. // create/find this object. Delete it here.
  728. //
  729. FUSION_DELETE_SINGLETON(pRecord);
  730. pRecord = NULL;
  731. }
  732. ::FusionpSetLastWin32Error(dwLastError);
  733. return fSuccess;
  734. }
  735. static BYTE p_bProtectionListBuffer[ sizeof(CProtectionRequestList) * 2 ];
  736. PCWSTR CProtectionRequestList::m_arrIgnorableSubdirs[] =
  737. {
  738. ASSEMBLY_INSTALL_TEMP_DIR_NAME,
  739. POLICY_ROOT_DIRECTORY_NAME,
  740. REGISTRY_BACKUP_ROOT_DIRECTORY_NAME
  741. };
  742. SIZE_T CProtectionRequestList::m_cIgnorableSubdirs =
  743. NUMBER_OF(CProtectionRequestList::m_arrIgnorableSubdirs);
  744. BOOL
  745. SxspIsSfcIgnoredStoreSubdir(PCWSTR pwsz)
  746. {
  747. return CProtectionRequestList::IsSfcIgnoredStoreSubdir(pwsz);
  748. }
  749. BOOL
  750. SxspConstructProtectionList()
  751. {
  752. CProtectionRequestList *pTemp = NULL;
  753. BOOL fSuccess = FALSE;
  754. FN_TRACE_WIN32(fSuccess);
  755. //
  756. // This only gets called once, if they know what's good for them.
  757. //
  758. ASSERT(!g_ProtectionRequestList);
  759. //
  760. // Construct - this should never fail, but if it does, there's trouble.
  761. //
  762. pTemp = new (&p_bProtectionListBuffer) CProtectionRequestList;
  763. if (!pTemp)
  764. {
  765. ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, "SXS: %s() - Failed placement new of CProtectionRequestList????\n", __FUNCTION__);
  766. ::FusionpSetLastWin32Error(FUSION_WIN32_ALLOCFAILED_ERROR);
  767. TRACE_WIN32_FAILURE_ORIGINATION(new(CProtectionRequestList));
  768. goto Exit;
  769. }
  770. IFW32FALSE_EXIT(pTemp->Initialize());
  771. g_ProtectionRequestList = pTemp;
  772. pTemp = NULL;
  773. //
  774. // Create our logon event.
  775. //
  776. IFW32NULL_EXIT(g_hSxsLoginEvent = CreateEventW(NULL, TRUE, FALSE, NULL));
  777. fSuccess = TRUE;
  778. Exit:
  779. if (pTemp)
  780. {
  781. //
  782. // If this is still set, then something failed somewhere in the construction
  783. // code for the protection system. We don't want to delete it, per-se, but
  784. // we need to just null out everything.
  785. //
  786. g_ProtectionRequestList = NULL;
  787. pTemp = NULL;
  788. g_hSxsLoginEvent = NULL;
  789. }
  790. return fSuccess;
  791. }
  792. BOOL WINAPI
  793. SxsProtectionNotifyW(
  794. PVOID pvCookie,
  795. PCWSTR wsChangeText,
  796. SIZE_T cchChangeText,
  797. DWORD dwChangeAction
  798. )
  799. {
  800. BOOL fSuccess;
  801. #if YOU_ARE_HAVING_ANY_WIERDNESS_WITH_SFC_AND_SXS
  802. fSuccess = TRUE;
  803. return TRUE;
  804. #else
  805. if (::FusionpDbgWouldPrintAtFilterLevel(FUSION_DBG_LEVEL_FILECHANGENOT))
  806. {
  807. const USHORT Length = (cchChangeText > UNICODE_STRING_MAX_CHARS) ? UNICODE_STRING_MAX_BYTES : ((USHORT) (cchChangeText * sizeof(WCHAR)));
  808. const UNICODE_STRING u = { Length, Length, const_cast<PWSTR>(wsChangeText) };
  809. ::FusionpDbgPrintEx(
  810. FUSION_DBG_LEVEL_FILECHANGENOT,
  811. "[%lx.%lx: %wZ] SXS FCN (cookie, action, text): %p, %lu, \"%wZ\"\n",
  812. HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess),
  813. HandleToULong(NtCurrentTeb()->ClientId.UniqueThread),
  814. &NtCurrentPeb()->ProcessParameters->ImagePathName,
  815. pvCookie,
  816. dwChangeAction,
  817. &u);
  818. }
  819. //
  820. // If we're not accepting notifications, then quit out immediately.
  821. //
  822. if (!s_fIsSfcAcceptingNotifications)
  823. {
  824. fSuccess = TRUE;
  825. goto Exit;
  826. }
  827. //
  828. // Having done this in the wrong order is also a Bad Bad Thing
  829. //
  830. ASSERT2_NTC(g_ProtectionRequestList != NULL, "SXS.DLL: Protection - Check order of operations, g_ProtectionRequestList is invalid!!\n");
  831. //
  832. // Let's not take down winlogon, shall we?
  833. //
  834. __try
  835. {
  836. fSuccess = g_ProtectionRequestList->AddRequest(
  837. (PSXS_PROTECT_DIRECTORY)pvCookie,
  838. wsChangeText,
  839. cchChangeText,
  840. dwChangeAction);
  841. }
  842. __except(SXS_SFC_EXCEPTION_SETTING)
  843. {
  844. }
  845. fSuccess = TRUE;
  846. Exit:
  847. return fSuccess;
  848. #endif
  849. }
  850. BOOL
  851. CProtectionRequestList::ProtectionManifestThreadProcNoSEH(LPVOID pvParam)
  852. {
  853. BOOL fSuccess = FALSE;
  854. CProtectionRequestList *pThis;
  855. FN_TRACE_WIN32(fSuccess);
  856. PARAMETER_CHECK(pvParam != NULL);
  857. pThis = reinterpret_cast<CProtectionRequestList*>(pvParam);
  858. IFW32FALSE_EXIT(pThis->ProtectionManifestThreadProcWrapped());
  859. fSuccess = TRUE;
  860. Exit:
  861. return fSuccess;
  862. }
  863. DWORD
  864. CProtectionRequestList::ProtectionManifestThreadProc(LPVOID pvParam)
  865. {
  866. BOOL fSuccess = FALSE;
  867. __try
  868. {
  869. fSuccess = ProtectionManifestThreadProcNoSEH(pvParam);
  870. }
  871. __except(SXS_SFC_EXCEPTION_SETTING)
  872. {
  873. //
  874. // Whoops..
  875. //
  876. }
  877. return static_cast<DWORD>(fSuccess);
  878. }
  879. BOOL
  880. CProtectionRequestList::ProtectionManifestSingleManifestWorker(
  881. const CStringListEntry *pEntry
  882. )
  883. {
  884. BOOL fSuccess = FALSE;
  885. FN_TRACE_WIN32(fSuccess);
  886. CStringBuffer sbAssemblyDirectoryName;
  887. CStringBuffer sbManifestPath;
  888. CAssemblyRecoveryInfo RecoverInfo;
  889. SxsRecoveryResult RecoverResult;
  890. HashValidateResult HashValidResult;
  891. BOOL fCatalogPresent = FALSE;
  892. bool fNoAssembly;
  893. PARAMETER_CHECK(pEntry);
  894. //
  895. // Calculate the name of the assembly based on the middling part of
  896. // the string
  897. //
  898. IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32Assign(pEntry->m_sbText));
  899. IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32RemoveFirstPathElement());
  900. IFW32FALSE_EXIT(sbAssemblyDirectoryName.Win32ClearPathExtension());
  901. if (sbAssemblyDirectoryName.Cch() == 0)
  902. FN_SUCCESSFUL_EXIT();
  903. //
  904. // Try mashing this into an assembly name/recovery info
  905. //
  906. IFW32FALSE_EXIT(RecoverInfo.AssociateWithAssembly(sbAssemblyDirectoryName, fNoAssembly));
  907. // If we couldn't figure out what this was for, we have to ignore it.
  908. if (fNoAssembly)
  909. {
  910. #if DBG
  911. ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP,
  912. "SXS.DLL: %s() - File \"%ls\" in the manifest directory modified, but could not be mapped to an assembly. IGNORING.\n",
  913. __FUNCTION__,
  914. static_cast<PCWSTR>(pEntry->m_sbText));
  915. #endif
  916. FN_SUCCESSFUL_EXIT();
  917. }
  918. //
  919. // Now that we have the recovery info..
  920. //
  921. if (!RecoverInfo.GetHasCatalog())
  922. {
  923. #if DBG
  924. ::FusionpDbgPrintEx(
  925. FUSION_DBG_LEVEL_WFP,
  926. "SXS.DLL: %s() - Assembly %ls was in the registry, but without a catalog so we aren't protecting it\n",
  927. __FUNCTION__,
  928. static_cast<PCWSTR>(sbAssemblyDirectoryName));
  929. #endif
  930. FN_SUCCESSFUL_EXIT();
  931. }
  932. //
  933. // Resolve the manifest path, then validate
  934. //
  935. IFW32FALSE_EXIT(::SxspResolveAssemblyManifestPath(sbAssemblyDirectoryName, sbManifestPath));
  936. IFW32FALSE_EXIT(
  937. ::SxspVerifyFileHash(
  938. SVFH_RETRY_LOGIC_SIMPLE,
  939. sbManifestPath,
  940. RecoverInfo.GetSecurityInformation().GetManifestHash(),
  941. CALG_SHA1,
  942. HashValidResult));
  943. IFW32FALSE_EXIT(::SxspEnsureCatalogStillPresentForManifest(sbManifestPath, fCatalogPresent));
  944. //
  945. // Reinstall needed?
  946. //
  947. if ((HashValidResult != HashValidate_Matches) || !fCatalogPresent)
  948. IFW32FALSE_EXIT(this->PerformRecoveryOfAssembly(RecoverInfo, NULL, RecoverResult));
  949. fSuccess = TRUE;
  950. Exit:
  951. return fSuccess;
  952. }
  953. BOOL
  954. CProtectionRequestList::ProtectionManifestThreadProcWrapped()
  955. {
  956. BOOL fSuccess = FALSE;
  957. BOOL bFoundItemsThisTimeAround;
  958. CStringListEntry *pNextItem = NULL;
  959. DWORD dwWaitResult;
  960. FN_TRACE_WIN32(fSuccess);
  961. bFoundItemsThisTimeAround = FALSE;
  962. do
  963. {
  964. //
  965. // Yes, mother, we hear you.
  966. //
  967. ResetEvent(m_hManifestEditHappened);
  968. //
  969. // Pull the next thing off the list and service it.
  970. //
  971. while (pNextItem = (CStringListEntry*)SxspInterlockedPopEntrySList(&m_ManifestEditList))
  972. {
  973. bFoundItemsThisTimeAround = TRUE;
  974. if (!this->ProtectionManifestSingleManifestWorker(pNextItem))
  975. ::FusionpDbgPrintEx(
  976. FUSION_DBG_LEVEL_WFP,
  977. "SXS: %s - Processing work item %p failed\n", __FUNCTION__, pNextItem);
  978. FUSION_DELETE_SINGLETON(pNextItem);
  979. }
  980. //
  981. // Loaf about for a bit and see if anyone else has stuff for us to do
  982. //
  983. dwWaitResult = ::WaitForSingleObject(m_hManifestEditHappened, 3000);
  984. if (dwWaitResult == WAIT_TIMEOUT)
  985. {
  986. ::SxspInterlockedExchange(&m_ulIsAThreadServicingManifests, 0);
  987. break;
  988. }
  989. else if (dwWaitResult == WAIT_OBJECT_0)
  990. {
  991. continue;
  992. }
  993. else
  994. {
  995. TRACE_WIN32_FAILURE_ORIGINATION(WaitForSingleObject);
  996. goto Exit;
  997. }
  998. }
  999. while (true);
  1000. fSuccess = TRUE;
  1001. Exit:
  1002. return fSuccess;
  1003. }
  1004. BOOL
  1005. CProtectionRequestList::ProtectionNormalThreadProcWrapped(
  1006. CProtectionRequestRecord *pRequestRecord
  1007. )
  1008. {
  1009. BOOL fSuccess = FALSE;
  1010. FN_TRACE_WIN32(fSuccess);
  1011. CProtectionRequestRecord &rRequest = *pRequestRecord;
  1012. CProtectionRequestList *pRequestList = rRequest.GetParent();
  1013. BOOL fNeedsReinstall = FALSE;
  1014. BOOL fAssemblyNotFound = FALSE;
  1015. DWORD dwAsmPathAttribs;
  1016. SxsRecoveryResult RecoverResult;
  1017. CSmallStringBuffer buffFullPathOfChange;
  1018. CSmallStringBuffer buffAssemblyStore;
  1019. CSmallStringBuffer buffAssemblyRelativeChange;
  1020. bool fNoAssembly;
  1021. //
  1022. // The request's key value contains the full path of the assembly that
  1023. // is being modified, in theory. So, we can just use it locally.
  1024. //
  1025. CStringBuffer rbuffAssemblyDirectoryName;
  1026. const CBaseStringBuffer &rbuffAssemblyPath = rRequest.GetChangeBasePath();
  1027. CAssemblyRecoveryInfo &rRecoveryInfo = rRequest.GetRecoveryInfo();
  1028. CSecurityMetaData &rSecurityMetaData = rRecoveryInfo.GetSecurityInformation();
  1029. //
  1030. // this name could be changes because the assemblyName in the request could be a short name,
  1031. // in this case, we need reset the AssemblyName in rRequest
  1032. //
  1033. IFW32FALSE_EXIT(rbuffAssemblyDirectoryName.Win32Assign(rRequest.GetAssemblyDirectoryName()));
  1034. //
  1035. // This value should not change at all during this function. Save it here.
  1036. //
  1037. IFW32FALSE_EXIT(rRequest.GetAssemblyStore(buffAssemblyStore));
  1038. //
  1039. // The big question of the day - find out the recovery information for this
  1040. // assembly. See if there was a catalog at installation time (a) or find out
  1041. // whether or not there is a catalog for it right now.
  1042. //
  1043. IFW32FALSE_EXIT(rRecoveryInfo.AssociateWithAssembly(rbuffAssemblyDirectoryName, fNoAssembly));
  1044. // If we couldn't figure out what assembly this was for, we ignore it.
  1045. if (fNoAssembly)
  1046. FN_SUCCESSFUL_EXIT();
  1047. //
  1048. // if rbuffAssemblyName is different from the assemblyname from rRequest,
  1049. // it must be a short name, we must reset the AssemblyName in rRequest
  1050. //
  1051. StringComparisonResult scr;
  1052. IFW32FALSE_EXIT(rbuffAssemblyDirectoryName.Win32Compare(rRequest.GetAssemblyDirectoryName(), rRequest.GetAssemblyDirectoryName().Cch(), scr, NORM_IGNORECASE));
  1053. if (scr != eEquals)
  1054. IFW32FALSE_EXIT(rRequest.SetAssemblyDirectoryName(rbuffAssemblyDirectoryName));
  1055. if (rRecoveryInfo.GetInfoPrepared() == FALSE)
  1056. ORIGINATE_WIN32_FAILURE_AND_EXIT(RecoveryInfoCouldNotBePrepared, ERROR_PATH_NOT_FOUND);
  1057. if (!rRecoveryInfo.GetHasCatalog())
  1058. {
  1059. #if DBG
  1060. ::FusionpDbgPrintEx(
  1061. FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INFO,
  1062. "SXS.DLL: %s - Assembly %ls not registered with catalog, WFP ignoring it.\n",
  1063. __FUNCTION__,
  1064. static_cast<PCWSTR>(rbuffAssemblyDirectoryName));
  1065. #endif
  1066. fSuccess = TRUE;
  1067. goto Exit;
  1068. }
  1069. //
  1070. // See if it still exists...
  1071. //
  1072. dwAsmPathAttribs = ::GetFileAttributesW(rbuffAssemblyPath);
  1073. if (dwAsmPathAttribs == INVALID_FILE_ATTRIBUTES)
  1074. {
  1075. ::FusionpDbgPrintEx(
  1076. FUSION_DBG_LEVEL_WFP,
  1077. "SXS.DLL: WFP reinstalling assembly because GetFileAttributesW(\"%ls\") failed with win32 error %ld\n",
  1078. static_cast<PCWSTR>(rbuffAssemblyPath),
  1079. ::FusionpGetLastWin32Error());
  1080. fNeedsReinstall = TRUE;
  1081. goto DoReinstall;
  1082. }
  1083. //
  1084. // Otherwise, is it maybe not a directory for one reason or another?
  1085. //
  1086. else if (!(dwAsmPathAttribs & FILE_ATTRIBUTE_DIRECTORY))
  1087. {
  1088. #if DBG
  1089. ::FusionpDbgPrintEx(
  1090. FUSION_DBG_LEVEL_ERROR,
  1091. "SXS.DLL:%s - %ws isn't a directory, we should attempt to remove it?\n",
  1092. __FUNCTION__,
  1093. static_cast<PCWSTR>(rbuffAssemblyPath));
  1094. #endif
  1095. FN_SUCCESSFUL_EXIT();
  1096. }
  1097. //
  1098. // Find out whether or not the file is still OK by checking against the manifest
  1099. //
  1100. {
  1101. HashValidateResult HashValid;
  1102. CStringBuffer buffManifestFullPath;
  1103. BOOL fPresent;
  1104. IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory( buffFullPathOfChange ) );
  1105. IFW32FALSE_EXIT(::SxspCreateManifestFileNameFromTextualString(
  1106. 0,
  1107. SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST,
  1108. buffFullPathOfChange,
  1109. rSecurityMetaData.GetTextualIdentity(),
  1110. buffManifestFullPath) );
  1111. #if DBG
  1112. ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP,
  1113. "SXS.DLL:%s - Checking to see if manifest %ls is OK\n",
  1114. __FUNCTION__,
  1115. static_cast<PCWSTR>(buffManifestFullPath));
  1116. #endif
  1117. IFW32FALSE_EXIT(::SxspVerifyFileHash(
  1118. SVFH_RETRY_LOGIC_SIMPLE,
  1119. buffManifestFullPath,
  1120. rSecurityMetaData.GetManifestHash(),
  1121. CALG_SHA1,
  1122. HashValid));
  1123. #if DBG
  1124. FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP,
  1125. "SXS.DLL:%s - Manifest %ls checks %ls\n",
  1126. __FUNCTION__,
  1127. static_cast<PCWSTR>(buffManifestFullPath),
  1128. SxspHashValidateResultToString(HashValid) );
  1129. #endif
  1130. if ( HashValid != HashValidate_Matches )
  1131. {
  1132. fNeedsReinstall = TRUE;
  1133. goto DoReinstall;
  1134. }
  1135. //
  1136. // Let's just ensure the catalog is there - it's not necessary anymore
  1137. // for the protection pass, but it should be there if someone wants to
  1138. // repackage the assembly for distribution.
  1139. //
  1140. IFW32FALSE_EXIT(::SxspEnsureCatalogStillPresentForManifest(buffManifestFullPath, fPresent));
  1141. if ( !fPresent )
  1142. {
  1143. fNeedsReinstall = TRUE;
  1144. goto DoReinstall;
  1145. }
  1146. }
  1147. //
  1148. // Now we can loop through the items in our list of things to be evaluated and
  1149. // see if any of them are bad (or missing, or whatever.)
  1150. //
  1151. // Start out by touching the thing that indicates the last time we spun through
  1152. // here and looked at the file list.
  1153. //
  1154. while (!fNeedsReinstall)
  1155. {
  1156. const CMetaDataFileElement* pFileDataElement = NULL;
  1157. HashValidateResult Valid = HashValidate_OtherProblems;
  1158. BOOL fFileNotFound;
  1159. if (!rRequest.PopNextFileChange(buffAssemblyRelativeChange))
  1160. {
  1161. break;
  1162. }
  1163. IFW32FALSE_EXIT(buffAssemblyRelativeChange.Win32RemoveFirstPathElement());
  1164. //
  1165. // The change here is really to the top level directory - don't
  1166. // bother doing anything in this case. Maybe we should catch
  1167. // this beforehand so we don't do the work of parsing?
  1168. //
  1169. if (buffAssemblyRelativeChange.Cch() == 0)
  1170. {
  1171. continue;
  1172. }
  1173. //
  1174. // Acquire the security data
  1175. //
  1176. IFW32FALSE_EXIT( rSecurityMetaData.GetFileMetaData(
  1177. buffAssemblyRelativeChange,
  1178. pFileDataElement ) );
  1179. //
  1180. // There wasn't any data for this file? Means we don't know about the file, so we
  1181. // probably should do /something/ about it. For now, however, with the agreeance of
  1182. // the backup team, we let sleeping files lie.
  1183. if ( pFileDataElement == NULL )
  1184. {
  1185. //
  1186. // because short-filename is not stored in registry, so for a filename, which might be long-pathname
  1187. // or a short pathname, if we try out all entries in the Registry and still can not find it,
  1188. // we assume it is a short filename. In this case, we would verify the assembly, if it is not intact,
  1189. // do reinstall for the assembly....
  1190. //
  1191. DWORD dwResult;
  1192. IFW32FALSE_EXIT(::SxspValidateEntireAssembly(
  1193. SXS_VALIDATE_ASM_FLAG_CHECK_EVERYTHING,
  1194. rRecoveryInfo,
  1195. dwResult));
  1196. fNeedsReinstall = ( dwResult != SXS_VALIDATE_ASM_FLAG_VALID_PERFECT );
  1197. goto DoReinstall;
  1198. }
  1199. //
  1200. // And build the full path of the change via:
  1201. //
  1202. // sbAssemblyPath + \ + buffAssemblyRelativeChange
  1203. //
  1204. IFW32FALSE_EXIT(buffFullPathOfChange.Win32Assign(rbuffAssemblyPath));
  1205. IFW32FALSE_EXIT(buffFullPathOfChange.Win32AppendPathElement(buffAssemblyRelativeChange));
  1206. //
  1207. // We really should check the return value here, but the
  1208. // function is smart enough to set Valid to something useful
  1209. // before returning. A failure here should NOT be an IFW32FALSE_EXIT
  1210. // call, mostly because we don't want to stop protecting this
  1211. // assembly just because it failed with a FILE_NOT_FOUND or other
  1212. // such.
  1213. //
  1214. IFW32FALSE_EXIT_UNLESS(::SxspValidateAllFileHashes(
  1215. *pFileDataElement,
  1216. buffFullPathOfChange,
  1217. Valid ),
  1218. FILE_OR_PATH_NOT_FOUND(::FusionpGetLastWin32Error()),
  1219. fFileNotFound );
  1220. if ( ( Valid != HashValidate_Matches ) || fFileNotFound )
  1221. {
  1222. fNeedsReinstall = TRUE;
  1223. goto DoReinstall;
  1224. }
  1225. } /* while */
  1226. DoReinstall:
  1227. //
  1228. // If somewhere along the line we were supposed to reinstall, then we
  1229. // do so.
  1230. //
  1231. if (fNeedsReinstall)
  1232. {
  1233. //
  1234. // We have to indicate that all changes from point A to point B need
  1235. // to be ignored.
  1236. //
  1237. rRequest.MarkInRecoveryMode(TRUE);
  1238. PerformRecoveryOfAssembly(rRecoveryInfo, NULL, RecoverResult);
  1239. rRequest.ClearList();
  1240. rRequest.MarkInRecoveryMode(FALSE);
  1241. //
  1242. // HACKHACK jonwis 1/20/2001 - Stop failing assertions because lasterror
  1243. // is set wrong by one of the above.
  1244. //
  1245. ::FusionpSetLastWin32Error(0);
  1246. }
  1247. fSuccess = TRUE;
  1248. Exit:
  1249. const DWORD dwLastErrorSaved = ::FusionpGetLastWin32Error();
  1250. //
  1251. // We are done - this always succeeds. The explanation is hairy.
  1252. //
  1253. if (pRequestList->AttemptRemoveItem(&rRequest))
  1254. {
  1255. ::FusionpSetLastWin32Error(dwLastErrorSaved);
  1256. }
  1257. else
  1258. {
  1259. if (!fSuccess)
  1260. {
  1261. // This seems bad that we're losing the original failure; let's at least spew it.
  1262. ::FusionpDbgPrintEx(
  1263. FUSION_DBG_LEVEL_ERROR,
  1264. "SXS.DLL: %s() losing original win32 error code of %d; replaced with %d from CProtectionRequestList::AttemptRemoveItem() call.\n",
  1265. __FUNCTION__,
  1266. dwLastErrorSaved,
  1267. ::FusionpGetLastWin32Error());
  1268. }
  1269. fSuccess = FALSE;
  1270. }
  1271. return fSuccess;
  1272. }
  1273. BOOL WINAPI
  1274. SxsProtectionUserLogonEvent()
  1275. {
  1276. return SetEvent(g_hSxsLoginEvent);
  1277. }
  1278. BOOL WINAPI
  1279. SxsProtectionUserLogoffEvent()
  1280. {
  1281. return ResetEvent(g_hSxsLoginEvent);
  1282. }
  1283. BOOL
  1284. CProtectionRequestList::PerformRecoveryOfAssembly(
  1285. const CAssemblyRecoveryInfo &RecoverInfo,
  1286. CRecoveryCopyQueue *pvPotentialQueue,
  1287. SxsRecoveryResult &ResultOut
  1288. )
  1289. {
  1290. BOOL fSuccess = FALSE;
  1291. FN_TRACE_WIN32(fSuccess);
  1292. BOOL fFound = FALSE;
  1293. CRecoveryJobTableEntry *pNewEntry, **pExistingEntry;
  1294. DWORD dwRecoveryLastError = ERROR_SUCCESS;
  1295. IFALLOCFAILED_EXIT(pNewEntry = new CRecoveryJobTableEntry);
  1296. IFW32FALSE_EXIT(pNewEntry->Initialize());
  1297. {
  1298. CSxsLockCriticalSection lock(m_cInstallerCriticalSection);
  1299. IFW32FALSE_EXIT(lock.Lock());
  1300. IFW32FALSE_EXIT(
  1301. m_pInstallsTable->FindOrInsertIfNotPresent(
  1302. RecoverInfo.GetAssemblyDirectoryName(),
  1303. pNewEntry,
  1304. &pExistingEntry,
  1305. &fFound));
  1306. }
  1307. //
  1308. // Great, it was either inserted or it was already there - if not already there,
  1309. // then we'll take care if it.
  1310. //
  1311. if (!fFound)
  1312. {
  1313. BOOL fSuccess = FALSE;
  1314. IFW32FALSE_EXIT(pNewEntry->StartInstallation());
  1315. //
  1316. // Perform the recovery.
  1317. //
  1318. fSuccess = ::SxspRecoverAssembly(RecoverInfo, pvPotentialQueue, ResultOut);
  1319. if (!fSuccess)
  1320. dwRecoveryLastError = ::FusionpGetLastWin32Error();
  1321. #if DBG
  1322. ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP,
  1323. "SXS: %s() - RecoverAssembly returned Result = %ls, fSuccess = %s, LastError = 0x%08x\n",
  1324. __FUNCTION__,
  1325. SxspRecoveryResultToString(ResultOut),
  1326. fSuccess ? "true" : "false",
  1327. dwRecoveryLastError);
  1328. #endif
  1329. //
  1330. // Tell this entry that it's all done. This releases the other people
  1331. // that were waiting on the event to get done as well.
  1332. //
  1333. IFW32FALSE_EXIT(pNewEntry->InstallationComplete(fSuccess, ResultOut, dwRecoveryLastError));
  1334. //
  1335. // And now delete the item from the list.
  1336. //
  1337. {
  1338. CSxsLockCriticalSection lock2(m_cInstallerCriticalSection);
  1339. IFW32FALSE_EXIT(lock2.Lock());
  1340. IFW32FALSE_EXIT(m_pInstallsTable->Remove(RecoverInfo.GetAssemblyDirectoryName()));
  1341. }
  1342. }
  1343. else
  1344. {
  1345. DWORD dwLastError;
  1346. IFW32FALSE_EXIT((*pExistingEntry)->WaitUntilCompleted(ResultOut, fSuccess, dwLastError));
  1347. #if DBG
  1348. ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_WFP,
  1349. "SXS: %s() - WaitUntilCompleted returned Result = %ls, fInstalledOk = %s, LastError = 0x%08x\n",
  1350. __FUNCTION__,
  1351. ::SxspRecoveryResultToString(ResultOut),
  1352. fSuccess ? "true" : "false",
  1353. dwLastError);
  1354. #endif
  1355. dwRecoveryLastError = dwLastError;
  1356. }
  1357. if (dwRecoveryLastError != ERROR_SUCCESS)
  1358. ORIGINATE_WIN32_FAILURE_AND_EXIT(RecoveryFailed, dwRecoveryLastError);
  1359. fSuccess = TRUE;
  1360. Exit:
  1361. return fSuccess;
  1362. }
  1363. VOID WINAPI
  1364. SxsProtectionEnableProcessing(BOOL fActivityEnabled)
  1365. {
  1366. s_fIsSfcAcceptingNotifications = fActivityEnabled;
  1367. }