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.

2043 lines
52 KiB

  1. /*
  2. Cache handling functions for use in kernel32.dll
  3. VadimB
  4. */
  5. #include "basedll.h"
  6. #pragma hdrstop
  7. #ifdef DbgPrint
  8. #undef DbgPrint
  9. #endif
  10. #define DbgPrint 0 && DbgPrint
  11. #define APPCOMPAT_CACHE_KEY_NAME \
  12. L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility"
  13. #define APPCOMPAT_CACHE_VALUE_NAME \
  14. L"AppCompatCache"
  15. static UNICODE_STRING AppcompatKeyPathLayers =
  16. RTL_CONSTANT_STRING(L"\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers");
  17. //
  18. // The number of cache entries we maintain
  19. //
  20. #define MAX_SHIM_CACHE_ENTRIES 96
  21. //
  22. // The default cache timeout. This timeout affects the maximum delay
  23. // that we can incur due to congestion for the shared mutex.
  24. //
  25. #define SHIM_CACHE_TIMEOUT 100
  26. //
  27. // Magic DWORD to recognize valid buffer
  28. //
  29. #define SHIM_CACHE_MAGIC 0xDEADBEEF
  30. //
  31. // Reasons for having to call into apphelp.dll
  32. // these flags are also defined in apphelp.h (windows\appcompat\apphelp)
  33. //
  34. #ifndef SHIM_CACHE_NOT_FOUND
  35. #define SHIM_CACHE_NOT_FOUND 0x00000001
  36. #define SHIM_CACHE_BYPASS 0x00000002 // bypass cache (either removable media or temp dir)
  37. #define SHIM_CACHE_LAYER_ENV 0x00000004 // layer env variable set
  38. #define SHIM_CACHE_MEDIA 0x00000008
  39. #define SHIM_CACHE_TEMP 0x00000010
  40. #define SHIM_CACHE_NOTAVAIL 0x00000020
  41. #endif
  42. //
  43. // definitions of our internal data types
  44. //
  45. #pragma pack(8)
  46. typedef struct tagSHIMCACHEENTRY {
  47. WCHAR wszPath[MAX_PATH + 1];
  48. //
  49. // Creation time and size of the file. It is used to verify that
  50. // the file is still there and the same (as opposed to patched exe's, etc)
  51. //
  52. LONGLONG FileTime; // creation time
  53. LONGLONG FileSize; // size of the file
  54. //
  55. // timestamp - when did we touch this item last
  56. //
  57. LONGLONG TimeStamp;
  58. } SHIMCACHEENTRY;
  59. typedef SHIMCACHEENTRY *PSHIMCACHEENTRY;
  60. //
  61. // The content of the shared section for use with our caching mechanism.
  62. //
  63. typedef struct tagSHIMCACHEHEADER {
  64. DWORD dwMagic; // expected SHIM_CACHE_MAGIC
  65. DWORD dwMaxSize; // expected MAX_SHIM_CACHE_ENTRIES
  66. DWORD dwCount; // entry count
  67. DWORD dwUnused; // keep this just so we're aligned
  68. int rgIndex[0];
  69. } SHIMCACHEHEADER, *PSHIMCACHEHEADER;
  70. #pragma pack()
  71. //
  72. // Shared data, initialized in LockShimCache().
  73. // Cleanup is done at dll unload time (when the process terminates).
  74. //
  75. WCHAR gwszCacheMutex[] = L"ShimCacheMutex";
  76. WCHAR gwszCacheSharedMemName[] = L"ShimSharedMemory";
  77. //
  78. // The cache is global and we initialize it as such.
  79. // The cache code is never re-entered on the same thread.
  80. // A mutex provides all the synchronization we need.
  81. //
  82. HANDLE ghShimCacheMutex; // shared mutex handle
  83. HANDLE ghShimCacheShared; // shared memory handle
  84. PVOID gpShimCacheShared; // shared memory ptr
  85. //
  86. // global strings that we check to see if an exe is running in temp directory
  87. //
  88. UNICODE_STRING gustrWindowsTemp;
  89. UNICODE_STRING gustrSystemdriveTemp;
  90. //
  91. // get pointer to the entries from header
  92. //
  93. #define GET_SHIM_CACHE_ENTRIES(pHeader) \
  94. ((PSHIMCACHEENTRY)((PBYTE)(pHeader) + sizeof(SHIMCACHEHEADER) + ((pHeader)->dwMaxSize * sizeof(INT))))
  95. #define SHIM_CACHE_SIZE(nEntries) \
  96. (sizeof(SHIMCACHEHEADER) + (nEntries)*sizeof(int) + (nEntries)*sizeof(SHIMCACHEENTRY))
  97. //
  98. // Locally defined functions
  99. //
  100. BOOL
  101. BasepShimCacheInitTempDirs(
  102. VOID
  103. );
  104. BOOL
  105. BasepShimCacheInit(
  106. PSHIMCACHEHEADER pHeader,
  107. PSHIMCACHEENTRY pEntries,
  108. DWORD dwMaxEntries
  109. );
  110. BOOL
  111. BasepShimCacheRead(
  112. PVOID pCache,
  113. DWORD dwCacheSize // buffer global size
  114. );
  115. BOOL
  116. BasepShimCacheLock(
  117. PSHIMCACHEHEADER* ppHeader,
  118. PSHIMCACHEENTRY* ppEntries
  119. );
  120. BOOL
  121. BasepShimCacheWrite(
  122. PSHIMCACHEHEADER pHeader
  123. );
  124. BOOL
  125. BasepShimCacheUnlock(
  126. VOID
  127. );
  128. BOOL
  129. BasepShimCacheCheckIntegrity(
  130. PSHIMCACHEHEADER pHeader
  131. );
  132. BOOL
  133. BasepIsRemovableMedia(
  134. HANDLE FileHandle,
  135. BOOL bCacheNetwork
  136. );
  137. VOID
  138. WINAPI
  139. BaseDumpAppcompatCache(
  140. VOID
  141. );
  142. BOOL
  143. BasepCheckCacheExcludeList(
  144. LPCWSTR pwszPath
  145. );
  146. //
  147. // Init support for this user - to be called from WinLogon ONLY
  148. //
  149. BOOL
  150. WINAPI
  151. BaseInitAppcompatCacheSupport(
  152. VOID
  153. )
  154. {
  155. SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
  156. PSECURITY_DESCRIPTOR psd = NULL;
  157. SECURITY_ATTRIBUTES SecurityAttributes;
  158. BOOL bSuccess = FALSE;
  159. PSID Anyone = NULL;
  160. NTSTATUS Status;
  161. ULONG AclSize;
  162. ACL* pAcl;
  163. DWORD dwWaitResult;
  164. BOOL bShimCacheLocked = FALSE;
  165. BOOL bExistingCache = FALSE;
  166. Status = RtlAllocateAndInitializeSid(&WorldAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &Anyone);
  167. if (!NT_SUCCESS(Status)) {
  168. // dbgprint (failed with the sid
  169. return FALSE;
  170. }
  171. // calculate the size of the ACL (one ace)
  172. // 1 is one ACE, which includes a single ULONG from SID, since we add the size
  173. // of any Sids in -- we don't need to count the said ULONG twice
  174. AclSize = sizeof(ACL) + (1 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))) +
  175. RtlLengthSid(Anyone);
  176. psd = (PSECURITY_DESCRIPTOR)RtlAllocateHeap(RtlProcessHeap(),
  177. HEAP_ZERO_MEMORY,
  178. SECURITY_DESCRIPTOR_MIN_LENGTH + AclSize);
  179. if (psd == NULL) {
  180. Status = STATUS_NO_MEMORY;
  181. goto Exit;
  182. }
  183. pAcl = (ACL*)((BYTE*)psd + SECURITY_DESCRIPTOR_MIN_LENGTH);
  184. Status = RtlCreateAcl(pAcl, AclSize, ACL_REVISION);
  185. if (!NT_SUCCESS(Status)) {
  186. goto Exit;
  187. }
  188. Status = RtlAddAccessAllowedAce(pAcl,
  189. ACL_REVISION,
  190. (SPECIFIC_RIGHTS_ALL|STANDARD_RIGHTS_ALL) & ~(WRITE_DAC|WRITE_OWNER),
  191. Anyone);
  192. if (!NT_SUCCESS(Status)) {
  193. goto Exit;
  194. }
  195. Status = RtlCreateSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION);
  196. if (!NT_SUCCESS(Status)) {
  197. goto Exit;
  198. }
  199. Status = RtlSetDaclSecurityDescriptor(psd, TRUE, pAcl, FALSE);
  200. if (!NT_SUCCESS(Status)) {
  201. goto Exit;
  202. }
  203. SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  204. SecurityAttributes.bInheritHandle = TRUE;
  205. SecurityAttributes.lpSecurityDescriptor = psd;
  206. //
  207. // First up is a mutex
  208. // while we are the first process, this user may already have a cache
  209. // and there may already be shared cache
  210. // we need to synchronize here - no need for cs stuff, but the mutex is
  211. // important indeed
  212. //
  213. ghShimCacheMutex = CreateMutexW(&SecurityAttributes, FALSE, gwszCacheMutex);
  214. if (ghShimCacheMutex == NULL) {
  215. goto Exit;
  216. }
  217. //
  218. // wait for the shared mutex (essentially lock the cache out)
  219. //
  220. dwWaitResult = WaitForSingleObject(ghShimCacheMutex, SHIM_CACHE_TIMEOUT);
  221. if (dwWaitResult == WAIT_TIMEOUT) {
  222. //
  223. // We could not acquire the mutex, don't retry, just exit
  224. //
  225. DbgPrint("BaseInitAppcompatCacheSupport: Timeout waiting for shared mutex\n");
  226. return FALSE;
  227. }
  228. bShimCacheLocked = TRUE;
  229. //
  230. // next is shared memory
  231. //
  232. ghShimCacheShared = CreateFileMappingW(INVALID_HANDLE_VALUE,
  233. &SecurityAttributes,
  234. PAGE_READWRITE,
  235. 0,
  236. SHIM_CACHE_SIZE(MAX_SHIM_CACHE_ENTRIES),
  237. gwszCacheSharedMemName);
  238. if (ghShimCacheShared == NULL) {
  239. goto Exit;
  240. }
  241. //
  242. // see if the cache already existed, if so -- we will need to integrate
  243. // currently we do not support integration
  244. //
  245. bExistingCache = (ERROR_ALREADY_EXISTS == GetLastError());
  246. if (bExistingCache) {
  247. DbgPrint("ShimCache: This cache already exists!!!\n");
  248. }
  249. gpShimCacheShared = MapViewOfFile(ghShimCacheShared, FILE_MAP_WRITE, 0, 0, 0);
  250. if (gpShimCacheShared == NULL) {
  251. goto Exit;
  252. }
  253. //
  254. // check whether the cache is new or existing one
  255. //
  256. BasepShimCacheInit(gpShimCacheShared, NULL, MAX_SHIM_CACHE_ENTRIES);
  257. //
  258. // now read the cache
  259. //
  260. BasepShimCacheRead(gpShimCacheShared, SHIM_CACHE_SIZE(MAX_SHIM_CACHE_ENTRIES));
  261. //
  262. // Init temporary directories as well
  263. //
  264. BasepShimCacheInitTempDirs();
  265. DbgPrint("ShimCache Created\n");
  266. bSuccess = TRUE;
  267. Exit:
  268. if (psd != NULL) {
  269. RtlFreeHeap(RtlProcessHeap(), 0, psd);
  270. }
  271. if (Anyone != NULL) {
  272. RtlFreeSid(Anyone);
  273. }
  274. //
  275. // check for success and cleanup
  276. //
  277. if (!bSuccess) {
  278. if (gpShimCacheShared != NULL) {
  279. UnmapViewOfFile(gpShimCacheShared);
  280. gpShimCacheShared = NULL;
  281. }
  282. if (ghShimCacheShared) {
  283. CloseHandle(ghShimCacheShared);
  284. ghShimCacheShared = NULL;
  285. }
  286. if (ghShimCacheMutex) {
  287. if (bShimCacheLocked) {
  288. ReleaseMutex(ghShimCacheMutex);
  289. }
  290. CloseHandle(ghShimCacheMutex);
  291. ghShimCacheMutex = NULL;
  292. }
  293. DbgPrint("ShimCache Creation error\n");
  294. } else {
  295. if (bShimCacheLocked) {
  296. ReleaseMutex(ghShimCacheMutex);
  297. }
  298. }
  299. return(bSuccess);
  300. }
  301. VOID
  302. WINAPI
  303. BaseCleanupAppcompatCache(
  304. VOID
  305. )
  306. {
  307. //
  308. // here we close global objects and cleanup stuff
  309. //
  310. if (!BasepShimCacheLock(NULL, NULL)) {
  311. return;
  312. }
  313. if (gpShimCacheShared != NULL) {
  314. UnmapViewOfFile(gpShimCacheShared);
  315. gpShimCacheShared = NULL;
  316. }
  317. if (ghShimCacheShared != NULL) {
  318. CloseHandle(ghShimCacheShared);
  319. ghShimCacheShared = NULL;
  320. }
  321. RtlFreeUnicodeString(&gustrWindowsTemp);
  322. RtlFreeUnicodeString(&gustrSystemdriveTemp);
  323. BasepShimCacheUnlock();
  324. RtlEnterCriticalSection(&gcsAppCompat);
  325. if (ghShimCacheMutex != NULL) {
  326. CloseHandle(ghShimCacheMutex);
  327. ghShimCacheMutex = NULL;
  328. }
  329. RtlLeaveCriticalSection(&gcsAppCompat);
  330. }
  331. BOOL
  332. WINAPI
  333. BaseCleanupAppcompatCacheSupport(
  334. BOOL bWrite
  335. )
  336. {
  337. //
  338. // we nuke stuff here
  339. //
  340. if (!BasepShimCacheLock(NULL, NULL)) {
  341. return FALSE;
  342. }
  343. //
  344. // we have the lock
  345. //
  346. if (bWrite && gpShimCacheShared != NULL) {
  347. BasepShimCacheWrite((PSHIMCACHEHEADER)gpShimCacheShared);
  348. }
  349. BasepShimCacheUnlock();
  350. return TRUE;
  351. }
  352. //
  353. // Init support for this process
  354. // call inside our critical section!!!
  355. //
  356. BOOL
  357. WINAPI
  358. BaseInitAppcompatCache(
  359. VOID
  360. )
  361. {
  362. DWORD dwWaitResult;
  363. //
  364. // first -- open mutex
  365. //
  366. RtlEnterCriticalSection(&gcsAppCompat); // enter crit sec please
  367. __try {
  368. if (ghShimCacheMutex == NULL) {
  369. ghShimCacheMutex = OpenMutexW(READ_CONTROL | SYNCHRONIZE | MUTEX_MODIFY_STATE,
  370. FALSE,
  371. gwszCacheMutex);
  372. if (ghShimCacheMutex == NULL) {
  373. __leave;
  374. }
  375. // if we are here -- this is the first time we are trying to do this -
  376. // recover then temp dir path
  377. BasepShimCacheInitTempDirs();
  378. }
  379. } __finally {
  380. RtlLeaveCriticalSection(&gcsAppCompat);
  381. }
  382. if (ghShimCacheMutex == NULL) {
  383. DbgPrint("BaseInitAppcompatCache: Failed to acquire shared mutex\n");
  384. return FALSE;
  385. }
  386. dwWaitResult = WaitForSingleObject(ghShimCacheMutex, SHIM_CACHE_TIMEOUT);
  387. if (dwWaitResult == WAIT_TIMEOUT) {
  388. //
  389. // We could not acquire the mutex, don't retry, just exit
  390. //
  391. DbgPrint("BasepShimCacheLock: Timeout waiting for shared mutex\n");
  392. return FALSE;
  393. }
  394. //
  395. // acquired shared mutex, now open section
  396. //
  397. if (ghShimCacheShared == NULL) {
  398. ghShimCacheShared = OpenFileMappingW(FILE_MAP_WRITE, FALSE, gwszCacheSharedMemName);
  399. if (ghShimCacheShared == NULL) {
  400. DbgPrint("BaseInitAppcompatCache: Failed to open file mapping 0x%lx\n", GetLastError());
  401. ReleaseMutex(ghShimCacheMutex);
  402. return FALSE;
  403. }
  404. }
  405. if (gpShimCacheShared == NULL) {
  406. gpShimCacheShared = MapViewOfFile(ghShimCacheShared, FILE_MAP_WRITE, 0, 0, 0);
  407. if (gpShimCacheShared == NULL) {
  408. DbgPrint("BaseInitAppcompatCache: Failed to map view 0x%lx\n", GetLastError());
  409. ReleaseMutex(ghShimCacheMutex);
  410. return FALSE;
  411. }
  412. }
  413. DbgPrint("BaseInitAppcompatCache: Initialized\n");
  414. //
  415. // if we are here -- we have all the shared objects and we are holding mutant too
  416. //
  417. return TRUE;
  418. }
  419. //
  420. // create cache buffer
  421. //
  422. PSHIMCACHEHEADER
  423. BasepShimCacheAllocate(
  424. DWORD dwCacheSize
  425. )
  426. {
  427. DWORD dwBufferSize;
  428. PSHIMCACHEHEADER pBuffer;
  429. dwBufferSize = SHIM_CACHE_SIZE(dwCacheSize);
  430. pBuffer = (PSHIMCACHEHEADER)RtlAllocateHeap(RtlProcessHeap(),
  431. HEAP_ZERO_MEMORY,
  432. dwBufferSize);
  433. if (pBuffer == NULL) {
  434. // debug out
  435. return NULL;
  436. }
  437. pBuffer->dwMagic = SHIM_CACHE_MAGIC;
  438. pBuffer->dwMaxSize = dwCacheSize;
  439. return pBuffer;
  440. }
  441. // load cache from the registry
  442. BOOL
  443. BasepShimCacheRead(
  444. PVOID pCache,
  445. DWORD dwCacheSize // buffer global size
  446. )
  447. {
  448. //
  449. static UNICODE_STRING ustrAppcompatCacheKeyName =
  450. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_KEY_NAME);
  451. static OBJECT_ATTRIBUTES objaAppcompatCacheKeyName =
  452. RTL_CONSTANT_OBJECT_ATTRIBUTES(&ustrAppcompatCacheKeyName, OBJ_CASE_INSENSITIVE);
  453. static UNICODE_STRING ustrAppcompatCacheValueName =
  454. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_VALUE_NAME);
  455. HANDLE hKey = NULL;
  456. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  457. ULONG KeyValueLength = 0;
  458. PVOID pBuffer;
  459. ULONG BufferSize;
  460. BOOL bSuccess = FALSE;
  461. NTSTATUS Status;
  462. Status = NtOpenKey(&hKey, KEY_QUERY_VALUE, (POBJECT_ATTRIBUTES) &objaAppcompatCacheKeyName);
  463. if (!NT_SUCCESS(Status)) {
  464. return FALSE;
  465. }
  466. //
  467. // we have a key, read please
  468. //
  469. BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + dwCacheSize;
  470. pBuffer = (PVOID)RtlAllocateHeap(RtlProcessHeap(),
  471. HEAP_ZERO_MEMORY,
  472. BufferSize);
  473. if (pBuffer == NULL) {
  474. // can't allocate memory
  475. NtClose(hKey);
  476. return FALSE;
  477. }
  478. KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)pBuffer;
  479. Status = NtQueryValueKey(hKey,
  480. &ustrAppcompatCacheValueName,
  481. KeyValuePartialInformation,
  482. KeyValueInformation,
  483. BufferSize,
  484. &KeyValueLength);
  485. if (NT_SUCCESS(Status) &&
  486. KeyValueInformation->Type == REG_BINARY) {
  487. if (BasepShimCacheCheckIntegrity((PSHIMCACHEHEADER)KeyValueInformation->Data)) {
  488. RtlMoveMemory(pCache, KeyValueInformation->Data, KeyValueInformation->DataLength);
  489. DbgPrint("Cache Initialized from the registry\n");
  490. bSuccess = TRUE;
  491. } else {
  492. DbgPrint("Registry data appear to be corrupted\n");
  493. }
  494. }
  495. RtlFreeHeap(RtlProcessHeap(), 0, pBuffer);
  496. NtClose(hKey);
  497. /*++
  498. This call will dump appcompat cache at the startup
  499. Normally disabled so as not to slow down the boot
  500. Dump cache through rundll32 apphelp.dll,ShimDumpCache
  501. #if DBG
  502. BaseDumpAppcompatCache();
  503. #endif
  504. --*/
  505. return bSuccess;
  506. }
  507. BOOL
  508. BasepShimCacheWrite(
  509. PSHIMCACHEHEADER pHeader
  510. )
  511. {
  512. //
  513. static UNICODE_STRING ustrAppcompatCacheKeyName =
  514. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_KEY_NAME);
  515. static OBJECT_ATTRIBUTES objaAppcompatCacheKeyName =
  516. RTL_CONSTANT_OBJECT_ATTRIBUTES(&ustrAppcompatCacheKeyName, OBJ_CASE_INSENSITIVE);
  517. static UNICODE_STRING ustrAppcompatCacheValueName =
  518. RTL_CONSTANT_STRING(APPCOMPAT_CACHE_VALUE_NAME);
  519. HANDLE hKey = NULL;
  520. PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  521. ULONG KeyValueLength = 0;
  522. ULONG BufferSize;
  523. BOOL bSuccess = FALSE;
  524. NTSTATUS Status;
  525. ULONG CreateDisposition;
  526. Status = NtCreateKey(&hKey,
  527. STANDARD_RIGHTS_WRITE |
  528. KEY_QUERY_VALUE |
  529. KEY_ENUMERATE_SUB_KEYS |
  530. KEY_SET_VALUE |
  531. KEY_CREATE_SUB_KEY,
  532. &objaAppcompatCacheKeyName,
  533. 0,
  534. NULL,
  535. REG_OPTION_NON_VOLATILE,
  536. &CreateDisposition);
  537. if (!NT_SUCCESS(Status)) {
  538. DbgPrint("BasepShimCacheWrite: failed to create key 0x%lx\n", Status);
  539. return FALSE;
  540. }
  541. BufferSize = SHIM_CACHE_SIZE(pHeader->dwMaxSize);
  542. Status = NtSetValueKey(hKey,
  543. &ustrAppcompatCacheValueName,
  544. 0,
  545. REG_BINARY,
  546. (PVOID)pHeader,
  547. BufferSize);
  548. if (!NT_SUCCESS(Status)) {
  549. DbgPrint("BasepShimCacheWrite: failed to set the value 0x%lx\n", Status);
  550. }
  551. NtClose(hKey);
  552. DbgPrint("Cache Dumped 0x%lx\n", Status);
  553. return NT_SUCCESS(Status);
  554. }
  555. BOOL
  556. BasepCheckStringPrefixUnicode(
  557. IN PUNICODE_STRING pStrPrefix, // the prefix to check for
  558. IN PUNICODE_STRING pString, // the string
  559. IN BOOL CaseInSensitive
  560. )
  561. /*++
  562. Return: TRUE if the specified string contains pStrPrefix at it's start.
  563. Desc: Verifies if a string is a prefix in another unicode counted string.
  564. It is equivalent to RtlStringPrefix.
  565. --*/
  566. {
  567. PWSTR ps1, ps2;
  568. UINT n;
  569. WCHAR c1, c2;
  570. n = pStrPrefix->Length;
  571. if (pString->Length < n || n == 0) {
  572. return FALSE; // do not prefix with blank strings
  573. }
  574. n /= sizeof(WCHAR); // convert to char count
  575. ps1 = pStrPrefix->Buffer;
  576. ps2 = pString->Buffer;
  577. if (CaseInSensitive) {
  578. while (n--) {
  579. c1 = *ps1++;
  580. c2 = *ps2++;
  581. if (c1 != c2) {
  582. c1 = RtlUpcaseUnicodeChar(c1);
  583. c2 = RtlUpcaseUnicodeChar(c2);
  584. if (c1 != c2) {
  585. return FALSE;
  586. }
  587. }
  588. }
  589. } else {
  590. while (n--) {
  591. if (*ps1++ != *ps2++) {
  592. return FALSE;
  593. }
  594. }
  595. }
  596. return TRUE;
  597. }
  598. BOOL
  599. BasepInitUserTempPath(
  600. PUNICODE_STRING pustrTempPath
  601. )
  602. {
  603. DWORD dwLength;
  604. WCHAR wszBuffer[MAX_PATH];
  605. BOOL TranslationStatus;
  606. BOOL bSuccess = FALSE;
  607. dwLength = BasepGetTempPathW(BASEP_GET_TEMP_PATH_PRESERVE_TEB, sizeof(wszBuffer)/sizeof(wszBuffer[0]), wszBuffer);
  608. if (dwLength && dwLength < sizeof(wszBuffer)/sizeof(wszBuffer[0])) {
  609. TranslationStatus = RtlDosPathNameToNtPathName_U(wszBuffer,
  610. pustrTempPath,
  611. NULL,
  612. NULL);
  613. if (!TranslationStatus) {
  614. DbgPrint("Failed to translate temp directory to nt\n");
  615. }
  616. bSuccess = TranslationStatus;
  617. }
  618. if (!bSuccess) {
  619. DbgPrint("BasepInitUserTempPath: Failed to obtain user's temp path\n");
  620. }
  621. return bSuccess;
  622. }
  623. BOOL
  624. BasepShimCacheInitTempDirs(
  625. VOID
  626. )
  627. {
  628. DWORD dwLength;
  629. WCHAR wszTemp[] = L"\\TEMP";
  630. LPWSTR pwszTemp;
  631. NTSTATUS Status;
  632. UNICODE_STRING ustrSystemDrive;
  633. UNICODE_STRING ustrSystemDriveEnvVarName;
  634. BOOL TranslationStatus;
  635. WCHAR wszBuffer[MAX_PATH];
  636. // next is windows dir
  637. dwLength = GetWindowsDirectoryW(wszBuffer, sizeof(wszBuffer)/sizeof(wszBuffer[0]));
  638. if (dwLength && dwLength < sizeof(wszBuffer)/sizeof(wszBuffer[0])) {
  639. pwszTemp = wszTemp;
  640. if (wszBuffer[dwLength - 1] == L'\\') {
  641. pwszTemp++;
  642. }
  643. wcscpy(&wszBuffer[dwLength], pwszTemp);
  644. TranslationStatus = RtlDosPathNameToNtPathName_U(wszBuffer,
  645. &gustrWindowsTemp,
  646. NULL,
  647. NULL);
  648. if (!TranslationStatus) {
  649. DbgPrint("Failed to translate windows\\temp to nt\n");
  650. }
  651. }
  652. //
  653. // The last one up is Rootdrive\temp for stupid legacy apps.
  654. //
  655. // Especially stupid apps may receive c:\temp as the temp directory
  656. // (what if you don't have drive c, huh?)
  657. //
  658. RtlInitUnicodeString(&ustrSystemDriveEnvVarName, L"SystemDrive");
  659. ustrSystemDrive.Length = 0;
  660. ustrSystemDrive.Buffer = wszBuffer;
  661. ustrSystemDrive.MaximumLength = sizeof(wszBuffer);
  662. Status = RtlQueryEnvironmentVariable_U(NULL,
  663. &ustrSystemDriveEnvVarName,
  664. &ustrSystemDrive);
  665. if (NT_SUCCESS(Status)) {
  666. pwszTemp = wszTemp;
  667. dwLength = ustrSystemDrive.Length / sizeof(WCHAR);
  668. if (wszBuffer[dwLength - 1] == L'\\') {
  669. pwszTemp++;
  670. }
  671. wcscpy(&wszBuffer[dwLength], pwszTemp);
  672. TranslationStatus = RtlDosPathNameToNtPathName_U(wszBuffer,
  673. &gustrSystemdriveTemp,
  674. NULL,
  675. NULL);
  676. if (!TranslationStatus) {
  677. DbgPrint("Failed to translate windows\\temp to nt\n");
  678. }
  679. }
  680. DbgPrint("BasepShimCacheInitTempDirs: Temporary Windows Dir: %S\n", gustrWindowsTemp.Buffer != NULL ? gustrWindowsTemp.Buffer : L"");
  681. DbgPrint("BasepShimCacheInitTempDirs: Temporary SystedDrive: %S\n", gustrSystemdriveTemp.Buffer != NULL ? gustrSystemdriveTemp.Buffer : L"");
  682. return TRUE;
  683. }
  684. BOOL
  685. BasepShimCacheCheckBypass(
  686. IN LPCWSTR pwszPath, // the full path to the EXE to be started
  687. IN HANDLE hFile,
  688. IN WCHAR* pEnvironment, // the environment of the starting EXE
  689. IN BOOL bCheckLayer, // should we check the layer too?
  690. OUT DWORD* pdwReason
  691. )
  692. /*++
  693. Return: TRUE if the cache should be bypassed, FALSE otherwise.
  694. Desc: This function checks if any of the conditions to bypass the cache are met.
  695. --*/
  696. {
  697. UNICODE_STRING ustrPath;
  698. PUNICODE_STRING rgp[3];
  699. int i;
  700. NTSTATUS Status;
  701. UNICODE_STRING ustrCompatLayerVarName;
  702. UNICODE_STRING ustrCompatLayer;
  703. BOOL bBypassCache = FALSE;
  704. DWORD dwReason = 0;
  705. UNICODE_STRING ustrUserTempPath = { 0 };
  706. //
  707. // Is the EXE is running from removable media we need to bypass the cache.
  708. //
  709. if (hFile != INVALID_HANDLE_VALUE && BasepIsRemovableMedia(hFile, TRUE)) {
  710. bBypassCache = TRUE;
  711. dwReason |= SHIM_CACHE_MEDIA;
  712. goto CheckLayer;
  713. }
  714. //
  715. // init user's temp path now and get up-to-date one
  716. //
  717. BasepInitUserTempPath(&ustrUserTempPath);
  718. //
  719. // Check now if the EXE is launched from one of the temp directories.
  720. //
  721. RtlInitUnicodeString(&ustrPath, pwszPath);
  722. rgp[0] = &gustrWindowsTemp;
  723. rgp[1] = &ustrUserTempPath;
  724. rgp[2] = &gustrSystemdriveTemp;
  725. for (i = 0; i < sizeof(rgp) / sizeof(rgp[0]); i++) {
  726. if (rgp[i]->Buffer != NULL && BasepCheckStringPrefixUnicode(rgp[i], &ustrPath, TRUE)) {
  727. DbgPrint("Application \"%ls\" is running in temp directory\n", pwszPath);
  728. bBypassCache = TRUE;
  729. dwReason |= SHIM_CACHE_TEMP;
  730. break;
  731. }
  732. }
  733. RtlFreeUnicodeString(&ustrUserTempPath);
  734. CheckLayer:
  735. if (bCheckLayer) {
  736. //
  737. // Check if the __COMPAT_LAYER environment variable is set
  738. //
  739. RtlInitUnicodeString(&ustrCompatLayerVarName, L"__COMPAT_LAYER");
  740. ustrCompatLayer.Length = 0;
  741. ustrCompatLayer.MaximumLength = 0;
  742. ustrCompatLayer.Buffer = NULL;
  743. Status = RtlQueryEnvironmentVariable_U(pEnvironment,
  744. &ustrCompatLayerVarName,
  745. &ustrCompatLayer);
  746. //
  747. // If the Status is STATUS_BUFFER_TOO_SMALL this means the variable is set.
  748. //
  749. if (Status == STATUS_BUFFER_TOO_SMALL) {
  750. dwReason |= SHIM_CACHE_LAYER_ENV;
  751. bBypassCache = TRUE;
  752. }
  753. }
  754. if (pdwReason != NULL) {
  755. *pdwReason = dwReason;
  756. }
  757. return bBypassCache;
  758. }
  759. NTSTATUS
  760. BasepShimCacheQueryFileInformation(
  761. HANDLE FileHandle,
  762. PLONGLONG pFileSize,
  763. PLONGLONG pFileTime
  764. )
  765. /*++
  766. Return: TRUE on success, FALSE otherwise.
  767. Desc: Queries for file size and timestamp.
  768. --*/
  769. {
  770. NTSTATUS Status;
  771. IO_STATUS_BLOCK IoStatusBlock;
  772. FILE_BASIC_INFORMATION BasicFileInfo;
  773. FILE_STANDARD_INFORMATION StdFileInfo;
  774. Status = NtQueryInformationFile(FileHandle,
  775. &IoStatusBlock,
  776. &BasicFileInfo,
  777. sizeof(BasicFileInfo),
  778. FileBasicInformation);
  779. if (!NT_SUCCESS(Status)) {
  780. /*
  781. DBGPRINT((sdlError,
  782. "ShimQueryFileInformation",
  783. "NtQueryInformationFile/BasicInfo failed 0x%x\n",
  784. Status));
  785. */
  786. DbgPrint("BasepShimCacheQueryFileInformation: NtQueryInformationFile(BasicFileInfo) failed 0x%lx\n", Status);
  787. return Status;
  788. }
  789. *pFileTime = BasicFileInfo.LastWriteTime.QuadPart;
  790. Status = NtQueryInformationFile(FileHandle,
  791. &IoStatusBlock,
  792. &StdFileInfo,
  793. sizeof(StdFileInfo),
  794. FileStandardInformation);
  795. if (!NT_SUCCESS(Status)) {
  796. /*
  797. DBGPRINT((sdlError,
  798. "ShimQueryFileInformation",
  799. "NtQueryInformationFile/StdInfo failed 0x%x\n",
  800. Status));
  801. */
  802. DbgPrint("BasepShimCacheQueryFileInformation: NtQueryInformationFile(StdFileInfo) failed 0x%lx\n", Status);
  803. return Status;
  804. }
  805. *pFileSize = StdFileInfo.EndOfFile.QuadPart;
  806. return STATUS_SUCCESS;
  807. }
  808. BOOL
  809. BasepIsRemovableMedia(
  810. HANDLE FileHandle,
  811. BOOL bCacheNetwork
  812. )
  813. /*++
  814. Return: TRUE if the media from where the app is run is removable,
  815. FALSE otherwise.
  816. Desc: Queries the media for being removable.
  817. --*/
  818. {
  819. NTSTATUS Status;
  820. IO_STATUS_BLOCK IoStatusBlock;
  821. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  822. BOOL bRemovable = FALSE;
  823. Status = NtQueryVolumeInformationFile(FileHandle,
  824. &IoStatusBlock,
  825. &DeviceInfo,
  826. sizeof(DeviceInfo),
  827. FileFsDeviceInformation);
  828. if (!NT_SUCCESS(Status)) {
  829. /*
  830. DBGPRINT((sdlError,
  831. "IsRemovableMedia",
  832. "NtQueryVolumeInformationFile Failed 0x%x\n",
  833. Status));
  834. */
  835. DbgPrint("BasepIsRemovableMedia: NtQueryVolumeInformationFile failed 0x%lx\n", Status);
  836. return TRUE;
  837. }
  838. //
  839. // We look at the characteristics of this particular device.
  840. // If the media is cdrom then we DO NOT need to convert to local time
  841. //
  842. bRemovable = (DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA);
  843. if (!bCacheNetwork) {
  844. bRemovable |= (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE);
  845. }
  846. if (!bRemovable) {
  847. //
  848. // Check the device type now.
  849. //
  850. switch (DeviceInfo.DeviceType) {
  851. case FILE_DEVICE_CD_ROM:
  852. case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
  853. bRemovable = TRUE;
  854. break;
  855. case FILE_DEVICE_NETWORK:
  856. case FILE_DEVICE_NETWORK_FILE_SYSTEM:
  857. if (!bCacheNetwork) {
  858. bRemovable = TRUE;
  859. }
  860. break;
  861. }
  862. }
  863. if (bRemovable) {
  864. DbgPrint("BasepIsRemovableMedia: Host device is removable, Shim cache deactivated\n");
  865. /*
  866. DBGPRINT((sdlInfo,
  867. "IsRemovableMedia",
  868. "The host device is removable. Shim cache deactivated for this file\n"));
  869. */
  870. }
  871. return bRemovable;
  872. }
  873. BOOL
  874. BasepShimCacheInit(
  875. PSHIMCACHEHEADER pHeader,
  876. PSHIMCACHEENTRY pEntries,
  877. DWORD dwMaxEntries
  878. )
  879. {
  880. // initialize the cache
  881. DWORD dwCacheSizeHeader;
  882. INT i;
  883. dwCacheSizeHeader = sizeof(SHIMCACHEHEADER) +
  884. dwMaxEntries * sizeof(int);
  885. RtlZeroMemory(pHeader, dwCacheSizeHeader);
  886. pHeader->dwMagic = SHIM_CACHE_MAGIC;
  887. pHeader->dwMaxSize = dwMaxEntries;
  888. if (pEntries != NULL) {
  889. RtlZeroMemory(pEntries, dwMaxEntries * sizeof(SHIMCACHEENTRY));
  890. }
  891. // dwCount of active entries is set to nothing
  892. // now roll through the entries and set them to -1
  893. for (i = 0; i < (int)dwMaxEntries; ++i) {
  894. pHeader->rgIndex[i] = -1;
  895. }
  896. return TRUE;
  897. }
  898. BOOL
  899. BasepShimCacheSearch(
  900. IN PSHIMCACHEHEADER pHeader,
  901. IN PSHIMCACHEENTRY pEntries,
  902. IN LPCWSTR pwszPath,
  903. OUT int* pIndex
  904. )
  905. /*++
  906. Return: TRUE if we have a cache hit, FALSE otherwise.
  907. Desc: Search the cache, return TRUE if we have a cache hit
  908. pIndex will receive an index into the rgIndex array that contains
  909. the entry which has been hit
  910. So that if entry 5 contains the hit, and rgIndexes[3] == 5 then
  911. *pIndex == 3
  912. --*/
  913. {
  914. int nIndex, nEntry;
  915. WCHAR* pCachePath;
  916. BOOL bSuccess;
  917. for (nIndex = 0; nIndex < (int)pHeader->dwCount; nIndex++) {
  918. nEntry = pHeader->rgIndex[nIndex];
  919. if (nEntry >= 0 && nEntry < (int)pHeader->dwMaxSize) { // guard against corruption
  920. pCachePath = pEntries[nEntry].wszPath;
  921. if (*pCachePath != L'\0' && !_wcsicmp(pwszPath, pCachePath)) {
  922. //
  923. // succeess
  924. //
  925. if (pIndex != NULL) {
  926. *pIndex = nIndex;
  927. }
  928. return TRUE;
  929. }
  930. }
  931. }
  932. return FALSE;
  933. }
  934. BOOL
  935. BasepShimCacheUpdateLRUIndex(
  936. OUT PSHIMCACHEHEADER pHeader,
  937. OUT PSHIMCACHEENTRY pEntries,
  938. IN int nIndex
  939. )
  940. /*++
  941. Return: TRUE.
  942. Desc: Update the LRU list by taking nIndex and inserting it into the head of the list.
  943. For instance, rgIndexes before the update:
  944. 2 5 9 3 1 ...
  945. if nIndex == 2, rgIndexes after the update:
  946. 9 2 5 3 1
  947. The entry at pos. 2 containing a hit entry number (which is 9) was moved
  948. to the front of the index.
  949. --*/
  950. {
  951. int nEntry;
  952. int* pStart;
  953. int* pTo;
  954. LARGE_INTEGER liTimeStamp;
  955. FILETIME ft;
  956. if (nIndex < 0 || nIndex >= (int)pHeader->dwMaxSize) {
  957. DbgPrint("BasepShimCacheUpdateLRUIndex: Bad index %ld\n", nIndex);
  958. return FALSE;
  959. }
  960. nEntry = pHeader->rgIndex[nIndex];
  961. //
  962. // Zap the entry by shifting memory to the right.
  963. //
  964. pStart = &pHeader->rgIndex[0];
  965. pTo = &pHeader->rgIndex[1];
  966. RtlMoveMemory(pTo, pStart, nIndex * sizeof(int));
  967. pHeader->rgIndex[0] = nEntry;
  968. GetSystemTimeAsFileTime(&ft);
  969. liTimeStamp.LowPart = ft.dwLowDateTime;
  970. liTimeStamp.HighPart = ft.dwHighDateTime;
  971. pEntries[nEntry].TimeStamp = liTimeStamp.QuadPart;
  972. return TRUE;
  973. }
  974. BOOL
  975. BasepShimCacheRemoveEntry(
  976. OUT PSHIMCACHEHEADER pHeader,
  977. OUT PSHIMCACHEENTRY pEntries,
  978. IN int nIndex // the index in rgIndex array containing
  979. // the entry that is to be removed.
  980. )
  981. /*++
  982. Return: TRUE.
  983. Desc: Remove the entry from the cache.
  984. We remove the entry by placing it as the last lru entry
  985. and emptying the path. This routine assumes that the index
  986. passed in is valid.
  987. --*/
  988. {
  989. int nLast, nEntry;
  990. int* pTo;
  991. int* pStart;
  992. PSHIMCACHEENTRY pEntry;
  993. if (nIndex < 0 || nIndex >= (int)pHeader->dwCount) {
  994. DbgPrint("BasepShimRemoveFromCache: Invalid index %ld\n", nIndex);
  995. return FALSE;
  996. }
  997. //
  998. // Get the entry.
  999. //
  1000. nEntry = pHeader->rgIndex[nIndex];
  1001. if (pHeader->dwCount < pHeader->dwMaxSize) {
  1002. nLast = pHeader->dwCount - 1;
  1003. } else {
  1004. nLast = pHeader->dwMaxSize - 1;
  1005. }
  1006. //
  1007. // 1. remove it from the LRU index in such a way that we account for
  1008. // number of entries.
  1009. //
  1010. pTo = &pHeader->rgIndex[nIndex];
  1011. pStart = &pHeader->rgIndex[nIndex + 1];
  1012. RtlMoveMemory(pTo, pStart, (nLast - nIndex) * sizeof(INT));
  1013. pHeader->rgIndex[nLast] = nEntry;
  1014. //
  1015. // 2. kill the path.
  1016. //
  1017. pEntry = pEntries + nEntry;
  1018. DbgPrint("BasepShimCacheRemoveEntry: removing %ld \"%S\"\n", nIndex, pEntry->wszPath);
  1019. *pEntry->wszPath = L'\0';
  1020. pEntry->TimeStamp = 0;
  1021. pEntry->FileSize = 0;
  1022. pEntry->FileTime = 0;
  1023. return TRUE;
  1024. }
  1025. PSHIMCACHEENTRY
  1026. BasepShimCacheAllocateEntry(
  1027. IN OUT PSHIMCACHEHEADER pHeader,
  1028. IN OUT PSHIMCACHEENTRY pEntries,
  1029. IN LPCWSTR pwszPath,
  1030. IN LONGLONG FileSize,
  1031. IN LONGLONG FileTime
  1032. )
  1033. /*++
  1034. Return: The pointer to the new entry.
  1035. Desc: Allocate an entry in the cache. The entry is acquired using the LRU algorithm
  1036. The least recently used entry is contained in rgIndex[MAX_SHIM_CACHE_ENTRIES-1]
  1037. or the next available entry if cache has not filled up yet. The entry returned
  1038. is also taken to the front of the list (making it most recently used one).
  1039. --*/
  1040. {
  1041. int nEntry;
  1042. int nIndex = -1;
  1043. int nFileNameSize;
  1044. PSHIMCACHEENTRY pEntry = NULL;
  1045. nFileNameSize = (wcslen(pwszPath) + 1) * sizeof(WCHAR);
  1046. if (nFileNameSize > sizeof(pEntry->wszPath)) {
  1047. DbgPrint("BasepShimCacheAllocateEntry: path is too long to be cached\n");
  1048. return NULL;
  1049. }
  1050. if (pHeader->dwCount < pHeader->dwMaxSize) {
  1051. //
  1052. // We can add a new entry.
  1053. //
  1054. nIndex = (int)pHeader->dwCount++;
  1055. nEntry = nIndex;
  1056. pHeader->rgIndex[nIndex] = nEntry;
  1057. } else {
  1058. //
  1059. // Displacement
  1060. //
  1061. nIndex = pHeader->dwMaxSize - 1; // displacing the very last entry
  1062. nEntry = (int)pHeader->rgIndex[nIndex];
  1063. }
  1064. //
  1065. // Now update... making this entry appear first
  1066. //
  1067. BasepShimCacheUpdateLRUIndex(pHeader, pEntries, nIndex);
  1068. pEntry = pEntries + nEntry;
  1069. //
  1070. // Copy the path and fill out the info
  1071. //
  1072. RtlMoveMemory(pEntry->wszPath, pwszPath, nFileNameSize);
  1073. pEntry->FileSize = FileSize;
  1074. pEntry->FileTime = FileTime;
  1075. DbgPrint("BaseShimCacheAllocateEntry: Entry \"%S\" index0 %ld Entry %ld\n", pwszPath, pHeader->rgIndex[0], nEntry);
  1076. return pEntry;
  1077. }
  1078. //
  1079. // This function is called to search the cache and update the
  1080. // entry if found. It will not check for the removable media -- but
  1081. // it does check other conditions (update file for instance)
  1082. //
  1083. BOOL
  1084. BasepShimCacheLookup(
  1085. PSHIMCACHEHEADER pHeader,
  1086. PSHIMCACHEENTRY pEntries,
  1087. LPCWSTR pwszPath,
  1088. HANDLE hFile
  1089. )
  1090. {
  1091. NTSTATUS Status;
  1092. LONGLONG FileSize = 0;
  1093. LONGLONG FileTime = 0;
  1094. INT nIndex = 0;
  1095. PSHIMCACHEENTRY pEntry;
  1096. if (!BasepShimCacheSearch(pHeader, pEntries, pwszPath, &nIndex)) {
  1097. return FALSE; // not found, sorry
  1098. }
  1099. //
  1100. // query file's information so that we can make sure it is the same file
  1101. //
  1102. if (hFile != INVALID_HANDLE_VALUE) {
  1103. //
  1104. // get file's information and compare to the entry
  1105. //
  1106. Status = BasepShimCacheQueryFileInformation(hFile, &FileSize, &FileTime);
  1107. if (!NT_SUCCESS(Status)) {
  1108. //
  1109. // we cannot confirm that the file is of certain size and/or timestamp,
  1110. // we treat this then as a non-entry. debug message will be printed from the function
  1111. // above. Whether we have found the file -- or not -- is quite irrelevant, do the detection
  1112. //
  1113. return FALSE;
  1114. }
  1115. pEntry = pEntries + pHeader->rgIndex[nIndex];
  1116. //
  1117. // check size and timestamp
  1118. //
  1119. if (pEntry->FileTime != FileTime || pEntry->FileSize != FileSize) {
  1120. //
  1121. // we will have to run detection again, this entry is bad and shall be removed
  1122. //
  1123. DbgPrint("BasepShimCacheLookup: Entry for file \"%S\" is invalid and will be removed\n", pwszPath);
  1124. BasepShimCacheRemoveEntry(pHeader, pEntries, nIndex);
  1125. return FALSE;
  1126. }
  1127. }
  1128. //
  1129. // check if this entry has been disallowed
  1130. //
  1131. if (!BasepCheckCacheExcludeList(pwszPath)) {
  1132. DbgPrint("BasepShimCacheLookup: Entry for %ls was disallowed yet found in cache, cleaning up\n", pwszPath);
  1133. BasepShimCacheRemoveEntry(pHeader, pEntries, nIndex);
  1134. return FALSE;
  1135. }
  1136. BasepShimCacheUpdateLRUIndex(pHeader, pEntries, nIndex);
  1137. return TRUE;
  1138. }
  1139. //
  1140. // This function will update the cache if need be
  1141. // called from apphelp to make sure that the file is cached (no fixes)
  1142. //
  1143. BOOL
  1144. BasepShimCacheUpdate(
  1145. PSHIMCACHEHEADER pHeader,
  1146. PSHIMCACHEENTRY pEntries,
  1147. LPCWSTR pwszPath,
  1148. HANDLE hFile
  1149. )
  1150. {
  1151. int nIndex;
  1152. LONGLONG FileSize = 0;
  1153. LONGLONG FileTime = 0;
  1154. PSHIMCACHEENTRY pEntry;
  1155. NTSTATUS Status;
  1156. if (hFile != INVALID_HANDLE_VALUE) {
  1157. Status = BasepShimCacheQueryFileInformation(hFile, &FileSize, &FileTime);
  1158. if (!NT_SUCCESS(Status)) {
  1159. //
  1160. // can't update entry
  1161. //
  1162. DbgPrint("BasepShimCacheUpdate: Failed to obtain file information\n");
  1163. return FALSE;
  1164. }
  1165. }
  1166. //
  1167. // if bRemove is TRUE, we remove this entry from the cache
  1168. //
  1169. if (BasepShimCacheSearch(pHeader, pEntries, pwszPath, &nIndex)) {
  1170. //
  1171. // found an existing entry
  1172. //
  1173. pEntry = pEntries + pHeader->rgIndex[nIndex];
  1174. if (pEntry->FileTime == FileTime && pEntry->FileSize == FileSize) {
  1175. //
  1176. // good entry, update lru
  1177. //
  1178. BasepShimCacheUpdateLRUIndex(pHeader, pEntries, nIndex);
  1179. return TRUE; // we are done
  1180. }
  1181. //
  1182. // we are here because we have found a bad entry, remove it and continue
  1183. //
  1184. BasepShimCacheRemoveEntry(pHeader, pEntries, nIndex);
  1185. }
  1186. //
  1187. // we have not found an entry -- or removed a bad entry, allocate it anew
  1188. //
  1189. BasepShimCacheAllocateEntry(pHeader, pEntries, pwszPath, FileSize, FileTime);
  1190. return TRUE;
  1191. }
  1192. DWORD
  1193. BasepBitMapCountBits(
  1194. PULONGLONG pMap,
  1195. DWORD dwMapSize
  1196. )
  1197. {
  1198. DWORD nBits = 0;
  1199. ULONGLONG Element;
  1200. INT i;
  1201. for (i = 0; i < (int)dwMapSize; ++i) {
  1202. Element = *pMap++;
  1203. while (Element) {
  1204. ++nBits;
  1205. Element &= (Element - 1);
  1206. }
  1207. }
  1208. return nBits;
  1209. }
  1210. //
  1211. // returns previous value of a flag
  1212. //
  1213. BOOL
  1214. BasepBitMapSetBit(
  1215. PULONGLONG pMap,
  1216. DWORD dwMapSize,
  1217. INT nBit
  1218. )
  1219. {
  1220. INT nElement;
  1221. INT nElementBit;
  1222. ULONGLONG Flag;
  1223. BOOL bNotSet;
  1224. nElement = nBit / (sizeof(ULONGLONG) * 8);
  1225. nElementBit = nBit % (sizeof(ULONGLONG) * 8);
  1226. Flag = (ULONGLONG)1 << nElementBit;
  1227. pMap += nElement;
  1228. bNotSet = !(*pMap & Flag);
  1229. if (bNotSet) {
  1230. *pMap |= Flag;
  1231. return FALSE;
  1232. }
  1233. return TRUE; // set already
  1234. }
  1235. BOOL
  1236. BasepBitMapCheckBit(
  1237. PULONGLONG pMap,
  1238. DWORD dwMapSize,
  1239. INT nBit
  1240. )
  1241. {
  1242. INT nElement;
  1243. INT nElementBit;
  1244. nElement = nBit / (sizeof(ULONGLONG) * 8);
  1245. nElementBit = nBit % (sizeof(ULONGLONG) * 8);
  1246. pMap += nElement;
  1247. return !!(*pMap & ((ULONGLONG)1 << nElementBit));
  1248. }
  1249. BOOL
  1250. BasepShimCacheCheckIntegrity(
  1251. PSHIMCACHEHEADER pHeader
  1252. )
  1253. {
  1254. ULONGLONG EntryMap[MAX_SHIM_CACHE_ENTRIES / (sizeof(ULONGLONG) * 8) + 1] = { 0 };
  1255. INT nEntry;
  1256. INT nIndex;
  1257. //
  1258. // validate magic number
  1259. //
  1260. if (pHeader->dwMagic != SHIM_CACHE_MAGIC) {
  1261. DbgPrint("BasepShimCheckCacheIntegrity: Bad magic number\n");
  1262. return FALSE;
  1263. }
  1264. //
  1265. // validate counters
  1266. //
  1267. if (pHeader->dwMaxSize > MAX_SHIM_CACHE_ENTRIES || pHeader->dwMaxSize == 0) {
  1268. DbgPrint("BasepShimCheckCacheIntegrity: Cache size is corrupted\n");
  1269. return FALSE;
  1270. }
  1271. if (pHeader->dwCount > pHeader->dwMaxSize) {
  1272. DbgPrint("BasepShimCheckCacheIntegrity: Cache element count is corrupted\n");
  1273. return FALSE;
  1274. }
  1275. //
  1276. // check index entries
  1277. //
  1278. for (nIndex = 0; nIndex < (int)pHeader->dwMaxSize; ++nIndex) {
  1279. nEntry = pHeader->rgIndex[nIndex];
  1280. if (nEntry >= 0 && nEntry < (int)pHeader->dwMaxSize) { // the only way we should have this condition -- is when we're traversing unused entries
  1281. //
  1282. // (to verify index we mark each entry in our bit-map)
  1283. //
  1284. if (BasepBitMapSetBit(EntryMap, sizeof(EntryMap)/sizeof(EntryMap[0]), nEntry)) {
  1285. //
  1286. // duplicate cache entry
  1287. //
  1288. DbgPrint("BasepShimCheckCacheIntegrity: Found duplicate cache entry\n");
  1289. return FALSE;
  1290. }
  1291. } else { // either nEntry < 0 or nEntry > MaxSize
  1292. if (nEntry < 0 && nIndex < (int)pHeader->dwCount) {
  1293. //
  1294. // trouble -- we have a bad entry
  1295. //
  1296. DbgPrint("BasepShimCheckCacheIntegrity: bad entry\n");
  1297. return FALSE;
  1298. }
  1299. // now check for overflow
  1300. if (nEntry >= (int)pHeader->dwMaxSize) {
  1301. DbgPrint("BasepShimCheckCacheIntegrity: overflow\n");
  1302. return FALSE;
  1303. }
  1304. }
  1305. }
  1306. //
  1307. // we have survived index check - verify that the count of elements is correct
  1308. //
  1309. if (pHeader->dwCount != BasepBitMapCountBits(EntryMap, sizeof(EntryMap)/sizeof(EntryMap[0]))) {
  1310. DbgPrint("BasepShimCheckCacheIntegrity: count mismatch\n");
  1311. return FALSE;
  1312. }
  1313. return TRUE;
  1314. }
  1315. BOOL
  1316. BasepShimCacheUnlock(
  1317. VOID
  1318. )
  1319. {
  1320. if (ghShimCacheMutex) {
  1321. return ReleaseMutex(ghShimCacheMutex);
  1322. }
  1323. return FALSE;
  1324. }
  1325. BOOL
  1326. BasepShimCacheLock(
  1327. PSHIMCACHEHEADER* ppHeader,
  1328. PSHIMCACHEENTRY* ppEntries
  1329. )
  1330. {
  1331. NTSTATUS Status;
  1332. DWORD dwWaitResult;
  1333. PSHIMCACHEHEADER pHeader = NULL;
  1334. PSHIMCACHEENTRY pEntries = NULL;
  1335. BOOL bReturn = FALSE;
  1336. //
  1337. // the function below will open (but not create!) all the shared objects
  1338. //
  1339. if (!BaseInitAppcompatCache()) {
  1340. DbgPrint("Call to BaseInitAppCompatCache failed\n");
  1341. return FALSE;
  1342. }
  1343. __try {
  1344. pHeader = (PSHIMCACHEHEADER)gpShimCacheShared;
  1345. //
  1346. // we have obtained global mutex - check the cache for defects
  1347. //
  1348. if (!BasepShimCacheCheckIntegrity(pHeader)) {
  1349. // cannot verify cache integrity -- too bad, re-init
  1350. BasepShimCacheInit(pHeader, NULL, MAX_SHIM_CACHE_ENTRIES);
  1351. }
  1352. pEntries = GET_SHIM_CACHE_ENTRIES(pHeader);
  1353. bReturn = TRUE;
  1354. } __except(EXCEPTION_EXECUTE_HANDLER){
  1355. DbgPrint("BasepShimCacheLock: exception while trying to check cache\n");
  1356. bReturn = FALSE;
  1357. }
  1358. if (bReturn) {
  1359. if (ppHeader != NULL) {
  1360. *ppHeader = pHeader;
  1361. }
  1362. if (ppEntries != NULL) {
  1363. *ppEntries = pEntries;
  1364. }
  1365. } else {
  1366. BasepShimCacheUnlock();
  1367. }
  1368. return bReturn;
  1369. }
  1370. /*++
  1371. Callable functions, with protection, etc
  1372. BasepCheckAppcompatCache returns true if an app has been found in cache, no fixes are needed
  1373. if BasepCheckAppcompatCache returns false - we will have to call into apphelp.dll to check further
  1374. apphelp.dll will then call BasepUpdateAppcompatCache if an app has no fixes to be applied to it
  1375. --*/
  1376. BOOL
  1377. WINAPI
  1378. BaseCheckAppcompatCache(
  1379. LPCWSTR pwszPath,
  1380. HANDLE hFile,
  1381. PVOID pEnvironment,
  1382. DWORD* pdwReason
  1383. )
  1384. {
  1385. PSHIMCACHEHEADER pHeader = NULL;
  1386. PSHIMCACHEENTRY pEntries = NULL;
  1387. BOOL bFoundInCache = FALSE;
  1388. BOOL bLayer = FALSE;
  1389. DWORD dwReason = 0;
  1390. if (BasepShimCacheCheckBypass(pwszPath, hFile, pEnvironment, TRUE, &dwReason)) {
  1391. //
  1392. // cache bypass was needed
  1393. //
  1394. dwReason |= SHIM_CACHE_BYPASS;
  1395. DbgPrint("Application \"%S\" Cache bypassed reason 0x%lx\n", pwszPath, dwReason);
  1396. goto Exit;
  1397. }
  1398. //
  1399. // aquire cache
  1400. //
  1401. if (!BasepShimCacheLock(&pHeader, &pEntries)) {
  1402. //
  1403. // cannot lock the cache
  1404. //
  1405. dwReason |= SHIM_CACHE_NOTAVAIL;
  1406. DbgPrint("Application \"%S\" cache not available\n", pwszPath);
  1407. goto Exit;
  1408. }
  1409. __try {
  1410. //
  1411. // search the cache
  1412. //
  1413. bFoundInCache = BasepShimCacheLookup(pHeader, pEntries, pwszPath, hFile);
  1414. if (!bFoundInCache) {
  1415. dwReason |= SHIM_CACHE_NOT_FOUND;
  1416. }
  1417. } __except(EXCEPTION_EXECUTE_HANDLER) {
  1418. DbgPrint("BaseCheckAppcompatCache: Exception while trying to lookup cache\n");
  1419. bFoundInCache = FALSE;
  1420. }
  1421. if (bFoundInCache) {
  1422. DbgPrint("Application \"%S\" found in cache\n", pwszPath);
  1423. } else {
  1424. DbgPrint("Application \"%S\" not found in cache\n", pwszPath);
  1425. }
  1426. BasepShimCacheUnlock();
  1427. Exit:
  1428. if (pdwReason != NULL) {
  1429. *pdwReason = dwReason;
  1430. }
  1431. return bFoundInCache;
  1432. }
  1433. BOOL
  1434. WINAPI
  1435. BaseUpdateAppcompatCache(
  1436. LPCWSTR pwszPath,
  1437. HANDLE hFile,
  1438. BOOL bRemove
  1439. )
  1440. {
  1441. PSHIMCACHEHEADER pHeader = NULL;
  1442. PSHIMCACHEENTRY pEntries = NULL;
  1443. BOOL bSuccess = FALSE;
  1444. INT nIndex = -1;
  1445. if (!BasepShimCacheLock(&pHeader, &pEntries)) {
  1446. //
  1447. // cannot lock the cache
  1448. //
  1449. DbgPrint("BaseUpdateAppcompatCache: Cache not available\n");
  1450. return FALSE;
  1451. }
  1452. __try {
  1453. //
  1454. // search the cache
  1455. //
  1456. if (bRemove) {
  1457. if (BasepShimCacheSearch(pHeader, pEntries, pwszPath, &nIndex)) {
  1458. bSuccess = BasepShimCacheRemoveEntry(pHeader, pEntries, nIndex);
  1459. }
  1460. } else {
  1461. //
  1462. // before updating check whether this entry should be bypassed
  1463. //
  1464. if (!BasepShimCacheCheckBypass(pwszPath, hFile, NULL, FALSE, NULL)) {
  1465. bSuccess = BasepShimCacheUpdate(pHeader, pEntries, pwszPath, hFile);
  1466. }
  1467. }
  1468. } __except(EXCEPTION_EXECUTE_HANDLER) {
  1469. bSuccess = FALSE;
  1470. }
  1471. BasepShimCacheUnlock();
  1472. return bSuccess;
  1473. }
  1474. BOOL
  1475. WINAPI
  1476. BaseFlushAppcompatCache(
  1477. VOID
  1478. )
  1479. {
  1480. PSHIMCACHEHEADER pHeader = NULL;
  1481. PSHIMCACHEENTRY pEntries = NULL;
  1482. BOOL bSuccess = FALSE;
  1483. if (!BasepShimCacheLock(&pHeader, &pEntries)) {
  1484. //
  1485. // cannot lock the cache
  1486. //
  1487. return FALSE;
  1488. }
  1489. __try {
  1490. //
  1491. // init the cache
  1492. //
  1493. BasepShimCacheInit(pHeader, pEntries, MAX_SHIM_CACHE_ENTRIES);
  1494. //
  1495. // now write the value into the registry
  1496. //
  1497. BasepShimCacheWrite(pHeader);
  1498. bSuccess = TRUE;
  1499. } __except(EXCEPTION_EXECUTE_HANDLER) {
  1500. bSuccess = FALSE;
  1501. }
  1502. BasepShimCacheUnlock();
  1503. if (bSuccess) {
  1504. DbgPrint("BaseFlushAppcompatCache: Cache Initialized\n");
  1505. }
  1506. return bSuccess;
  1507. }
  1508. //
  1509. // returns TRUE if cache is allowed
  1510. //
  1511. BOOL
  1512. BasepCheckCacheExcludeList(
  1513. LPCWSTR pwszPath
  1514. )
  1515. {
  1516. NTSTATUS Status;
  1517. ULONG ResultLength;
  1518. OBJECT_ATTRIBUTES ObjectAttributes;
  1519. UNICODE_STRING KeyPathUser = { 0 }; // path to hkcu
  1520. UNICODE_STRING ExePathNt; // temp holder
  1521. KEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
  1522. RTL_UNICODE_STRING_BUFFER ExePathBuffer; // buffer to store exe path
  1523. RTL_UNICODE_STRING_BUFFER KeyNameBuffer;
  1524. UCHAR BufferKey[MAX_PATH * 2];
  1525. UCHAR BufferPath[MAX_PATH * 2];
  1526. HANDLE KeyHandle = NULL;
  1527. BOOL bCacheAllowed = FALSE;
  1528. RtlInitUnicodeStringBuffer(&ExePathBuffer, BufferPath, sizeof(BufferPath));
  1529. RtlInitUnicodeStringBuffer(&KeyNameBuffer, BufferKey, sizeof(BufferKey));
  1530. Status = RtlFormatCurrentUserKeyPath(&KeyPathUser);
  1531. if (!NT_SUCCESS(Status)) {
  1532. DbgPrint("BasepCheckCacheExcludeList: failed to format user key path 0x%lx\n", Status);
  1533. goto Cleanup;
  1534. }
  1535. //
  1536. // allocate a buffer that'd be large enough -- or use a local buffer
  1537. //
  1538. Status = RtlAssignUnicodeStringBuffer(&KeyNameBuffer, &KeyPathUser);
  1539. if (!NT_SUCCESS(Status)) {
  1540. DbgPrint("BasepCheckCacheExcludeList: failed to copy hkcu path status 0x%lx\n", Status);
  1541. goto Cleanup;
  1542. }
  1543. Status = RtlAppendUnicodeStringBuffer(&KeyNameBuffer, &AppcompatKeyPathLayers);
  1544. if (!NT_SUCCESS(Status)) {
  1545. DbgPrint("BasepCheckCacheExcludeList: failed to copy layers path status 0x%lx\n", Status);
  1546. goto Cleanup;
  1547. }
  1548. // we have a string for the key path
  1549. InitializeObjectAttributes(&ObjectAttributes,
  1550. &KeyNameBuffer.String,
  1551. OBJ_CASE_INSENSITIVE,
  1552. NULL,
  1553. NULL);
  1554. Status = NtOpenKey(&KeyHandle,
  1555. KEY_READ, // note - read access only
  1556. &ObjectAttributes);
  1557. if (!NT_SUCCESS(Status)) {
  1558. bCacheAllowed = (STATUS_OBJECT_NAME_NOT_FOUND == Status);
  1559. goto Cleanup;
  1560. }
  1561. //
  1562. // now create value name
  1563. //
  1564. RtlInitUnicodeString(&ExePathNt, pwszPath);
  1565. Status = RtlAssignUnicodeStringBuffer(&ExePathBuffer, &ExePathNt);
  1566. if (!NT_SUCCESS(Status)) {
  1567. DbgPrint("BasepCheckCacheExcludeList: failed to acquire sufficient buffer size for path %ls status 0x%lx\n", pwszPath, Status);
  1568. goto Cleanup;
  1569. }
  1570. Status = RtlNtPathNameToDosPathName(0, &ExePathBuffer, NULL, NULL);
  1571. if (!NT_SUCCESS(Status)) {
  1572. DbgPrint("BasepCheckCacheExcludeList: failed to convert nt path name %ls to dos path name status 0x%lx\n", pwszPath, Status);
  1573. goto Cleanup;
  1574. }
  1575. // now we shall query the value
  1576. Status = NtQueryValueKey(KeyHandle,
  1577. &ExePathBuffer.String,
  1578. KeyValuePartialInformation,
  1579. &KeyValueInformation,
  1580. sizeof(KeyValueInformation),
  1581. &ResultLength);
  1582. bCacheAllowed = (Status == STATUS_OBJECT_NAME_NOT_FOUND); // does not exist is more like it
  1583. Cleanup:
  1584. if (KeyHandle) {
  1585. NtClose(KeyHandle);
  1586. }
  1587. RtlFreeUnicodeString(&KeyPathUser);
  1588. RtlFreeUnicodeStringBuffer(&ExePathBuffer);
  1589. RtlFreeUnicodeStringBuffer(&KeyNameBuffer);
  1590. if (!bCacheAllowed) {
  1591. DbgPrint("BasepCheckCacheExcludeList: Cache not allowed for %ls\n", pwszPath);
  1592. }
  1593. return bCacheAllowed;
  1594. }
  1595. #undef DbgPrint
  1596. VOID
  1597. WINAPI
  1598. BaseDumpAppcompatCache(
  1599. VOID
  1600. )
  1601. {
  1602. PSHIMCACHEHEADER pHeader = NULL;
  1603. PSHIMCACHEENTRY pEntries = NULL;
  1604. INT i;
  1605. INT iEntry;
  1606. PSHIMCACHEENTRY pEntry;
  1607. if (!BasepShimCacheLock(&pHeader, &pEntries)) {
  1608. DbgPrint("Can't get ShimCacheLock\n");
  1609. return;
  1610. }
  1611. DbgPrint("---------------------------------------------\n");
  1612. DbgPrint("Total Entries = 0x%x\n", pHeader->dwCount);
  1613. if (pHeader->dwCount) { // " 01. 0x12345678 0x12345678 0x12345678 "
  1614. DbgPrint("(LRU) (Exe Name) (FileSize)\n");
  1615. }
  1616. for (i = 0; i < (int)pHeader->dwCount; ++i) {
  1617. iEntry = pHeader->rgIndex[i];
  1618. pEntry = pEntries + iEntry;
  1619. DbgPrint(" %2d. \"%ls\" %ld\n",
  1620. i + 1,
  1621. pEntry->wszPath,
  1622. (DWORD)pEntry->FileSize);
  1623. }
  1624. DbgPrint("---------------------------------------------\n");
  1625. BasepShimCacheUnlock();
  1626. }