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.

843 lines
21 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. cache.c
  5. Abstract:
  6. Implements a cache mechanism to speed up OpenRegKeyStr.
  7. Author:
  8. Jim Schmidt (jimschm) 11-Sep-2000
  9. Revisions:
  10. <alias> <date> <comments>
  11. --*/
  12. #include "pch.h"
  13. #include "regp.h"
  14. #define DBG_REG "Reg"
  15. typedef struct {
  16. HKEY Key;
  17. BOOL Unicode;
  18. UINT RefCount;
  19. UINT ClosesNeeded;
  20. REGSAM Sam;
  21. UINT KeyStringBytes;
  22. BYTE KeyString[];
  23. } REGKEYCACHE, *PREGKEYCACHE;
  24. typedef struct {
  25. HKEY Key;
  26. BOOL Unicode;
  27. UINT RefCount;
  28. UINT ClosesNeeded;
  29. REGSAM Sam;
  30. UINT KeyStringBytes;
  31. CHAR KeyString[MAX_REGISTRY_KEY];
  32. } WORKITEMA, *PWORKITEMA;
  33. typedef struct {
  34. HKEY Key;
  35. BOOL Unicode;
  36. UINT RefCount;
  37. UINT ClosesNeeded;
  38. REGSAM Sam;
  39. UINT KeyStringBytes;
  40. WCHAR KeyString[MAX_REGISTRY_KEY];
  41. } WORKITEMW, *PWORKITEMW;
  42. GROWLIST g_KeyCache = INIT_GROWLIST;
  43. UINT g_MaxCacheCount = 0;
  44. UINT g_CacheAddPos;
  45. CHAR g_LastParent[MAX_REGISTRY_KEY];
  46. UINT g_LastParentBytes;
  47. UINT g_LastParentUse;
  48. WCHAR g_LastParentW[MAX_REGISTRY_KEY];
  49. UINT g_LastParentBytesW;
  50. UINT g_LastParentUseW;
  51. BOOL
  52. pRemoveItemFromCache (
  53. IN UINT Item
  54. );
  55. //
  56. // Implementation
  57. //
  58. VOID
  59. RegInitializeCache (
  60. IN UINT InitialCacheSize
  61. )
  62. {
  63. if (InitialCacheSize > 64) {
  64. InitialCacheSize = 64;
  65. }
  66. g_MaxCacheCount = InitialCacheSize;
  67. g_CacheAddPos = 0;
  68. g_LastParentUse = 0;
  69. g_LastParentUseW = 0;
  70. }
  71. VOID
  72. RegTerminateCache (
  73. VOID
  74. )
  75. {
  76. UINT u;
  77. UINT count;
  78. count = GlGetSize (&g_KeyCache);
  79. for (u = 0 ; u < count ; u++) {
  80. pRemoveItemFromCache (u);
  81. }
  82. GlFree (&g_KeyCache);
  83. g_MaxCacheCount = 0;
  84. g_LastParentUse = 0;
  85. g_LastParentUseW = 0;
  86. }
  87. BOOL
  88. pRemoveItemFromCache (
  89. IN UINT Item
  90. )
  91. {
  92. PREGKEYCACHE cacheItem;
  93. // this function will always succeed.
  94. // It will close the opened key even
  95. // if refcount is not zero (but assert that).
  96. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, Item);
  97. if (!cacheItem) {
  98. return TRUE;
  99. }
  100. MYASSERT (!cacheItem->RefCount);
  101. while (cacheItem->ClosesNeeded) {
  102. CloseRegKeyWorker (cacheItem->Key);
  103. cacheItem->ClosesNeeded--;
  104. }
  105. ZeroMemory (cacheItem, sizeof (REGKEYCACHE));
  106. return TRUE;
  107. }
  108. VOID
  109. RegRecordParentInCacheA (
  110. IN PCSTR KeyString,
  111. IN PCSTR StringEnd
  112. )
  113. {
  114. HKEY key;
  115. UINT byteCount;
  116. CHAR lowerStr[MAX_REGISTRY_KEY];
  117. HKEY rootKey;
  118. UINT end;
  119. if (!g_MaxCacheCount) {
  120. return;
  121. }
  122. if (StringEnd <= KeyString) {
  123. return;
  124. }
  125. byteCount = (UINT) (HALF_PTR) ((PBYTE) StringEnd - (PBYTE) KeyString);
  126. if (byteCount >= (MAX_REGISTRY_KEY * sizeof (CHAR))) {
  127. return;
  128. }
  129. if (g_LastParentUse && g_LastParentUse < 3 && g_LastParentBytes == byteCount) {
  130. StringCopyABA (lowerStr, KeyString, StringEnd);
  131. CharLowerA (lowerStr);
  132. if (StringMatchA (g_LastParent, lowerStr)) {
  133. g_LastParentUse++;
  134. if (g_LastParentUse == 3) {
  135. //
  136. // Stimulate the cache
  137. //
  138. rootKey = ConvertRootStringToKeyA (lowerStr, &end);
  139. if (rootKey) {
  140. if (lowerStr[end]) {
  141. #ifdef DEBUG
  142. key = OpenRegKeyWorkerA (rootKey, &lowerStr[end], __FILE__, __LINE__);
  143. #else
  144. key = OpenRegKeyWorkerA (rootKey, &lowerStr[end]);
  145. #endif
  146. if (key) {
  147. RegAddKeyToCacheA (lowerStr, key, g_OpenSam);
  148. CloseRegKey (key);
  149. }
  150. }
  151. }
  152. }
  153. return;
  154. }
  155. }
  156. StringCopyABA (g_LastParent, KeyString, StringEnd);
  157. CharLowerA (g_LastParent);
  158. g_LastParentBytes = byteCount;
  159. g_LastParentUse = 1;
  160. }
  161. HKEY
  162. RegGetKeyFromCacheA (
  163. IN PCSTR KeyString,
  164. IN PCSTR KeyEnd, OPTIONAL
  165. IN REGSAM Sam,
  166. IN BOOL IncRefCount
  167. )
  168. {
  169. UINT stringBytes;
  170. PCSTR end;
  171. CHAR lowerStr[MAX_REGISTRY_KEY];
  172. UINT u;
  173. UINT count;
  174. PREGKEYCACHE cacheItem;
  175. // The cache holds all open keys.
  176. count = GlGetSize (&g_KeyCache);
  177. if (!count) {
  178. return NULL;
  179. }
  180. if (!KeyEnd) {
  181. end = StackStringCopyA (lowerStr, KeyString);
  182. } else {
  183. if (KeyEnd > KeyString + MAX_REGISTRY_KEY) {
  184. KeyEnd = KeyString + MAX_REGISTRY_KEY;
  185. }
  186. end = StringCopyABA (lowerStr, KeyString, KeyEnd);
  187. }
  188. CharLowerA (lowerStr);
  189. stringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) lowerStr);
  190. //
  191. // Scan the cache sequentially (it should be small), and return a match
  192. // if one is found. Stored strings are always in lower case.
  193. //
  194. u = g_CacheAddPos;
  195. do {
  196. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, u);
  197. if (!cacheItem) {
  198. return NULL;
  199. }
  200. if ((cacheItem->KeyStringBytes == stringBytes) &&
  201. (!cacheItem->Unicode) &&
  202. ((cacheItem->Sam & Sam) == Sam) &&
  203. (StringMatchA ((PCSTR) cacheItem->KeyString, lowerStr))
  204. ) {
  205. if (IncRefCount) {
  206. cacheItem->RefCount++;
  207. }
  208. return cacheItem->Key;
  209. }
  210. u++;
  211. if (u >= count) {
  212. u = 0;
  213. }
  214. } while (u != g_CacheAddPos);
  215. return NULL;
  216. }
  217. VOID
  218. RegAddKeyToCacheA (
  219. IN PCSTR KeyString,
  220. IN HKEY Key,
  221. IN REGSAM Sam
  222. )
  223. {
  224. PREGKEYCACHE cacheItem;
  225. PREGKEYCACHE lastAddItem;
  226. WORKITEMA workItem;
  227. PCSTR end;
  228. UINT minStructSize;
  229. UINT pos;
  230. UINT count;
  231. UINT u;
  232. // The cache holds all open keys.
  233. if (!g_MaxCacheCount || !Key) {
  234. return;
  235. }
  236. //
  237. // Scan the cache for the existing Key
  238. //
  239. count = GlGetSize (&g_KeyCache);
  240. for (u = 0 ; u < count ; u++) {
  241. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, u);
  242. if (cacheItem->Key == Key) {
  243. cacheItem->RefCount++;
  244. g_CacheAddPos = u;
  245. cacheItem->Sam |= Sam;
  246. if (cacheItem->KeyStringBytes == 0 && *KeyString && !cacheItem->Unicode) {
  247. //
  248. // This key was added before we knew the name. Update the name
  249. // now.
  250. //
  251. DEBUGMSG ((DBG_REG, "Updating empty-named key %s", KeyString));
  252. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString);
  253. CopyMemory (&workItem, cacheItem, minStructSize);
  254. end = StackStringCopyA (workItem.KeyString, KeyString);
  255. CharLowerA (workItem.KeyString);
  256. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  257. workItem.ClosesNeeded++;
  258. minStructSize += workItem.KeyStringBytes + sizeof (CHAR);
  259. GlSetItem (&g_KeyCache, u, (PBYTE) &workItem, minStructSize);
  260. } else if (*KeyString == 0) {
  261. cacheItem->ClosesNeeded++;
  262. }
  263. return;
  264. }
  265. }
  266. //
  267. // Create the new cache item
  268. //
  269. workItem.Key = Key;
  270. workItem.Unicode = FALSE;
  271. workItem.RefCount = 1;
  272. workItem.ClosesNeeded = 1;
  273. workItem.Sam = Sam;
  274. end = StackStringCopyA (workItem.KeyString, KeyString);
  275. CharLowerA (workItem.KeyString);
  276. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  277. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString) + workItem.KeyStringBytes + sizeof (CHAR);
  278. //
  279. // Put work item into grow list
  280. //
  281. if (count < g_MaxCacheCount) {
  282. g_CacheAddPos = count;
  283. GlAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  284. } else {
  285. //
  286. // Look for a closed key to discard. If cache is too full, then
  287. // increase the cache size. If the cache size hits 64, then don't
  288. // cache this add.
  289. //
  290. lastAddItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, g_CacheAddPos);
  291. if (lastAddItem) {
  292. for (pos = 0 ; pos < count ; pos++) {
  293. if (pos == g_CacheAddPos) {
  294. continue;
  295. }
  296. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, pos);
  297. // never discard items that are currently referenced
  298. if (cacheItem->RefCount) {
  299. continue;
  300. }
  301. // this is an optimization that has to do with how enumeration usually works.
  302. // If we have an item that's shorter that one that's in the cache already,
  303. // it's not very likely that we are going to go back to the one in the cache
  304. // so we just remove it.
  305. if (cacheItem->KeyStringBytes >= lastAddItem->KeyStringBytes) {
  306. break;
  307. }
  308. if (cacheItem->Unicode) {
  309. continue;
  310. }
  311. if (!StringPrefixA ((PCSTR) lastAddItem->KeyString, (PCSTR) cacheItem->KeyString)) {
  312. break;
  313. }
  314. }
  315. if (pos == count) {
  316. if (g_MaxCacheCount == 64) {
  317. DEBUGMSG ((DBG_REG, "Cache is full of open keys"));
  318. return;
  319. }
  320. g_MaxCacheCount++;
  321. GlAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  322. } else {
  323. pRemoveItemFromCache (pos);
  324. GlSetItem (&g_KeyCache, pos, (PBYTE) &workItem, minStructSize);
  325. }
  326. g_CacheAddPos = pos;
  327. }
  328. }
  329. }
  330. BOOL
  331. RegDecrementRefCount (
  332. IN HKEY Key
  333. )
  334. {
  335. UINT u;
  336. UINT count;
  337. PREGKEYCACHE cacheItem;
  338. // The cache holds all open keys.
  339. if (!g_MaxCacheCount) {
  340. return FALSE;
  341. }
  342. count = GlGetSize (&g_KeyCache);
  343. for (u = 0 ; u < count ; u++) {
  344. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, u);
  345. if (cacheItem->Key == Key) {
  346. if (cacheItem->RefCount == 0) {
  347. //
  348. // The caller is tried to close the key more times than what
  349. // it was opened.
  350. //
  351. if (cacheItem->Unicode) {
  352. DEBUGMSGW ((
  353. DBG_WHOOPS,
  354. "Reg key %s ref count == 0; trying to close it too many times",
  355. cacheItem->KeyString
  356. ));
  357. } else {
  358. DEBUGMSGA ((
  359. DBG_WHOOPS,
  360. "Reg key %s ref count == 0; trying to close it too many times",
  361. cacheItem->KeyString
  362. ));
  363. }
  364. } else {
  365. cacheItem->RefCount--;
  366. }
  367. // One more check. If the refcount is zero and this
  368. // is an item with an empty key string, let's get rid
  369. // of it.
  370. if ((cacheItem->RefCount == 0) &&
  371. (cacheItem->KeyStringBytes == 0)
  372. ) {
  373. pRemoveItemFromCache (u);
  374. }
  375. //
  376. // Return TRUE to either postpone the close
  377. // or to avoid the close because pRemoveItemFromCache did
  378. // it already
  379. //
  380. return TRUE;
  381. }
  382. }
  383. return FALSE;
  384. }
  385. VOID
  386. RegRecordParentInCacheW (
  387. IN PCWSTR KeyString,
  388. IN PCWSTR StringEnd
  389. )
  390. {
  391. HKEY key;
  392. UINT byteCount;
  393. WCHAR lowerStr[MAX_REGISTRY_KEY];
  394. HKEY rootKey;
  395. UINT end;
  396. if (!g_MaxCacheCount) {
  397. return;
  398. }
  399. if (StringEnd <= KeyString) {
  400. return;
  401. }
  402. byteCount = (UINT) (HALF_PTR) ((PBYTE) StringEnd - (PBYTE) KeyString);
  403. if (byteCount >= (MAX_REGISTRY_KEY * sizeof (WCHAR))) {
  404. return;
  405. }
  406. if (g_LastParentUseW && g_LastParentUseW < 3 && g_LastParentBytesW == byteCount) {
  407. StringCopyABW (lowerStr, KeyString, StringEnd);
  408. CharLowerW (lowerStr);
  409. if (StringMatchW (g_LastParentW, lowerStr)) {
  410. g_LastParentUseW++;
  411. if (g_LastParentUseW == 3) {
  412. //
  413. // Stimulate the cache
  414. //
  415. rootKey = ConvertRootStringToKeyW (lowerStr, &end);
  416. if (rootKey) {
  417. if (lowerStr[end]) {
  418. #ifdef DEBUG
  419. key = OpenRegKeyWorkerW (rootKey, &lowerStr[end], __FILE__, __LINE__);
  420. #else
  421. key = OpenRegKeyWorkerW (rootKey, &lowerStr[end]);
  422. #endif
  423. if (key) {
  424. RegAddKeyToCacheW (lowerStr, key, g_OpenSam);
  425. CloseRegKey (key);
  426. }
  427. }
  428. }
  429. }
  430. return;
  431. }
  432. }
  433. StringCopyABW (g_LastParentW, KeyString, StringEnd);
  434. CharLowerW (g_LastParentW);
  435. g_LastParentBytesW = byteCount;
  436. g_LastParentUseW = 1;
  437. }
  438. HKEY
  439. RegGetKeyFromCacheW (
  440. IN PCWSTR KeyString,
  441. IN PCWSTR KeyEnd, OPTIONAL
  442. IN REGSAM Sam,
  443. IN BOOL IncRefCount
  444. )
  445. {
  446. UINT stringBytes;
  447. PCWSTR end;
  448. WCHAR lowerStr[MAX_REGISTRY_KEY];
  449. UINT u;
  450. UINT count;
  451. PREGKEYCACHE cacheItem;
  452. // The cache holds all open keys.
  453. count = GlGetSize (&g_KeyCache);
  454. if (!count) {
  455. return NULL;
  456. }
  457. if (!KeyEnd) {
  458. end = StackStringCopyW (lowerStr, KeyString);
  459. } else {
  460. if (KeyEnd > KeyString + MAX_REGISTRY_KEY) {
  461. KeyEnd = KeyString + MAX_REGISTRY_KEY;
  462. }
  463. end = StringCopyABW (lowerStr, KeyString, KeyEnd);
  464. }
  465. CharLowerW (lowerStr);
  466. stringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) lowerStr);
  467. //
  468. // Scan the cache sequentially (it should be small), and return a match
  469. // if one is found. Stored strings are always in lower case.
  470. //
  471. u = g_CacheAddPos;
  472. do {
  473. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, u);
  474. if (!cacheItem) {
  475. return NULL;
  476. }
  477. if ((cacheItem->KeyStringBytes == stringBytes) &&
  478. (cacheItem->Unicode) &&
  479. ((cacheItem->Sam & Sam) == Sam) &&
  480. (StringMatchW ((PCWSTR) cacheItem->KeyString, lowerStr))
  481. ) {
  482. if (IncRefCount) {
  483. cacheItem->RefCount++;
  484. }
  485. return cacheItem->Key;
  486. }
  487. u++;
  488. if (u >= count) {
  489. u = 0;
  490. }
  491. } while (u != g_CacheAddPos);
  492. return NULL;
  493. }
  494. VOID
  495. RegAddKeyToCacheW (
  496. IN PCWSTR KeyString,
  497. IN HKEY Key,
  498. IN REGSAM Sam
  499. )
  500. {
  501. PREGKEYCACHE cacheItem;
  502. PREGKEYCACHE lastAddItem;
  503. WORKITEMW workItem;
  504. PCWSTR end;
  505. UINT minStructSize;
  506. UINT pos;
  507. UINT count;
  508. UINT u;
  509. // The cache holds all open keys.
  510. if (!g_MaxCacheCount || !Key) {
  511. return;
  512. }
  513. //
  514. // Scan the cache for the existing Key
  515. //
  516. count = GlGetSize (&g_KeyCache);
  517. for (u = 0 ; u < count ; u++) {
  518. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, u);
  519. if (cacheItem->Key == Key) {
  520. cacheItem->RefCount++;
  521. g_CacheAddPos = u;
  522. cacheItem->Sam |= Sam;
  523. if (cacheItem->KeyStringBytes == 0 && *KeyString && cacheItem->Unicode) {
  524. //
  525. // This key was added before we knew the name. Update the name
  526. // now.
  527. //
  528. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString);
  529. CopyMemory (&workItem, cacheItem, minStructSize);
  530. end = StackStringCopyW (workItem.KeyString, KeyString);
  531. CharLowerW (workItem.KeyString);
  532. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  533. workItem.ClosesNeeded++;
  534. minStructSize += workItem.KeyStringBytes + sizeof (WCHAR);
  535. GlSetItem (&g_KeyCache, u, (PBYTE) &workItem, minStructSize);
  536. } else if (*KeyString == 0) {
  537. cacheItem->ClosesNeeded++;
  538. }
  539. return;
  540. }
  541. }
  542. //
  543. // Create the new cache item
  544. //
  545. workItem.Key = Key;
  546. workItem.Unicode = TRUE;
  547. workItem.RefCount = 1;
  548. workItem.ClosesNeeded = 1;
  549. workItem.Sam = Sam;
  550. end = StackStringCopyW (workItem.KeyString, KeyString);
  551. CharLowerW (workItem.KeyString);
  552. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  553. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString) + workItem.KeyStringBytes + sizeof (WCHAR);
  554. //
  555. // Put work item into grow list
  556. //
  557. if (count < g_MaxCacheCount) {
  558. g_CacheAddPos = count;
  559. GlAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  560. } else {
  561. //
  562. // Look for a closed key to discard. If cache is too full, then
  563. // increase the cache size. If the cache size hits 64, then don't
  564. // cache this add.
  565. //
  566. lastAddItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, g_CacheAddPos);
  567. if (lastAddItem) {
  568. for (pos = 0 ; pos < count ; pos++) {
  569. if (pos == g_CacheAddPos) {
  570. continue;
  571. }
  572. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, pos);
  573. // never discard items that are currently referenced
  574. if (cacheItem->RefCount) {
  575. continue;
  576. }
  577. // this is an optimization that has to do with how enumeration usually works.
  578. // If we have an item that's shorter that one that's in the cache already,
  579. // it's not very likely that we are going to go back to the one in the cache
  580. // so we just remove it.
  581. if (cacheItem->KeyStringBytes >= lastAddItem->KeyStringBytes) {
  582. break;
  583. }
  584. if (!cacheItem->Unicode) {
  585. continue;
  586. }
  587. if (!StringPrefixW ((PCWSTR) lastAddItem->KeyString, (PCWSTR) cacheItem->KeyString)) {
  588. break;
  589. }
  590. }
  591. if (pos == count) {
  592. if (g_MaxCacheCount == 64) {
  593. DEBUGMSG ((DBG_REG, "Cache is full of open keys"));
  594. return;
  595. }
  596. g_MaxCacheCount++;
  597. GlAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  598. } else {
  599. pRemoveItemFromCache (pos);
  600. GlSetItem (&g_KeyCache, pos, (PBYTE) &workItem, minStructSize);
  601. }
  602. g_CacheAddPos = pos;
  603. }
  604. }
  605. }
  606. VOID
  607. RegRemoveItemFromCacheA (
  608. IN PCSTR RegKey
  609. )
  610. {
  611. PREGKEYCACHE cacheItem;
  612. UINT pos;
  613. UINT count;
  614. count = GlGetSize (&g_KeyCache);
  615. for (pos = 0 ; pos < count ; pos++) {
  616. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, pos);
  617. if (StringIMatchA ((PCSTR) cacheItem->KeyString, RegKey)) {
  618. // we are forced to remove this, regardless of the refcount
  619. cacheItem->RefCount = 0;
  620. pRemoveItemFromCache (pos);
  621. // we need to keep going since different security
  622. // access masks would have generated different
  623. // items in the cache
  624. }
  625. }
  626. }
  627. VOID
  628. RegRemoveItemFromCacheW (
  629. IN PCWSTR RegKey
  630. )
  631. {
  632. PREGKEYCACHE cacheItem;
  633. UINT pos;
  634. UINT count;
  635. count = GlGetSize (&g_KeyCache);
  636. for (pos = 0 ; pos < count ; pos++) {
  637. cacheItem = (PREGKEYCACHE) GlGetItem (&g_KeyCache, pos);
  638. if (StringIMatchW ((PCWSTR) cacheItem->KeyString, RegKey)) {
  639. // we are forced to remove this, regardless of the refcount
  640. cacheItem->RefCount = 0;
  641. pRemoveItemFromCache (pos);
  642. // we need to keep going since different security
  643. // access masks would have generated different
  644. // items in the cache
  645. }
  646. }
  647. }