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.

1693 lines
50 KiB

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