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.

1349 lines
36 KiB

  1. /*
  2. Cache handling functions for use in kernel32.dll
  3. VadimB
  4. */
  5. #include "sdbp.h"
  6. #define _APPHELP_CACHE_INIT_
  7. #include "ahcache.h"
  8. #include <safeboot.h>
  9. NTSTATUS
  10. ApphelpCacheControlValidateParameters(
  11. IN PAHCACHESERVICEDATA pServiceData,
  12. OUT PUNICODE_STRING pFileName,
  13. OUT HANDLE* pFileHandle
  14. );
  15. NTSYSCALLAPI
  16. NTSTATUS
  17. NtApphelpCacheControl(
  18. IN APPHELPCACHESERVICECLASS Service,
  19. IN OUT PVOID ServiceData
  20. );
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text(PAGE, ApphelpCacheInitialize) // INIT ?
  23. #pragma alloc_text(PAGE, ApphelpDuplicateUnicodeString)
  24. #pragma alloc_text(PAGE, ApphelpFreeUnicodeString)
  25. #pragma alloc_text(PAGE, ApphelpCacheQueryFileInformation)
  26. #pragma alloc_text(PAGE, ApphelpCacheCompareEntries)
  27. #pragma alloc_text(PAGE, ApphelpAVLTableAllocate)
  28. #pragma alloc_text(PAGE, ApphelpAVLTableFree)
  29. #pragma alloc_text(PAGE, _ApphelpCacheFreeEntry)
  30. #pragma alloc_text(PAGE, _ApphelpCacheDeleteEntry)
  31. #pragma alloc_text(PAGE, ApphelpCacheRemoveEntry)
  32. #pragma alloc_text(PAGE, ApphelpCacheInsertEntry)
  33. #pragma alloc_text(PAGE, ApphelpCacheLookupEntry)
  34. #pragma alloc_text(PAGE, ApphelpCacheParseBuffer)
  35. #pragma alloc_text(PAGE, ApphelpCacheCreateBuffer)
  36. #pragma alloc_text(PAGE, ApphelpCacheWrite)
  37. #pragma alloc_text(PAGE, ApphelpCacheRead)
  38. #pragma alloc_text(PAGE, ApphelpCacheVerifyContext)
  39. #pragma alloc_text(PAGE, ApphelpCacheReleaseLock)
  40. #pragma alloc_text(PAGE, ApphelpCacheLockExclusive)
  41. #pragma alloc_text(PAGE, ApphelpCacheLockExclusiveNoWait)
  42. #pragma alloc_text(PAGE, ApphelpCacheFlush)
  43. #pragma alloc_text(PAGE, ApphelpCacheControlValidateParameters)
  44. #pragma alloc_text(PAGE, ApphelpCacheShutdown)
  45. #pragma alloc_text(PAGE, ApphelpCacheDump)
  46. #pragma alloc_text(PAGE, NtApphelpCacheControl)
  47. #endif // ALLOC_PRAGMA
  48. #define APPCOMPAT_CACHE_KEY_NAME \
  49. L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCompatCache"
  50. #define APPCOMPAT_CACHE_VALUE_NAME \
  51. L"AppCompatCache"
  52. static UNICODE_STRING AppcompatKeyPathLayers =
  53. RTL_CONSTANT_STRING(L"\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers");
  54. static UNICODE_STRING AppcompatKeyPathCustom =
  55. RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Custom\\");
  56. //
  57. // The default cache timeout. This timeout affects the maximum delay
  58. // that we can incur due to congestion for the shared mutex.
  59. //
  60. #define SHIM_CACHE_TIMEOUT 100
  61. //
  62. // Cache entries as they are stored in the registry
  63. //
  64. typedef struct tagSTOREDCACHEENTRY {
  65. UNICODE_STRING FileName; // length, maximum length and buffer that is an offset
  66. LONGLONG FileTime;
  67. LONGLONG FileSize;
  68. } STOREDCACHEENTRY, *PSTOREDCACHEENTRY;
  69. typedef struct tagSTOREDCACHEHEADER {
  70. DWORD dwMagic; // cache identifier
  71. DWORD dwCount; // entry count
  72. } STOREDCACHEHEADER, *PSTOREDCACHEHEADER;
  73. //
  74. // Global cache data
  75. //
  76. typedef struct tagSHIMCACHEHEADER {
  77. RTL_AVL_TABLE Table; // cache
  78. LIST_ENTRY ListHead; // cache nodes, lru list
  79. } SHIMCACHEHEADER, *PSHIMCACHEHEADER;
  80. SHIMCACHEHEADER g_ShimCache; // global header
  81. ERESOURCE g_SharedLock;
  82. BOOL g_bCacheEnabled = FALSE;
  83. //
  84. // Magic DWORD that allows us to validate the cache quickly
  85. // we do not however limit validation to this dword check
  86. // and we are prepared for data being completely invalid
  87. //
  88. #define SHIM_CACHE_MAGIC_NEW 0xBADC0FFE
  89. //
  90. // Maximum number of cache entries
  91. //
  92. #define MAX_SHIM_CACHE_ENTRIES 0x200
  93. ///////////////////////////////////////////////////////////////////////////////////////
  94. //
  95. // Utility functions
  96. //
  97. //
  98. ///////////////////////////////////////////////////////////////////////////////////////
  99. NTSTATUS
  100. ApphelpDuplicateUnicodeString(
  101. PUNICODE_STRING pStrDest,
  102. PUNICODE_STRING pStrSrc
  103. )
  104. {
  105. USHORT Length = pStrSrc->Length;
  106. if (Length == 0) {
  107. pStrDest->Length =
  108. pStrDest->MaximumLength = 0;
  109. pStrDest->Buffer = NULL;
  110. return STATUS_SUCCESS;
  111. }
  112. pStrDest->MaximumLength = Length + sizeof(UNICODE_NULL);
  113. pStrDest->Buffer = (PWCHAR)SdbAlloc(pStrDest->MaximumLength);
  114. if (pStrDest->Buffer == NULL) {
  115. return STATUS_NO_MEMORY;
  116. }
  117. RtlCopyMemory(pStrDest->Buffer, pStrSrc->Buffer, Length);
  118. *(pStrDest->Buffer + Length/sizeof(WCHAR)) = L'\0';
  119. pStrDest->Length = Length;
  120. return STATUS_SUCCESS;
  121. }
  122. VOID
  123. ApphelpFreeUnicodeString(
  124. PUNICODE_STRING pStr
  125. )
  126. {
  127. if (pStr != NULL) {
  128. if (pStr->Buffer != NULL) {
  129. // #if DBG
  130. RtlFillMemory(pStr->Buffer, pStr->MaximumLength, 'B');
  131. // #endif
  132. SdbFree(pStr->Buffer);
  133. }
  134. RtlZeroMemory(pStr, sizeof(*pStr));
  135. }
  136. }
  137. NTSTATUS
  138. ApphelpCacheQueryFileInformation(
  139. HANDLE FileHandle,
  140. PLONGLONG pFileSize,
  141. PLONGLONG pFileTime
  142. )
  143. /*++
  144. Return: Status indicating success or failure
  145. Desc: Queries for file size and timestamp.
  146. --*/
  147. {
  148. NTSTATUS Status;
  149. IO_STATUS_BLOCK IoStatusBlock;
  150. FILE_BASIC_INFORMATION BasicFileInfo;
  151. FILE_STANDARD_INFORMATION StdFileInfo;
  152. LONGLONG FileTime;
  153. Status = NtQueryInformationFile(FileHandle,
  154. &IoStatusBlock,
  155. &BasicFileInfo,
  156. sizeof(BasicFileInfo),
  157. FileBasicInformation);
  158. if (!NT_SUCCESS(Status)) {
  159. DBGPRINT((sdlError,
  160. "ShimQueryFileInformation",
  161. "NtQueryInformationFile/BasicInfo failed 0x%x\n",
  162. Status));
  163. goto Cleanup;
  164. }
  165. FileTime = BasicFileInfo.LastWriteTime.QuadPart;
  166. Status = NtQueryInformationFile(FileHandle,
  167. &IoStatusBlock,
  168. &StdFileInfo,
  169. sizeof(StdFileInfo),
  170. FileStandardInformation);
  171. if (!NT_SUCCESS(Status)) {
  172. DBGPRINT((sdlError,
  173. "ShimQueryFileInformation",
  174. "NtQueryInformationFile/StdInfo failed 0x%x\n",
  175. Status));
  176. goto Cleanup;
  177. }
  178. *pFileSize = StdFileInfo.EndOfFile.QuadPart;
  179. *pFileTime = FileTime;
  180. Cleanup:
  181. return Status;
  182. }
  183. ///////////////////////////////////////////////////////////////////////////////////
  184. //
  185. // Table handling routines
  186. //
  187. ///////////////////////////////////////////////////////////////////////////////////
  188. RTL_GENERIC_COMPARE_RESULTS
  189. NTAPI
  190. ApphelpCacheCompareEntries(
  191. IN PRTL_AVL_TABLE pTable,
  192. IN PVOID pFirstStruct,
  193. IN PVOID pSecondStruct
  194. )
  195. {
  196. PSHIMCACHEENTRY pFirstEntry = (PSHIMCACHEENTRY)pFirstStruct;
  197. PSHIMCACHEENTRY pSecondEntry = (PSHIMCACHEENTRY)pSecondStruct;
  198. LONG lResult;
  199. UNREFERENCED_PARAMETER(pTable);
  200. lResult = RtlCompareUnicodeString(&pFirstEntry->FileName,
  201. &pSecondEntry->FileName,
  202. TRUE);
  203. if (lResult < 0) {
  204. return GenericLessThan;
  205. }
  206. if (lResult > 0) {
  207. return GenericGreaterThan;
  208. }
  209. // match
  210. return GenericEqual;
  211. }
  212. PVOID
  213. NTAPI
  214. ApphelpAVLTableAllocate(
  215. struct _RTL_AVL_TABLE *Table,
  216. CLONG ByteSize
  217. )
  218. {
  219. UNREFERENCED_PARAMETER(Table);
  220. return SdbAlloc(ByteSize);
  221. }
  222. VOID
  223. NTAPI
  224. ApphelpAVLTableFree(
  225. struct _RTL_AVL_TABLE *Table,
  226. PVOID pBuffer
  227. )
  228. {
  229. UNREFERENCED_PARAMETER(Table);
  230. if (pBuffer != NULL) {
  231. SdbFree(pBuffer);
  232. }
  233. }
  234. //////////////////////////////////////////////////////////////////////////////////////
  235. //
  236. // Delete cache entry - by filename or using a pointer
  237. //
  238. //////////////////////////////////////////////////////////////////////////////////////
  239. NTSTATUS
  240. _ApphelpCacheFreeEntry(
  241. IN PSHIMCACHEENTRY pEntry
  242. )
  243. {
  244. PWCHAR pBuffer;
  245. BOOL bDeleted;
  246. NTSTATUS Status = STATUS_NOT_FOUND;
  247. ULONG BufferLength;
  248. ASSERT(ExIsResourceAcquiredExclusiveLite(&g_SharedLock) == TRUE);
  249. RemoveEntryList(&pEntry->ListEntry);
  250. pBuffer = pEntry->FileName.Buffer;
  251. BufferLength = pEntry->FileName.MaximumLength;
  252. bDeleted = RtlDeleteElementGenericTableAvl(&g_ShimCache.Table,
  253. pEntry);
  254. if (bDeleted) {
  255. // #if DBG
  256. RtlFillMemory(pBuffer, BufferLength, 'C');
  257. // #endif
  258. SdbFree(pBuffer);
  259. Status = STATUS_SUCCESS;
  260. }
  261. return Status;
  262. }
  263. //
  264. // Delete Cache entry, no lock
  265. //
  266. //
  267. NTSTATUS
  268. _ApphelpCacheDeleteEntry(
  269. IN PUNICODE_STRING pFileName
  270. )
  271. {
  272. SHIMCACHEENTRY ShimCacheEntry;
  273. PSHIMCACHEENTRY pEntryFound;
  274. NTSTATUS Status;
  275. ASSERT(ExIsResourceAcquiredExclusiveLite(&g_SharedLock) == TRUE);
  276. ShimCacheEntry.FileName = *pFileName;
  277. //
  278. // First find the element and unlink it.
  279. //
  280. pEntryFound = RtlLookupElementGenericTableAvl(&g_ShimCache.Table,
  281. &ShimCacheEntry);
  282. if (pEntryFound) {
  283. Status = _ApphelpCacheFreeEntry(pEntryFound);
  284. } else {
  285. Status = STATUS_NOT_FOUND;
  286. }
  287. return Status;
  288. }
  289. //
  290. // Delete cache entry under the lock
  291. //
  292. NTSTATUS
  293. ApphelpCacheRemoveEntry(
  294. IN PUNICODE_STRING FileName
  295. )
  296. {
  297. NTSTATUS Status;
  298. Status = ApphelpCacheLockExclusive();
  299. if (!NT_SUCCESS(Status)) {
  300. return Status;
  301. }
  302. Status = _ApphelpCacheDeleteEntry(FileName);
  303. ApphelpCacheReleaseLock();
  304. return Status;
  305. }
  306. //////////////////////////////////////////////////////////////////////////////////////
  307. //
  308. // Update the cache by inserting a new entry
  309. //
  310. //////////////////////////////////////////////////////////////////////////////////////
  311. //
  312. // Uses Lock
  313. //
  314. NTSTATUS
  315. ApphelpCacheInsertEntry(
  316. IN PUNICODE_STRING pFileName,
  317. IN HANDLE FileHandle
  318. )
  319. {
  320. PSHIMCACHEENTRY pEntryFound;
  321. SHIMCACHEENTRY ShimCacheEntry = { 0 };
  322. PVOID pNodeOrParent;
  323. TABLE_SEARCH_RESULT SearchResult;
  324. NTSTATUS Status;
  325. ULONG nElements;
  326. LONGLONG FileTime = 0;
  327. LONGLONG FileSize = 0;
  328. Status = ApphelpCacheLockExclusive();
  329. if (!NT_SUCCESS(Status)) {
  330. return Status;
  331. }
  332. if (FileHandle != INVALID_HANDLE_VALUE) {
  333. Status = ApphelpCacheQueryFileInformation(FileHandle, &FileSize, &FileTime);
  334. if (!NT_SUCCESS(Status)) {
  335. DBGPRINT((sdlError,
  336. "ApphelpCacheInsertEntry",
  337. "Failed to query file information for \"%ls\" status 0x%lx\n",
  338. pFileName->Buffer,
  339. Status));
  340. goto Cleanup;
  341. }
  342. }
  343. ShimCacheEntry.FileName = *pFileName; // note that we take it as-is for lookup
  344. pEntryFound = (PSHIMCACHEENTRY)RtlLookupElementGenericTableFullAvl(&g_ShimCache.Table,
  345. &ShimCacheEntry,
  346. &pNodeOrParent,
  347. &SearchResult);
  348. if (SearchResult == TableFoundNode) {
  349. //
  350. // pEntryFound is valid and points to our node
  351. //
  352. //
  353. // update the data
  354. //
  355. pEntryFound->FileTime = FileTime;
  356. pEntryFound->FileSize = FileSize;
  357. //
  358. // update the lru index - exclude the element from it's place and insert
  359. // at the front of the list
  360. //
  361. RemoveEntryList(&pEntryFound->ListEntry);
  362. InsertHeadList(&g_ShimCache.ListHead, &pEntryFound->ListEntry);
  363. Status = STATUS_SUCCESS;
  364. goto Cleanup;
  365. }
  366. //
  367. // allocate the entry
  368. //
  369. Status = ApphelpDuplicateUnicodeString(&ShimCacheEntry.FileName, pFileName);
  370. if (!NT_SUCCESS(Status)) {
  371. goto Cleanup;
  372. }
  373. ShimCacheEntry.FileTime = FileTime;
  374. ShimCacheEntry.FileSize = FileSize;
  375. pEntryFound = RtlInsertElementGenericTableFullAvl(&g_ShimCache.Table,
  376. &ShimCacheEntry,
  377. sizeof(ShimCacheEntry),
  378. NULL,
  379. pNodeOrParent,
  380. SearchResult);
  381. InsertHeadList(&g_ShimCache.ListHead, &pEntryFound->ListEntry);
  382. nElements = RtlNumberGenericTableElementsAvl(&g_ShimCache.Table);
  383. if (nElements > MAX_SHIM_CACHE_ENTRIES) {
  384. //
  385. // remove an element -- check for list being empty, just in case
  386. //
  387. pEntryFound = (PSHIMCACHEENTRY)RemoveTailList(&g_ShimCache.ListHead);
  388. //
  389. // remove this entry
  390. //
  391. Status = _ApphelpCacheFreeEntry(pEntryFound);
  392. if (!NT_SUCCESS(Status)) {
  393. DBGPRINT((sdlError,
  394. "ApphelpCacheInsertEntry",
  395. "Failed to remove cache entry\n"));
  396. goto Cleanup;
  397. }
  398. }
  399. Status = STATUS_SUCCESS;
  400. Cleanup:
  401. ApphelpCacheReleaseLock();
  402. return Status;
  403. }
  404. //
  405. // returns STATUS_SUCCESS if the entry was found in the cache
  406. // Procedure is using locking mechanism
  407. //
  408. //
  409. NTSTATUS
  410. ApphelpCacheLookupEntry(
  411. IN PUNICODE_STRING pFileName,
  412. IN HANDLE FileHandle
  413. )
  414. {
  415. PSHIMCACHEENTRY pEntryFound;
  416. SHIMCACHEENTRY ShimCacheEntry;
  417. NTSTATUS Status = STATUS_NOT_FOUND;
  418. LONGLONG FileTime;
  419. LONGLONG FileSize;
  420. //
  421. // lock for exclusive access yet we do not wait
  422. //
  423. Status = ApphelpCacheLockExclusiveNoWait();
  424. if (!NT_SUCCESS(Status)) {
  425. DBGPRINT((sdlInfo,
  426. "ApphelpCacheLookupEntry",
  427. "Failed to lock cache\n"));
  428. return STATUS_NOT_FOUND;
  429. }
  430. ShimCacheEntry.FileName = *pFileName;
  431. pEntryFound = RtlLookupElementGenericTableAvl(&g_ShimCache.Table,
  432. &ShimCacheEntry);
  433. if (pEntryFound == NULL) {
  434. Status = STATUS_NOT_FOUND;
  435. goto Cleanup;
  436. }
  437. if (FileHandle != INVALID_HANDLE_VALUE) {
  438. Status = ApphelpCacheQueryFileInformation(FileHandle, &FileSize, &FileTime);
  439. if (!NT_SUCCESS(Status) ||
  440. pEntryFound->FileTime != FileTime ||
  441. pEntryFound->FileSize != FileSize) {
  442. //
  443. // most likely the file is gone
  444. //
  445. Status = _ApphelpCacheDeleteEntry(pFileName);
  446. if (!NT_SUCCESS(Status)) {
  447. DBGPRINT((sdlWarning,
  448. "ApphelpCacheLookupEntry",
  449. "Entry \"%ls\" was found then disappeared 0x%lx\n",
  450. pFileName->Buffer,
  451. Status));
  452. }
  453. Status = STATUS_NOT_FOUND;
  454. goto Cleanup;
  455. }
  456. }
  457. //
  458. // if we have not removed the entry -- move it to the head of the lru
  459. //
  460. RemoveEntryList(&pEntryFound->ListEntry);
  461. InsertHeadList(&g_ShimCache.ListHead, &pEntryFound->ListEntry);
  462. Status = STATUS_SUCCESS;
  463. Cleanup:
  464. ApphelpCacheReleaseLock();
  465. return Status;
  466. }
  467. //
  468. // Verify apphelp cache caller's context
  469. //
  470. //
  471. NTSTATUS
  472. ApphelpCacheVerifyContext(
  473. VOID
  474. )
  475. {
  476. //
  477. // verify that the cache is good to operate on
  478. //
  479. KPROCESSOR_MODE PreviousMode;
  480. NTSTATUS Status = STATUS_SUCCESS;
  481. PreviousMode = ExGetPreviousMode();
  482. if (PreviousMode != KernelMode) {
  483. //
  484. // Does the caller have "trusted computer base" privilge?
  485. //
  486. if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) {
  487. DBGPRINT((sdlError, "ApphelpCacheVerifyContext", "Security check failed\n"));
  488. Status = STATUS_ACCESS_DENIED;
  489. }
  490. }
  491. return Status;
  492. }
  493. //////////////////////////////////////////////////////////////////////////////////////
  494. //
  495. // Cache persistance routines
  496. //
  497. //////////////////////////////////////////////////////////////////////////////////////
  498. NTSTATUS
  499. ApphelpCacheParseBuffer(
  500. PVOID pBuffer,
  501. ULONG lBufferSize
  502. )
  503. {
  504. PSTOREDCACHEHEADER pStoredHeader;
  505. PSTOREDCACHEENTRY pStoredEntry;
  506. SHIMCACHEENTRY ShimCacheEntry;
  507. ULONG nElement;
  508. NTSTATUS Status;
  509. PSHIMCACHEENTRY pCacheEntry;
  510. PVOID pBufferEnd;
  511. STOREDCACHEENTRY StoredEntry;
  512. STOREDCACHEHEADER StoredHeader;
  513. if (lBufferSize < sizeof(STOREDCACHEHEADER)) {
  514. return STATUS_INVALID_PARAMETER;
  515. }
  516. pBufferEnd = (PVOID)((PBYTE)pBuffer + lBufferSize);
  517. RtlCopyMemory(&StoredHeader, pBuffer, sizeof(StoredHeader));
  518. if (StoredHeader.dwMagic != SHIM_CACHE_MAGIC_NEW) {
  519. return STATUS_INVALID_PARAMETER;
  520. }
  521. pStoredHeader = (PSTOREDCACHEHEADER)pBuffer;
  522. pStoredEntry = (PSTOREDCACHEENTRY)(pStoredHeader + 1);
  523. for (nElement = 0; nElement < StoredHeader.dwCount; ++nElement, ++pStoredEntry) {
  524. if ((ULONG_PTR)(pStoredEntry + 1) > (ULONG_PTR)pBufferEnd) {
  525. //
  526. // invalid data
  527. //
  528. break;
  529. }
  530. //
  531. // once we determined that the entry has a valid size, copy it
  532. //
  533. RtlCopyMemory(&StoredEntry, pStoredEntry, sizeof(StoredEntry));
  534. if ((ULONG_PTR)StoredEntry.FileName.Buffer >= (ULONG_PTR)lBufferSize) {
  535. //
  536. // also invalid entry -- offset is greater than the size of the buffer
  537. //
  538. break;
  539. }
  540. //
  541. // fixup the buffer please
  542. //
  543. StoredEntry.FileName.Buffer = (PWCHAR)((ULONG_PTR)pBuffer +
  544. (ULONG_PTR)StoredEntry.FileName.Buffer);
  545. if (StoredEntry.FileName.Length > StoredEntry.FileName.MaximumLength) {
  546. break;
  547. }
  548. if ((ULONG_PTR)(StoredEntry.FileName.Buffer +
  549. StoredEntry.FileName.MaximumLength/sizeof(WCHAR)) > (ULONG_PTR)pBufferEnd) {
  550. //
  551. // invalid data
  552. //
  553. break;
  554. }
  555. Status = ApphelpDuplicateUnicodeString(&ShimCacheEntry.FileName,
  556. &StoredEntry.FileName);
  557. if (!NT_SUCCESS(Status)) {
  558. return Status;
  559. }
  560. ShimCacheEntry.FileTime = StoredEntry.FileTime;
  561. ShimCacheEntry.FileSize = StoredEntry.FileSize;
  562. //
  563. // insert the entry
  564. //
  565. pCacheEntry = (PSHIMCACHEENTRY)RtlInsertElementGenericTableAvl(&g_ShimCache.Table,
  566. &ShimCacheEntry,
  567. sizeof(ShimCacheEntry),
  568. NULL);
  569. if (pCacheEntry == NULL) {
  570. //
  571. // the entry has not been inserted
  572. // clean it up now
  573. //
  574. ApphelpFreeUnicodeString(&ShimCacheEntry.FileName);
  575. return STATUS_NO_MEMORY;
  576. }
  577. InsertTailList(&g_ShimCache.ListHead, &pCacheEntry->ListEntry);
  578. }
  579. return STATUS_SUCCESS;
  580. }
  581. NTSTATUS
  582. ApphelpCacheCreateBuffer(
  583. PVOID* ppBuffer,
  584. PULONG pBufferSize
  585. )
  586. {
  587. ULONG nElements = 0;
  588. ULONG lBufferSize; // size of the buffer we need
  589. PLIST_ENTRY pListEntry;
  590. PSHIMCACHEENTRY pEntry;
  591. PVOID pBuffer;
  592. PSTOREDCACHEENTRY pStoredEntry;
  593. PSTOREDCACHEHEADER pStoredHeader;
  594. PWCHAR pStringBuffer;
  595. //
  596. // Calculate total size
  597. //
  598. lBufferSize = sizeof(STOREDCACHEHEADER);
  599. pListEntry = g_ShimCache.ListHead.Flink;
  600. while (pListEntry != &g_ShimCache.ListHead) {
  601. pEntry = (PSHIMCACHEENTRY)pListEntry;
  602. lBufferSize += sizeof(STOREDCACHEENTRY) +
  603. pEntry->FileName.MaximumLength;
  604. nElements++;
  605. pListEntry = pListEntry->Flink;
  606. }
  607. lBufferSize = ROUND_UP_COUNT(lBufferSize, sizeof(ULONGLONG));
  608. //
  609. // once we have all the entries, allocate the cache
  610. //
  611. pBuffer = SdbAlloc(lBufferSize);
  612. if (pBuffer == NULL) {
  613. return STATUS_NO_MEMORY;
  614. }
  615. pStoredHeader = (PSTOREDCACHEHEADER)pBuffer;
  616. pStoredHeader->dwMagic = SHIM_CACHE_MAGIC_NEW;
  617. pStoredHeader->dwCount = nElements;
  618. pStoredEntry = (PSTOREDCACHEENTRY)(pStoredHeader + 1);
  619. pStringBuffer = (PWCHAR)((PBYTE)pBuffer + lBufferSize);
  620. //
  621. // flatten the data
  622. //
  623. pListEntry = g_ShimCache.ListHead.Flink;
  624. while (pListEntry != &g_ShimCache.ListHead) {
  625. pEntry = (PSHIMCACHEENTRY)pListEntry;
  626. //
  627. // store the entry first
  628. //
  629. pStoredEntry->FileTime = pEntry->FileTime;
  630. pStoredEntry->FileSize = pEntry->FileSize;
  631. //
  632. // Filename is more interesting
  633. //
  634. pStoredEntry->FileName.Length = pEntry->FileName.Length;
  635. pStoredEntry->FileName.MaximumLength = pEntry->FileName.MaximumLength;
  636. //
  637. // now the buffer
  638. //
  639. pStringBuffer = (PWCHAR)((PBYTE)pStringBuffer - pEntry->FileName.MaximumLength);
  640. RtlCopyMemory(pStringBuffer, pEntry->FileName.Buffer, pEntry->FileName.MaximumLength);
  641. //
  642. // fixup the pointer
  643. //
  644. pStoredEntry->FileName.Buffer = (PWCHAR)((ULONG_PTR)pStringBuffer - (ULONG_PTR)pBuffer);
  645. pListEntry = pListEntry->Flink;
  646. pStoredEntry++;
  647. }
  648. //
  649. // done with the buffer
  650. //
  651. *ppBuffer = pBuffer;
  652. *pBufferSize = lBufferSize;
  653. return STATUS_SUCCESS;
  654. }
  655. NTSTATUS
  656. ApphelpCacheWrite(
  657. VOID
  658. )
  659. {
  660. static UNICODE_STRING ustrAppcompatCacheKeyName =
  661. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_KEY_NAME);
  662. static OBJECT_ATTRIBUTES objaAppcompatCacheKeyName =
  663. RTL_CONSTANT_OBJECT_ATTRIBUTES(&ustrAppcompatCacheKeyName, OBJ_CASE_INSENSITIVE);
  664. static UNICODE_STRING ustrAppcompatCacheValueName =
  665. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_VALUE_NAME);
  666. HANDLE hKey = NULL;
  667. ULONG BufferSize = 0;
  668. NTSTATUS Status;
  669. ULONG CreateDisposition;
  670. PVOID pBuffer = NULL;
  671. Status = ApphelpCacheCreateBuffer(&pBuffer, &BufferSize);
  672. if (!NT_SUCCESS(Status)) {
  673. return Status;
  674. }
  675. Status = NtCreateKey(&hKey,
  676. STANDARD_RIGHTS_WRITE |
  677. KEY_QUERY_VALUE |
  678. KEY_ENUMERATE_SUB_KEYS |
  679. KEY_SET_VALUE |
  680. KEY_CREATE_SUB_KEY,
  681. &objaAppcompatCacheKeyName,
  682. 0,
  683. NULL,
  684. REG_OPTION_NON_VOLATILE,
  685. &CreateDisposition);
  686. if (!NT_SUCCESS(Status)) {
  687. DBGPRINT((sdlError, "ApphelpCacheWrite", "Failed to create key 0x%lx\n", Status));
  688. goto Cleanup;
  689. }
  690. Status = NtSetValueKey(hKey,
  691. &ustrAppcompatCacheValueName,
  692. 0,
  693. REG_BINARY,
  694. pBuffer,
  695. BufferSize);
  696. if (!NT_SUCCESS(Status)) {
  697. DBGPRINT((sdlError, "ApphelpCacheWrite", "Failed to create key 0x%lx\n", Status));
  698. }
  699. NtClose(hKey);
  700. Cleanup:
  701. if (pBuffer != NULL) {
  702. SdbFree(pBuffer);
  703. }
  704. return Status;
  705. }
  706. NTSTATUS
  707. ApphelpCacheRead(
  708. VOID
  709. )
  710. {
  711. //
  712. static UNICODE_STRING ustrAppcompatCacheKeyName =
  713. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_KEY_NAME);
  714. static OBJECT_ATTRIBUTES objaAppcompatCacheKeyName =
  715. RTL_CONSTANT_OBJECT_ATTRIBUTES(&ustrAppcompatCacheKeyName, OBJ_CASE_INSENSITIVE);
  716. static UNICODE_STRING ustrAppcompatCacheValueName =
  717. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_VALUE_NAME);
  718. HANDLE KeyHandle = NULL;
  719. KEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
  720. PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInfo = &KeyValuePartialInfo;
  721. ULONG KeyValueLength = 0;
  722. ULONG BufferSize = sizeof(KeyValuePartialInformation);
  723. NTSTATUS Status;
  724. Status = NtOpenKey(&KeyHandle,
  725. KEY_QUERY_VALUE,
  726. (POBJECT_ATTRIBUTES)&objaAppcompatCacheKeyName);
  727. if (!NT_SUCCESS(Status)) {
  728. return FALSE;
  729. }
  730. Status = NtQueryValueKey(KeyHandle,
  731. &ustrAppcompatCacheValueName,
  732. KeyValuePartialInformation,
  733. pKeyValueInfo,
  734. BufferSize,
  735. &KeyValueLength);
  736. if (!NT_SUCCESS(Status)) {
  737. if (Status != STATUS_BUFFER_TOO_SMALL) {
  738. DBGPRINT((sdlError,
  739. "ApphelpCacheRead",
  740. "NtQueryValueKey fails for \"%s\", status 0x%lx\n",
  741. ustrAppcompatCacheValueName.Buffer,
  742. Status));
  743. goto Cleanup;
  744. }
  745. BufferSize = KeyValueLength;
  746. pKeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)SdbAlloc(BufferSize);
  747. if (pKeyValueInfo == NULL) {
  748. DBGPRINT((sdlError,
  749. "ApphelpCacheRead",
  750. "Failed to allocate cache buffer 0x%lx bytes\n",
  751. BufferSize));
  752. Status = STATUS_NO_MEMORY;
  753. goto Cleanup;
  754. }
  755. Status = NtQueryValueKey(KeyHandle,
  756. &ustrAppcompatCacheValueName,
  757. KeyValuePartialInformation,
  758. pKeyValueInfo,
  759. BufferSize,
  760. &KeyValueLength);
  761. if (!NT_SUCCESS(Status)) {
  762. DBGPRINT((sdlError,
  763. "ApphelpCacheRead",
  764. "NtQueryValueKey fails(2) for \"%s\", status 0x%lx\n",
  765. ustrAppcompatCacheValueName.Buffer,
  766. Status));
  767. goto Cleanup;
  768. }
  769. }
  770. if (pKeyValueInfo->Type != REG_BINARY) {
  771. DBGPRINT((sdlError,
  772. "ApphelpCacheRead",
  773. "Bad value type for apphelp cache 0x%lx\n",
  774. pKeyValueInfo->Type));
  775. Status = STATUS_OBJECT_TYPE_MISMATCH;
  776. goto Cleanup;
  777. }
  778. //
  779. // Now go through the cache (flat part) and parse all the entries into the table.
  780. // Make sure that we are able to parse old entries (after system upgrade).
  781. //
  782. Status = ApphelpCacheParseBuffer((PVOID)pKeyValueInfo->Data,
  783. pKeyValueInfo->DataLength);
  784. Cleanup:
  785. if (pKeyValueInfo != &KeyValuePartialInfo) {
  786. SdbFree(pKeyValueInfo);
  787. }
  788. if (KeyHandle != NULL) {
  789. NtClose(KeyHandle);
  790. }
  791. return Status;
  792. }
  793. //////////////////////////////////////////////////////////////////////////////////////
  794. //
  795. // Cache Initialization routine
  796. //
  797. //////////////////////////////////////////////////////////////////////////////////////
  798. NTSTATUS
  799. ApphelpCacheInitialize(
  800. IN PLOADER_PARAMETER_BLOCK pLoaderBlock,
  801. IN ULONG BootPhase
  802. )
  803. {
  804. NTSTATUS Status;
  805. //
  806. // Check for safe mode.
  807. //
  808. if (pLoaderBlock->LoadOptions != NULL &&
  809. strstr(pLoaderBlock->LoadOptions, SAFEBOOT_LOAD_OPTION_A) != NULL) {
  810. g_bCacheEnabled = FALSE;
  811. return STATUS_SUCCESS;
  812. }
  813. //
  814. // Create the cache lock.
  815. //
  816. Status = ExInitializeResourceLite(&g_SharedLock);
  817. if (!NT_SUCCESS(Status)) {
  818. DBGPRINT((sdlError,
  819. "ApphelpCacheInitialize",
  820. "Failed to initialize the cache lock\n",
  821. Status));
  822. g_bCacheEnabled = FALSE;
  823. return Status;
  824. }
  825. RtlInitializeGenericTableAvl(&g_ShimCache.Table,
  826. ApphelpCacheCompareEntries,
  827. ApphelpAVLTableAllocate,
  828. ApphelpAVLTableFree,
  829. NULL);
  830. InitializeListHead(&g_ShimCache.ListHead);
  831. //
  832. // Once we initialize - load the cache from the registry
  833. //
  834. Status = ApphelpCacheRead();
  835. if (!NT_SUCCESS(Status)) {
  836. DBGPRINT((sdlError,
  837. "ApphelpCacheInitialize",
  838. "Failed to retrieve apphelp cache 0x%lx\n",
  839. Status));
  840. }
  841. g_bCacheEnabled = TRUE;
  842. return STATUS_SUCCESS;
  843. UNREFERENCED_PARAMETER(pLoaderBlock);
  844. UNREFERENCED_PARAMETER(BootPhase);
  845. }
  846. NTSTATUS
  847. ApphelpCacheShutdown(
  848. IN ULONG ShutdownPhase
  849. )
  850. {
  851. NTSTATUS Status = STATUS_SUCCESS;
  852. if (g_bCacheEnabled) {
  853. Status = ApphelpCacheWrite();
  854. }
  855. return Status;
  856. UNREFERENCED_PARAMETER(ShutdownPhase);
  857. }
  858. //////////////////////////////////////////////////////////////////////////////////////
  859. //
  860. // Locking
  861. //
  862. //////////////////////////////////////////////////////////////////////////////////////
  863. NTSTATUS
  864. ApphelpCacheLockExclusiveNoWait(
  865. VOID
  866. )
  867. {
  868. BOOLEAN bLock;
  869. NTSTATUS Status = STATUS_SUCCESS;
  870. KeEnterCriticalRegion();
  871. bLock = ExTryToAcquireResourceExclusiveLite(&g_SharedLock);
  872. if (!bLock) {
  873. KeLeaveCriticalRegion();
  874. DBGPRINT((sdlWarning,
  875. "ApphelpCacheLockExclusiveNoWait",
  876. "Failed to lock apphelp cache\n"));
  877. Status = STATUS_ACCESS_DENIED;
  878. }
  879. return Status;
  880. }
  881. NTSTATUS
  882. ApphelpCacheLockExclusive(
  883. VOID
  884. )
  885. {
  886. BOOLEAN bLock;
  887. KeEnterCriticalRegion();
  888. bLock = ExAcquireResourceExclusiveLite(&g_SharedLock, TRUE);
  889. ASSERT(bLock);
  890. return STATUS_SUCCESS;
  891. }
  892. NTSTATUS
  893. ApphelpCacheReleaseLock(
  894. VOID
  895. )
  896. {
  897. ExReleaseResourceLite(&g_SharedLock);
  898. KeLeaveCriticalRegion();
  899. return STATUS_SUCCESS;
  900. }
  901. //////////////////////////////////////////////////////////////////////////////////////
  902. //
  903. // Routine to flush the cache
  904. //
  905. //////////////////////////////////////////////////////////////////////////////////////
  906. NTSTATUS
  907. ApphelpCacheFlush(
  908. VOID
  909. )
  910. {
  911. PWCHAR pBuffer;
  912. BOOL bDeleted;
  913. NTSTATUS Status;
  914. PSHIMCACHEENTRY pEntry;
  915. ULONG BufferLength;
  916. Status = ApphelpCacheLockExclusive();
  917. if (!NT_SUCCESS(Status)) {
  918. return Status;
  919. }
  920. //
  921. // Enumerate the entries and remove them from the cache
  922. //
  923. for (pEntry = (PSHIMCACHEENTRY)RtlEnumerateGenericTableAvl(&g_ShimCache.Table, TRUE);
  924. pEntry != NULL;
  925. pEntry = (PSHIMCACHEENTRY)RtlEnumerateGenericTableAvl(&g_ShimCache.Table, TRUE)) {
  926. pBuffer = pEntry->FileName.Buffer;
  927. BufferLength = pEntry->FileName.MaximumLength;
  928. RemoveEntryList(&pEntry->ListEntry);
  929. bDeleted = RtlDeleteElementGenericTableAvl(&g_ShimCache.Table,
  930. pEntry);
  931. if (bDeleted) {
  932. // #if DBG
  933. RtlFillMemory(pBuffer, BufferLength, 'A');
  934. // #endif
  935. SdbFree(pBuffer);
  936. } else {
  937. DBGPRINT((sdlError,
  938. "ApphelpCacheFlush",
  939. "Failed to remove the entry from cache %ls\n",
  940. pBuffer));
  941. }
  942. }
  943. ASSERT(RtlIsGenericTableEmptyAvl(&g_ShimCache.Table));
  944. ASSERT(IsListEmpty(&g_ShimCache.ListHead));
  945. ApphelpCacheReleaseLock();
  946. return STATUS_SUCCESS;
  947. }
  948. NTSTATUS
  949. ApphelpCacheDump(
  950. VOID
  951. )
  952. {
  953. PLIST_ENTRY pListEntry;
  954. int nElement = 0;
  955. PSHIMCACHEENTRY pEntry;
  956. NTSTATUS Status;
  957. Status = ApphelpCacheLockExclusive();
  958. if (!NT_SUCCESS(Status)) {
  959. return Status;
  960. }
  961. DBGPRINT((sdlInfo, "ApphelpCacheDump", "(LRU) (Name)\n"));
  962. pListEntry = g_ShimCache.ListHead.Flink;
  963. while (pListEntry != &g_ShimCache.ListHead) {
  964. pEntry = (PSHIMCACHEENTRY)pListEntry;
  965. ++nElement;
  966. DBGPRINT((sdlInfo,
  967. "ApphelpCacheDump",
  968. "%2d. %ls\n",
  969. nElement,
  970. pEntry->FileName.Buffer));
  971. pListEntry = pListEntry->Flink;
  972. }
  973. DBGPRINT((sdlInfo, "ApphelpCacheDump", "----\n"));
  974. ApphelpCacheReleaseLock();
  975. return STATUS_SUCCESS;
  976. }
  977. //////////////////////////////////////////////////////////////////////////////////////
  978. //
  979. // Control function calls
  980. //
  981. //////////////////////////////////////////////////////////////////////////////////////
  982. NTSTATUS
  983. ApphelpCacheControlValidateParameters(
  984. IN PAHCACHESERVICEDATA pServiceData,
  985. OUT PUNICODE_STRING pFileName,
  986. OUT HANDLE* pFileHandle
  987. )
  988. {
  989. NTSTATUS Status;
  990. UNICODE_STRING FileName;
  991. if (pServiceData == NULL) {
  992. return STATUS_INVALID_PARAMETER;
  993. }
  994. RtlZeroMemory(pFileName, sizeof(*pFileName));
  995. try {
  996. ProbeForRead(pServiceData, sizeof(AHCACHESERVICEDATA), sizeof(ULONG));
  997. *pFileHandle = pServiceData->FileHandle;
  998. FileName = ProbeAndReadUnicodeString(&pServiceData->FileName);
  999. ProbeForRead(FileName.Buffer, FileName.Length, sizeof(UCHAR));
  1000. Status = ApphelpDuplicateUnicodeString(pFileName, &FileName);
  1001. if (!NT_SUCCESS(Status)) {
  1002. return Status;
  1003. }
  1004. } except(EXCEPTION_EXECUTE_HANDLER) {
  1005. ApphelpFreeUnicodeString(pFileName);
  1006. return GetExceptionCode();
  1007. }
  1008. return STATUS_SUCCESS;
  1009. }
  1010. NTSYSCALLAPI
  1011. NTSTATUS
  1012. NtApphelpCacheControl(
  1013. IN APPHELPCACHESERVICECLASS Service,
  1014. IN OUT PVOID ServiceData
  1015. )
  1016. {
  1017. UNICODE_STRING FileName = { 0 };
  1018. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  1019. NTSTATUS Status;
  1020. PAHCACHESERVICEDATA pServiceData = (PAHCACHESERVICEDATA)ServiceData;
  1021. if (!g_bCacheEnabled) {
  1022. return STATUS_INVALID_PARAMETER;
  1023. }
  1024. //
  1025. // This is the parameter validation code
  1026. //
  1027. switch (Service) {
  1028. case ApphelpCacheServiceLookup:
  1029. //
  1030. // Lookup the entry specified in ServiceData.
  1031. // Potentially may remove an entry from the cache.
  1032. //
  1033. Status = ApphelpCacheControlValidateParameters(pServiceData, &FileName, &FileHandle);
  1034. if (!NT_SUCCESS(Status)) {
  1035. goto Done;
  1036. }
  1037. Status = ApphelpCacheLookupEntry(&FileName, FileHandle);
  1038. break;
  1039. case ApphelpCacheServiceUpdate:
  1040. Status = ApphelpCacheVerifyContext();
  1041. if (!NT_SUCCESS(Status)) {
  1042. goto Done;
  1043. }
  1044. Status = ApphelpCacheControlValidateParameters(pServiceData, &FileName, &FileHandle);
  1045. if (!NT_SUCCESS(Status)) {
  1046. goto Done;
  1047. }
  1048. Status = ApphelpCacheInsertEntry(&FileName, FileHandle);
  1049. break;
  1050. case ApphelpCacheServiceRemove:
  1051. Status = ApphelpCacheControlValidateParameters(pServiceData, &FileName, &FileHandle);
  1052. if (!NT_SUCCESS(Status)) {
  1053. goto Done;
  1054. }
  1055. Status = ApphelpCacheRemoveEntry(&FileName);
  1056. break;
  1057. case ApphelpCacheServiceFlush:
  1058. Status = ApphelpCacheFlush();
  1059. break;
  1060. case ApphelpCacheServiceDump:
  1061. #if DBG
  1062. Status = ApphelpCacheDump();
  1063. #else
  1064. Status = STATUS_SUCCESS;
  1065. #endif
  1066. break;
  1067. default:
  1068. Status = STATUS_INVALID_PARAMETER;
  1069. break;
  1070. }
  1071. Done:
  1072. ApphelpFreeUnicodeString(&FileName);
  1073. return Status;
  1074. }