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.

2892 lines
76 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. RegeCls.c
  5. Abstract:
  6. This module contains helper functions for enumerating
  7. class registrations via the win32 RegEnumKeyEx api
  8. Author:
  9. Adam Edwards (adamed) 06-May-1998
  10. Key Functions:
  11. EnumTableGetNextEnum
  12. EnumTableRemoveKey
  13. InitializeClassesEnumTable
  14. ClassKeyCountSubKeys
  15. Notes:
  16. Starting with NT5, the HKEY_CLASSES_ROOT key is per-user
  17. instead of per-machine -- previously, HKCR was an alias for
  18. HKLM\Software\Classes. Please see regclass.c for more information
  19. on this functionality.
  20. This feature complicates registry key enumeration because certain keys,
  21. such as CLSID, can have some subkeys that come from HKLM\Software\Classes, and
  22. other subkeys that come from HKCU\Software\Classes. Since the feature is
  23. implemented in user mode, the kernel mode apis know nothing of this. When it's
  24. time to enumerate keys, the kernel doesn't know that it should enumerate keys from
  25. two different parent keys.
  26. The key problem is that keys with the same name can exist in the user and machine portions.
  27. When this happens, we choose the user portion is belonging to HKCR -- the other
  28. one does not exist -- it is "overridden" by the user version. This means that
  29. we cannot simply enumerate from both places and return the results -- we would
  30. get duplicates in this case. Thus, we have to do work in user mode to make
  31. sure duplicates are not returned.
  32. This module provides the user mode implementation for enumerating class
  33. registration keys in HKEY_CLASSES_ROOT.
  34. The general method is to maintain state between each call to RegEnumKeyEx. The
  35. state is kept in a global table indexed by registry key handle and thread id. The
  36. state allows the api to remember where it is in the enumeration. The rest of the code
  37. handles finding the next key, which is accomplished by retrieving keys from both user
  38. and machine locations. Since the kernel returns keys from either of these locations in
  39. sorted order, we can compare the key names and return whichever one is less or greater,
  40. depending on if we're enumerating upward or downward. We keep track of where
  41. we are for both user and machine locations, so we know which key to enumerate
  42. next and when to stop.
  43. **************************
  44. IMPORTANT ASSUMPTIONS:
  45. **************************
  46. This code assumes that the caller has both query permission and enumerate subkey
  47. permission in the registry key's acl -- some calls may fail with access denied if the
  48. acl denies access to the caller.
  49. --*/
  50. #ifdef LOCAL
  51. #include <rpc.h>
  52. #include "regrpc.h"
  53. #include "localreg.h"
  54. #include "regclass.h"
  55. #include "regecls.h"
  56. #include <malloc.h>
  57. NTSTATUS QueryKeyInfo(
  58. HKEY hKey,
  59. KEY_INFORMATION_CLASS KeyInformationClass,
  60. PVOID *ppKeyInfo,
  61. ULONG BufferLength,
  62. BOOL fClass,
  63. USHORT MaxClassLength);
  64. //
  65. // Global table of registry key enumeration state. This is initialized
  66. // at dll initialize time.
  67. //
  68. EnumTable gClassesEnumTable;
  69. //
  70. // Global indicating need for calling thread detach routines
  71. //
  72. BOOL gbDllHasThreadState = FALSE;
  73. BOOL InitializeClassesEnumTable()
  74. /*++
  75. Routine Description:
  76. Initializes the global classes enumeration table when
  77. advapi32.dll is initialized.
  78. Arguments:
  79. Return Value:
  80. Returns TRUE for success, FALSE for failure
  81. Notes:
  82. This recordset merging is all in user mode --
  83. should be moved to the kernel for perf and other reasons ??
  84. --*/
  85. {
  86. NTSTATUS Status;
  87. //
  88. // Init the classes enumeration table
  89. //
  90. Status = EnumTableInit(&gClassesEnumTable);
  91. return NT_SUCCESS(Status);
  92. }
  93. BOOL CleanupClassesEnumTable(BOOL fThisThreadOnly)
  94. /*++
  95. Routine Description:
  96. Uninitializes the global classes enumeration table when
  97. advapi32.dll is unloaded -- this frees all
  98. heap associated with the enumeration table, including
  99. that for keys which have not been closed. Other resources
  100. required for the table are also freed.
  101. Arguments:
  102. dwCriteria - if this is ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD,
  103. then only the table entries concerning this thread are cleaned up.
  104. If it is ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD, the table entries
  105. for all threads in the process are cleaned up.
  106. Return Value:
  107. TRUE for success, FALSE otherwise.
  108. Notes:
  109. --*/
  110. {
  111. NTSTATUS Status;
  112. DWORD dwCriteria;
  113. dwCriteria = fThisThreadOnly ? ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD :
  114. ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD;
  115. //
  116. // Clear our enumeration table
  117. //
  118. Status = EnumTableClear(&gClassesEnumTable, dwCriteria);
  119. return NT_SUCCESS(Status);
  120. }
  121. NTSTATUS EnumTableInit(EnumTable* pEnumTable)
  122. /*++
  123. Routine Description:
  124. Initializes an enumeration state table
  125. Arguments:
  126. pEnumTable - table to initialize
  127. Return Value:
  128. Returns NT_SUCCESS (0) for success; error-code for failure.
  129. Notes:
  130. --*/
  131. {
  132. NTSTATUS Status;
  133. EnumState* rgNewState;
  134. #if defined(_REGCLASS_ENUMTABLE_INSTRUMENTED_)
  135. DbgPrint("WINREG: Instrumented enum table data for process id 0x%x\n", NtCurrentTeb()->ClientId.UniqueProcess);
  136. DbgPrint("WINREG: EnumTableInit subtree state size %d\n", sizeof(rgNewState->UserState));
  137. DbgPrint("WINREG: EnumTableInit state size %d\n", sizeof(*rgNewState));
  138. DbgPrint("WINREG: EnumTableInit initial table size %d\n", sizeof(*pEnumTable));
  139. #endif // _REGCLASS_ENUMTABLE_INSTRUMENTED_
  140. //
  141. // Initialize the thread list
  142. //
  143. StateObjectListInit(
  144. &(pEnumTable->ThreadEnumList),
  145. 0);
  146. //
  147. // We have not initialized the critical section
  148. // for this table yet -- remember this.
  149. //
  150. pEnumTable->bCriticalSectionInitialized = FALSE;
  151. //
  152. // Initialize the critical section that will be used to
  153. // synchronize access to this table
  154. //
  155. Status = RtlInitializeCriticalSection(
  156. &(pEnumTable->CriticalSection));
  157. //
  158. // Remember that we have initialized this critical section
  159. // so we can remember to delete it.
  160. //
  161. pEnumTable->bCriticalSectionInitialized = NT_SUCCESS(Status);
  162. return Status;
  163. }
  164. NTSTATUS EnumTableClear(EnumTable* pEnumTable, DWORD dwCriteria)
  165. /*++
  166. Routine Description:
  167. Clears all state in an enumeration table --
  168. frees all state (memory, resources) memory associated
  169. with the enumeration table.
  170. Arguments:
  171. pEnumTable - table to clear
  172. dwCriteria - if this is ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD,
  173. enumeration states are removed for this thread only.
  174. If it is ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD, enumeration
  175. states are removed for all threads in the process.
  176. Return Value:
  177. none
  178. Notes:
  179. --*/
  180. {
  181. NTSTATUS Status;
  182. BOOL fThisThreadOnly;
  183. DWORD dwThreadId;
  184. #if defined(_REGCLASS_ENUMTABLE_INSTRUMENTED_)
  185. DWORD cOrphanedStates = 0;
  186. #endif // _REGCLASS_ENUMTABLE_INSTRUMENTED_
  187. ASSERT((ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD == dwCriteria) ||
  188. (ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD == dwCriteria));
  189. Status = STATUS_SUCCESS;
  190. //
  191. // we assume that if we are called with ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD
  192. // that we are being called at process detach to remove all keys from the
  193. // table and free the table itself -- this means that we are the only
  194. // thread executing this code.
  195. //
  196. //
  197. // Protect ourselves while modifying the table
  198. //
  199. if (dwCriteria != ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD) {
  200. Status = RtlEnterCriticalSection(&(pEnumTable->CriticalSection));
  201. ASSERT( NT_SUCCESS( Status ) );
  202. if ( !NT_SUCCESS( Status ) ) {
  203. #if DBG
  204. DbgPrint( "WINREG: RtlEnterCriticalSection() in EnumTableRemoveKey() failed. Status = %lx \n", Status );
  205. #endif
  206. return Status;
  207. }
  208. }
  209. fThisThreadOnly = (ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD == dwCriteria);
  210. //
  211. // Find our thread id if the caller wants to remove
  212. // state for just this thread
  213. //
  214. if (fThisThreadOnly) {
  215. KeyStateList* pStateList;
  216. dwThreadId = GetCurrentThreadId();
  217. pStateList = (KeyStateList*) StateObjectListRemove(
  218. &(pEnumTable->ThreadEnumList),
  219. ULongToPtr((const unsigned long)dwThreadId));
  220. //
  221. // Announce that this dll no longer stores state for any
  222. // threads -- used to avoid calls to dll thread
  223. // detach routines when there's no state to clean up.
  224. //
  225. if (StateObjectListIsEmpty(&(pEnumTable->ThreadEnumList))) {
  226. gbDllHasThreadState = FALSE;
  227. }
  228. if (pStateList) {
  229. KeyStateListDestroy((StateObject*) pStateList);
  230. }
  231. } else {
  232. //
  233. // If we're clearing all threads, just destroy this list
  234. //
  235. StateObjectListClear(&(pEnumTable->ThreadEnumList),
  236. KeyStateListDestroy);
  237. gbDllHasThreadState = FALSE;
  238. }
  239. //
  240. // It's safe to unlock the table
  241. //
  242. if (dwCriteria != ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD) {
  243. Status = RtlLeaveCriticalSection(&(pEnumTable->CriticalSection));
  244. ASSERT( NT_SUCCESS( Status ) );
  245. #if DBG
  246. if ( !NT_SUCCESS( Status ) ) {
  247. DbgPrint( "WINREG: RtlLeaveCriticalSection() in EnumTableClear() failed. Status = %lx \n", Status );
  248. }
  249. #endif
  250. }
  251. if (pEnumTable->bCriticalSectionInitialized && !fThisThreadOnly) {
  252. Status = RtlDeleteCriticalSection(&(pEnumTable->CriticalSection));
  253. ASSERT(NT_SUCCESS(Status));
  254. #if DBG
  255. if ( !NT_SUCCESS( Status ) ) {
  256. DbgPrint( "WINREG: RtlDeleteCriticalSection() in EnumTableClear() failed. Status = %lx \n", Status );
  257. }
  258. #endif
  259. }
  260. #if defined(_REGCLASS_ENUMTABLE_INSTRUMENTED_)
  261. if (!fThisThreadOnly) {
  262. DbgPrint("WINREG: EnumTableClear() deleted %d unfreed states.\n", cOrphanedStates);
  263. DbgPrint("WINREG: If the number of unfreed states is > 1, either the\n"
  264. "WINREG: process terminated a thread with TerminateThread, the process\n"
  265. "WINREG: didn't close all registry handles before exiting,\n"
  266. "WINREG: or there's a winreg bug in the classes enumeration code\n");
  267. }
  268. #endif // _REGCLASS_ENUMTABLE_INSTRUMENTED_
  269. return Status;
  270. }
  271. NTSTATUS EnumTableFindKeyState(
  272. EnumTable* pEnumTable,
  273. HKEY hKey,
  274. EnumState** ppEnumState)
  275. /*++
  276. Routine Description:
  277. Searches for the state for a registry key in
  278. an enumeration table
  279. Arguments:
  280. pEnumTable - table in which to search
  281. hKey - key for whose state we're searching
  282. ppEnumState - out param for result of search
  283. Return Value:
  284. Returns NT_SUCCESS (0) for success; error-code for failure.
  285. Notes:
  286. --*/
  287. {
  288. KeyStateList* pStateList;
  289. pStateList = (KeyStateList*) StateObjectListFind(
  290. &(pEnumTable->ThreadEnumList),
  291. ULongToPtr((const unsigned long)GetCurrentThreadId()));
  292. if (!pStateList) {
  293. return STATUS_OBJECT_NAME_NOT_FOUND;
  294. } else {
  295. *ppEnumState = (EnumState*) StateObjectListFind(
  296. (StateObjectList*) pStateList,
  297. hKey);
  298. if (!*ppEnumState) {
  299. return STATUS_OBJECT_NAME_NOT_FOUND;
  300. }
  301. }
  302. return STATUS_SUCCESS;
  303. }
  304. NTSTATUS EnumTableAddKey(
  305. EnumTable* pEnumTable,
  306. HKEY hKey,
  307. DWORD dwFirstSubKey,
  308. EnumState** ppEnumState,
  309. EnumState** ppRootState)
  310. /*++
  311. Routine Description:
  312. Adds an enumeration state to
  313. an enumeration table for a given key.
  314. Arguments:
  315. pEnumTable - table in which to add state
  316. hKey - key for whom we want to add state
  317. dwFirstSubKey - index of first subkey requested by caller
  318. for enumeration
  319. ppEnumState - out param for result of search or add
  320. Return Value:
  321. Returns NT_SUCCESS (0) for success; error-code for failure.
  322. Notes:
  323. --*/
  324. {
  325. EnumState* pEnumState;
  326. KeyStateList* pStateList;
  327. NTSTATUS Status;
  328. pEnumState = NULL;
  329. //
  330. // Announce that this dll has thread state so it will
  331. // be properly cleaned up by dll thread detach routines
  332. //
  333. gbDllHasThreadState = TRUE;
  334. pStateList = (KeyStateList*) StateObjectListFind(
  335. (StateObjectList*) &(pEnumTable->ThreadEnumList),
  336. ULongToPtr((const unsigned long)GetCurrentThreadId()));
  337. if (!pStateList) {
  338. pStateList = RegClassHeapAlloc(sizeof(*pStateList));
  339. if (!pStateList) {
  340. return STATUS_NO_MEMORY;
  341. }
  342. KeyStateListInit(pStateList);
  343. StateObjectListAdd(
  344. &(pEnumTable->ThreadEnumList),
  345. (StateObject*) pStateList);
  346. }
  347. pEnumState = RegClassHeapAlloc(sizeof(*pEnumState));
  348. if (!pEnumState) {
  349. return STATUS_NO_MEMORY;
  350. }
  351. RtlZeroMemory(pEnumState, sizeof(*pEnumState));
  352. {
  353. SKeySemantics keyinfo;
  354. UNICODE_STRING EmptyString = {0, 0, 0};
  355. BYTE rgNameBuf[REG_MAX_CLASSKEY_LEN + REG_CHAR_SIZE + sizeof(KEY_NAME_INFORMATION)];
  356. //
  357. // Set buffer to store info about this key
  358. //
  359. RtlZeroMemory(&keyinfo, sizeof(keyinfo));
  360. keyinfo._pFullPath = (PKEY_NAME_INFORMATION) rgNameBuf;
  361. keyinfo._cbFullPath = sizeof(rgNameBuf);
  362. keyinfo._fAllocedNameBuf = FALSE;
  363. //
  364. // get information about this key
  365. //
  366. Status = BaseRegGetKeySemantics(hKey, &EmptyString, &keyinfo);
  367. if (!NT_SUCCESS(Status)) {
  368. goto error_exit;
  369. }
  370. //
  371. // initialize the empty spot
  372. //
  373. Status = EnumStateInit(
  374. pEnumState,
  375. hKey,
  376. dwFirstSubKey,
  377. dwFirstSubKey ? ENUM_DIRECTION_BACKWARD : ENUM_DIRECTION_FORWARD,
  378. &keyinfo);
  379. BaseRegReleaseKeySemantics(&keyinfo);
  380. if (!NT_SUCCESS(Status)) {
  381. goto error_exit;
  382. }
  383. if (IsRootKey(&keyinfo)) {
  384. NTSTATUS RootStatus;
  385. //
  386. // If this fails, it is not fatal -- it just means
  387. // we may miss out on an optimization. This can
  388. // fail due to out of memory, so it is possible
  389. // that it may fail and we would still want to continue
  390. //
  391. RootStatus = EnumTableGetRootState(pEnumTable, ppRootState);
  392. #if DBG
  393. if (!NT_SUCCESS(RootStatus)) {
  394. DbgPrint( "WINREG: EnumTableAddKey failed to get classes root state. Status = %lx \n", RootStatus );
  395. }
  396. #endif // DBG
  397. if (NT_SUCCESS(RootStatus)) {
  398. RootStatus = EnumStateCopy(
  399. pEnumState,
  400. *ppRootState);
  401. #if DBG
  402. if (!NT_SUCCESS(RootStatus)) {
  403. DbgPrint( "WINREG: EnumTableAddKey failed to copy key state. Status = %lx \n", RootStatus );
  404. }
  405. #endif // DBG
  406. }
  407. }
  408. }
  409. //
  410. // set the out parameter for the caller
  411. //
  412. *ppEnumState = pEnumState;
  413. StateObjectListAdd(
  414. (StateObjectList*) pStateList,
  415. (StateObject*) pEnumState);
  416. Status = STATUS_SUCCESS;
  417. error_exit:
  418. if (!NT_SUCCESS(Status) && pEnumState) {
  419. RegClassHeapFree(pEnumState);
  420. }
  421. return Status;
  422. }
  423. NTSTATUS EnumTableRemoveKey(
  424. EnumTable* pEnumTable,
  425. HKEY hKey,
  426. DWORD dwCriteria)
  427. /*++
  428. Routine Description:
  429. remove an enumeration state from
  430. an enumeration table for a given key.
  431. Arguments:
  432. pEnumTable - table in which to remove state
  433. hKey - key whose state we wish to remove
  434. dwCriteria - if this is ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD,
  435. the enumeration state for hkey is removed for this thread only.
  436. If it is ENUM_TABLE_REMOVEKEY_CRITERIA_ANYTHREAD, the enumeration
  437. state for hkey is removed for all threads in the
  438. process.
  439. Return Value:
  440. Returns NT_SUCCESS (0) for success; error-code for failure.
  441. Notes:
  442. --*/
  443. {
  444. KeyStateList* pStateList;
  445. EnumState* pEnumState;
  446. BOOL fThisThreadOnly;
  447. NTSTATUS Status;
  448. //
  449. // Protect ourselves while modifying the table
  450. //
  451. Status = RtlEnterCriticalSection(&(pEnumTable->CriticalSection));
  452. ASSERT( NT_SUCCESS( Status ) );
  453. if ( !NT_SUCCESS( Status ) ) {
  454. #if DBG
  455. DbgPrint( "WINREG: RtlEnterCriticalSection() in EnumTableRemoveKey() failed. Status = %lx \n", Status );
  456. #endif
  457. return Status;
  458. }
  459. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  460. fThisThreadOnly = (ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD == dwCriteria);
  461. {
  462. KeyStateList* pNext;
  463. pNext = NULL;
  464. for (pStateList = (KeyStateList*) (pEnumTable->ThreadEnumList.pHead);
  465. pStateList != NULL;
  466. pStateList = NULL)
  467. {
  468. EnumState* pEnumState;
  469. if (fThisThreadOnly) {
  470. pStateList = (KeyStateList*) StateObjectListFind(
  471. (StateObjectList*) &(pEnumTable->ThreadEnumList),
  472. ULongToPtr((const unsigned long)GetCurrentThreadId()));
  473. if (!pStateList) {
  474. break;
  475. }
  476. } else {
  477. pNext = (KeyStateList*) (pStateList->Object.Links.Flink);
  478. }
  479. pEnumState = (EnumState*) StateObjectListRemove(
  480. (StateObjectList*) pStateList,
  481. hKey);
  482. if (pEnumState) {
  483. Status = STATUS_SUCCESS;
  484. EnumStateDestroy((StateObject*) pEnumState);
  485. //
  486. // Note the state list might be empty for a given thread,
  487. // but we will not destroy this list in order to avoid
  488. // excessive heap calls
  489. //
  490. }
  491. }
  492. }
  493. //
  494. // It's safe to unlock the table
  495. //
  496. Status = RtlLeaveCriticalSection(&(pEnumTable->CriticalSection));
  497. ASSERT( NT_SUCCESS( Status ) );
  498. #if DBG
  499. if ( !NT_SUCCESS( Status ) ) {
  500. DbgPrint( "WINREG: RtlLeaveCriticalSection() in EnumTableRemoveKey() failed. Status = %lx \n", Status );
  501. }
  502. #endif
  503. return Status;
  504. }
  505. NTSTATUS EnumTableGetNextEnum(
  506. EnumTable* pEnumTable,
  507. HKEY hKey,
  508. DWORD dwSubkey,
  509. KEY_INFORMATION_CLASS KeyInformationClass,
  510. PVOID pKeyInfo,
  511. DWORD cbKeyInfo,
  512. LPDWORD pcbKeyInfo)
  513. /*++
  514. Routine Description:
  515. Gets the next enumerated subkey for a
  516. particular subkey
  517. Arguments:
  518. pEnumTable - table that holds state of
  519. registry key enumerations
  520. hKey - key for whom we want to add state
  521. dwSubKey - index of subkey requested by caller
  522. for enumeration
  523. KeyInformationClass - the type of key information data
  524. requested by caller
  525. pKeyInfo - out param -- buffer for key information data for caller
  526. cbKeyInfo - size of pKeyInfo buffer
  527. pcbKeyInfo - out param -- size of key information returned to caller
  528. Return Value:
  529. Returns NT_SUCCESS (0) for success; error-code for failure.
  530. Notes:
  531. --*/
  532. {
  533. EnumState* pEnumState;
  534. EnumState* pRootState;
  535. NTSTATUS Status;
  536. BOOL fFreeState;
  537. //
  538. // Protect ourselves while we enumerate
  539. //
  540. Status = RtlEnterCriticalSection(&(pEnumTable->CriticalSection));
  541. //
  542. // Very big -- unlikely to happen unless there's a runaway enumeration
  543. // due to a bug in this module.
  544. //
  545. // ASSERT(dwSubkey < 16383);
  546. ASSERT( NT_SUCCESS( Status ) );
  547. if ( !NT_SUCCESS( Status ) ) {
  548. #if DBG
  549. DbgPrint( "WINREG: RtlEnterCriticalSection() in EnumTableGetNextENUm() failed. Status = %lx \n", Status );
  550. #endif
  551. return Status;
  552. }
  553. //
  554. // Find the enumeration state for the requested key. Note that even if this
  555. // function fails to find an existing state, which case it returns a failure code
  556. // it can still return an empty pEnumState for that hKey so it can be added later
  557. //
  558. Status = EnumTableGetKeyState(pEnumTable, hKey, dwSubkey, &pEnumState, &pRootState, pcbKeyInfo);
  559. if (!NT_SUCCESS(Status) || !pEnumState) {
  560. goto cleanup;
  561. }
  562. //
  563. // We have a state for this key, now we can use it to enumerate the next key
  564. //
  565. Status = EnumStateGetNextEnum(pEnumState, dwSubkey, KeyInformationClass, pKeyInfo, cbKeyInfo, pcbKeyInfo, &fFreeState);
  566. //
  567. // Below is an optimization for apps that enumerate HKEY_CLASSES_ROOT but close the handle and reopen it each
  568. // time before they call the registry enumeration api. This is a very bad way to use the api (that's two extra
  569. // kernel calls for the open and close per enumeration), but existing applications do this and
  570. // without the optimization, their enumeration times can go from 3 seconds to 1 or more minutes. With this optimization,
  571. // the time gets back down to a few seconds. This happened because we lost state after the close -- when the new
  572. // key was opened, we had to call the kernel to enumerate all the keys up to the requested index since we had no
  573. // previous state to go by -- this ends up making the entire enumeration an O(n^2) operation instead of O(n) as it
  574. // had been when callers didn't close the key during the enumeration. Here, n is a kernel trap to enumerate a key.
  575. //
  576. //
  577. // Above, we retrieved an enumeration state for the root of classes -- this state reflects the enumeration state
  578. // of the last handle that was used to enumerate the root on this thread. This way, when a new handle is opened
  579. // to enumerate the root, we start with this state which will most likely be right at the index before the requested
  580. // index. Instead of making i calls to NtEnumerateKey where i is the index of enumeration requested by the caller,
  581. // we make 1 or at most 2 calls.
  582. //
  583. //
  584. // Here, we update the root state to match the recently enumerated state. Note that this only happens
  585. // if the key being enumerated refers to HKEY_CLASSES_ROOT since pRootState is only non-NULL in this
  586. // case.
  587. //
  588. if (pRootState) {
  589. EnumTableUpdateRootState(pEnumTable, pRootState, pEnumState, fFreeState);
  590. }
  591. if (fFreeState) {
  592. NTSTATUS RemoveStatus;
  593. //
  594. // For whatever reason, we've been told to free the enumeration state for this key.
  595. // This could be due to an error, or it could be a normal situation such as reaching
  596. // the end of an enumeration.
  597. //
  598. RemoveStatus = EnumTableRemoveKey(pEnumTable, hKey, ENUM_TABLE_REMOVEKEY_CRITERIA_THISTHREAD);
  599. ASSERT(NT_SUCCESS(RemoveStatus));
  600. }
  601. cleanup:
  602. //
  603. // It's safe to unlock the table now.
  604. //
  605. {
  606. NTSTATUS CriticalSectionStatus;
  607. CriticalSectionStatus = RtlLeaveCriticalSection(&(pEnumTable->CriticalSection));
  608. ASSERT( NT_SUCCESS( CriticalSectionStatus ) );
  609. #if DBG
  610. if ( !NT_SUCCESS( CriticalSectionStatus ) ) {
  611. DbgPrint( "WINREG: RtlLeaveCriticalSection() in EnumTableGetNextEnum() failed. Status = %lx \n",
  612. CriticalSectionStatus );
  613. }
  614. #endif
  615. }
  616. return Status;
  617. }
  618. NTSTATUS EnumTableGetKeyState(
  619. EnumTable* pEnumTable,
  620. HKEY hKey,
  621. DWORD dwSubkey,
  622. EnumState** ppEnumState,
  623. EnumState** ppRootState,
  624. LPDWORD pcbKeyInfo)
  625. /*++
  626. Routine Description:
  627. Finds a key state for hKey -- creates a new state for hkey if
  628. there is no existing state
  629. Arguments:
  630. pEnumTable - enumeration table in which to find key's state
  631. hKey - handle to registry key for which to find state
  632. dwSubkey - subkey that we're trying to enumerate -- needed in
  633. case we need to create a new state
  634. ppEnumState - pointer to where we should return address of
  635. the retrieved state,
  636. ppRootState - if the retrieved state is the root of the classes
  637. tree, this address will point to a known state for the root
  638. that's good across all hkey's enumerated on this thread
  639. pcbKeyInfo - stores size of key information on return
  640. Return Value:
  641. STATUS_SUCCESS for success, other error code on error
  642. Notes:
  643. --*/
  644. {
  645. NTSTATUS Status;
  646. if (ppRootState) {
  647. *ppRootState = NULL;
  648. }
  649. //
  650. // Find the enumeration state for the requested key. Note that even if this
  651. // function fails to find an existing state, in which case it returns a failure code
  652. // it can still return an empty pEnumState for that hKey so it can be added later
  653. //
  654. Status = EnumTableFindKeyState(pEnumTable, hKey, ppEnumState);
  655. if (!NT_SUCCESS(Status)) {
  656. if (STATUS_OBJECT_NAME_NOT_FOUND == Status) {
  657. //
  658. // This means the key didn't exist, already, so we'll add it
  659. //
  660. Status = EnumTableAddKey(pEnumTable, hKey, dwSubkey, ppEnumState, ppRootState);
  661. if (!NT_SUCCESS(Status)) {
  662. return Status;
  663. }
  664. //
  665. // The above function can succeed but return a NULL pEnumState -- this
  666. // happens if it turns out this key is not a "special key" -- i.e. this key's
  667. // parents exist in only one hive, not two, so we don't need to do anything here
  668. // and regular enumeration will suffice.
  669. //
  670. if (!(*ppEnumState)) {
  671. //
  672. // We set this value to let our caller know that this isn't a class key
  673. //
  674. *pcbKeyInfo = 0;
  675. }
  676. }
  677. } else {
  678. if ((*ppEnumState)->fClassesRoot) {
  679. Status = EnumTableGetRootState(pEnumTable, ppRootState);
  680. }
  681. }
  682. return Status;
  683. }
  684. NTSTATUS EnumTableGetRootState(
  685. EnumTable* pEnumTable,
  686. EnumState** ppRootState)
  687. /*++
  688. Routine Description:
  689. Arguments:
  690. pEnumTable - enumeration table in which to find the root
  691. state
  692. ppRootState - points to address of root state on return
  693. Return Value:
  694. Returns NT_SUCCESS (0) for success; error-code for failure.
  695. Notes:
  696. --*/
  697. {
  698. DWORD cbKeyInfo;
  699. KeyStateList* pStateList;
  700. //
  701. // We assume the caller has made sure that a state list
  702. // for this thread exists -- this should never, ever fail
  703. //
  704. pStateList = (KeyStateList*) StateObjectListFind(
  705. &(pEnumTable->ThreadEnumList),
  706. ULongToPtr((const unsigned long)GetCurrentThreadId()));
  707. ASSERT(pStateList);
  708. *ppRootState = &(pStateList->RootState);
  709. return STATUS_SUCCESS;
  710. }
  711. void EnumTableUpdateRootState(
  712. EnumTable* pEnumTable,
  713. EnumState* pRootState,
  714. EnumState* pEnumState,
  715. BOOL fResetState)
  716. /*++
  717. Routine Description:
  718. Updates the state of the classes root for this thread -- this
  719. allows us to optimize for apps that close handles when enumerating
  720. hkcr -- we use this classes root state when no existing state is
  721. found for an hkey that refers to hkcr, and we update this state
  722. after enumerating an hkcr key on this thread so that it will
  723. be up to date.
  724. Arguments:
  725. pEnumTable - enumeration table in which the classes root state resides
  726. pRootState - classes root state that should be updated
  727. ppEnumState - state that contains the data with which pRootState should
  728. be updated
  729. fResetState - if TRUE, this flag means we should not update the root state
  730. with pEnumState's data, just reset it. If FALSE, we update the root
  731. with pEnumState's data.
  732. Return Value:
  733. None.
  734. Notes:
  735. --*/
  736. {
  737. NTSTATUS Status;
  738. //
  739. // See if we need to merely reset the root or actually
  740. // update it with another state
  741. //
  742. if (!fResetState) {
  743. //
  744. // Don't reset -- copy over the state from pEnumState to the
  745. // root state -- the root's state will be the same as pEnumState's
  746. // after this copy
  747. //
  748. Status = EnumStateCopy(pRootState, pEnumState);
  749. } else {
  750. //
  751. // Just clear out the state -- caller didn't request that we
  752. // use pEnumState.
  753. //
  754. Status = EnumStateInit(
  755. pRootState,
  756. 0,
  757. 0,
  758. ENUM_DIRECTION_FORWARD,
  759. NULL);
  760. }
  761. //
  762. // If there's a failure, it must be out-of-memory, so we should get rid
  763. // of this state since we can't make it accurately reflect the true
  764. // enumeration state
  765. //
  766. if (!NT_SUCCESS(Status)) {
  767. #if DBG
  768. DbgPrint( "WINREG: failure in UpdateRootState. Status = %lx \n", Status );
  769. #endif
  770. ASSERT(STATUS_NO_MEMORY == Status);
  771. EnumStateClear(pRootState);
  772. }
  773. }
  774. VOID KeyStateListInit(KeyStateList* pStateList)
  775. /*++
  776. Routine Description:
  777. Initializes a state list
  778. Arguments:
  779. pObject -- pointer to KeyStateList object to destroy
  780. Return Value:
  781. Returns NT_SUCCESS (0) for success; error-code for failure.
  782. Notes:
  783. --*/
  784. {
  785. //
  786. // First initialize the base object
  787. //
  788. StateObjectListInit((StateObjectList*) pStateList,
  789. ULongToPtr((const unsigned long)GetCurrentThreadId()));
  790. //
  791. // Now do KeyStateList specific init
  792. //
  793. (void) EnumStateInit(
  794. &(pStateList->RootState),
  795. NULL,
  796. 0,
  797. ENUM_DIRECTION_FORWARD,
  798. NULL);
  799. }
  800. VOID KeyStateListDestroy(StateObject* pObject)
  801. /*++
  802. Routine Description:
  803. Destroys an KeyStateList, freeing its resources such
  804. as memory or kernel object handles
  805. Arguments:
  806. pObject -- pointer to KeyStateList object to destroy
  807. Return Value:
  808. Returns NT_SUCCESS (0) for success; error-code for failure.
  809. Notes:
  810. --*/
  811. {
  812. KeyStateList* pThisList;
  813. pThisList = (KeyStateList*) pObject;
  814. //
  815. // Destroy all states in this list
  816. //
  817. StateObjectListClear(
  818. (StateObjectList*) pObject,
  819. EnumStateDestroy);
  820. //
  821. // Free resources associated with the root state
  822. //
  823. EnumStateClear(&(pThisList->RootState));
  824. //
  825. // Free the data structure for this object
  826. //
  827. RegClassHeapFree(pThisList);
  828. }
  829. NTSTATUS EnumStateInit(
  830. EnumState* pEnumState,
  831. HKEY hKey,
  832. DWORD dwFirstSubKey,
  833. DWORD dwDirection,
  834. SKeySemantics* pKeySemantics)
  835. /*++
  836. Routine Description:
  837. Initializes enumeration state
  838. Arguments:
  839. pEnumState - enumeration state to initialize
  840. hKey - registry key to which this state refers
  841. dwFirstSubKey - index of the first subkey which this state will enumerate
  842. dwDirection - direction through which we should enumerate -- either
  843. ENUM_DIRECTION_FORWARD or ENUM_DIRECTION_BACKWARD
  844. pKeySemantics - structure containing information about hKey
  845. Return Value:
  846. Returns NT_SUCCESS (0) for success; error-code for failure.
  847. --*/
  848. {
  849. NTSTATUS Status;
  850. ULONG cMachineKeys;
  851. ULONG cUserKeys;
  852. HKEY hkOther;
  853. ASSERT((ENUM_DIRECTION_FORWARD == dwDirection) || (ENUM_DIRECTION_BACKWARD == dwDirection) ||
  854. (ENUM_DIRECTION_IGNORE == dwDirection));
  855. ASSERT((ENUM_DIRECTION_IGNORE == dwDirection) ? hKey == NULL : TRUE);
  856. Status = STATUS_SUCCESS;
  857. hkOther = NULL;
  858. //
  859. // If no hkey is specified, this is an init of a blank enum
  860. // state, so clear everything
  861. //
  862. if (!hKey) {
  863. memset(pEnumState, 0, sizeof(*pEnumState));
  864. }
  865. //
  866. // Clear each subtree
  867. //
  868. EnumSubtreeStateClear(&(pEnumState->UserState));
  869. EnumSubtreeStateClear(&(pEnumState->MachineState));
  870. //
  871. // Reset each subtree
  872. //
  873. pEnumState->UserState.Finished = FALSE;
  874. pEnumState->MachineState.Finished = FALSE;
  875. pEnumState->UserState.iSubKey = 0;
  876. pEnumState->MachineState.iSubKey = 0;
  877. cUserKeys = 0;
  878. cMachineKeys = 0;
  879. if (pKeySemantics) {
  880. StateObjectInit((StateObject*) &(pEnumState->Object), hKey);
  881. }
  882. if (hKey) {
  883. if (pKeySemantics) {
  884. pEnumState->fClassesRoot = IsRootKey(pKeySemantics);
  885. }
  886. //
  887. // open the other key if we have enough info to do so --
  888. //
  889. if (pKeySemantics) {
  890. //
  891. // Remember, only one of the handles returned below
  892. // is new -- the other is simply hKey
  893. //
  894. Status = BaseRegGetUserAndMachineClass(
  895. pKeySemantics,
  896. hKey,
  897. MAXIMUM_ALLOWED,
  898. &(pEnumState->hkMachineKey),
  899. &(pEnumState->hkUserKey));
  900. if (!NT_SUCCESS(Status)) {
  901. return Status;
  902. }
  903. }
  904. //
  905. // for backwards enumerations
  906. //
  907. if (ENUM_DIRECTION_BACKWARD == dwDirection) {
  908. ULONG cMachineKeys;
  909. ULONG cUserKeys;
  910. HKEY hkUser;
  911. HKEY hkMachine;
  912. cMachineKeys = 0;
  913. cUserKeys = 0;
  914. hkMachine = pEnumState->hkMachineKey;
  915. hkUser = pEnumState->hkUserKey;
  916. //
  917. // In order to query for subkey counts, we should
  918. // to get a new handle since the caller supplied handle
  919. // may not have enough permissions
  920. //
  921. {
  922. HKEY hkSource;
  923. HANDLE hCurrentProcess;
  924. hCurrentProcess = NtCurrentProcess();
  925. hkSource = (hkMachine == hKey) ? hkMachine : hkUser;
  926. Status = NtDuplicateObject(
  927. hCurrentProcess,
  928. hkSource,
  929. hCurrentProcess,
  930. &hkOther,
  931. KEY_QUERY_VALUE,
  932. FALSE,
  933. 0);
  934. if (!NT_SUCCESS(Status)) {
  935. goto error_exit;
  936. }
  937. if (hkSource == hkUser) {
  938. hkUser = hkOther;
  939. } else {
  940. hkMachine = hkOther;
  941. }
  942. }
  943. //
  944. // find new start -- query for index of last subkey in
  945. // each hive
  946. //
  947. if (hkMachine) {
  948. Status = GetSubKeyCount(hkMachine, &cMachineKeys);
  949. if (!NT_SUCCESS(Status)) {
  950. goto error_exit;
  951. }
  952. }
  953. if (hkUser) {
  954. Status = GetSubKeyCount(hkUser, &cUserKeys);
  955. if (!NT_SUCCESS(Status)) {
  956. goto error_exit;
  957. }
  958. }
  959. //
  960. // If either subtree has no subkeys, we're done enumerating that
  961. // subtree
  962. //
  963. if (!cUserKeys) {
  964. pEnumState->UserState.Finished = TRUE;
  965. } else {
  966. pEnumState->UserState.iSubKey = cUserKeys - 1;
  967. }
  968. if (!cMachineKeys) {
  969. pEnumState->MachineState.Finished = TRUE;
  970. } else {
  971. pEnumState->MachineState.iSubKey = cMachineKeys - 1;
  972. }
  973. }
  974. }
  975. //
  976. // Set members of this structure
  977. //
  978. pEnumState->dwThreadId = GetCurrentThreadId();
  979. pEnumState->Direction = dwDirection;
  980. pEnumState->dwLastRequest = dwFirstSubKey;
  981. pEnumState->LastLocation = ENUM_LOCATION_NONE;
  982. pEnumState->hKey = hKey;
  983. error_exit:
  984. if (!NT_SUCCESS(Status)) {
  985. EnumSubtreeStateClear(&(pEnumState->MachineState));
  986. EnumSubtreeStateClear(&(pEnumState->UserState));
  987. }
  988. if (hkOther) {
  989. NtClose(hkOther);
  990. }
  991. return Status;
  992. }
  993. NTSTATUS EnumStateGetNextEnum(
  994. EnumState* pEnumState,
  995. DWORD dwSubKey,
  996. KEY_INFORMATION_CLASS KeyInformationClass,
  997. PVOID pKeyInfo,
  998. DWORD cbKeyInfo,
  999. LPDWORD pcbKeyInfo,
  1000. BOOL* pfFreeState)
  1001. /*++
  1002. Routine Description:
  1003. Gets the next key in an enumeration based on the current state.
  1004. Arguments:
  1005. pEnumState - enumeration state on which to base our search
  1006. for the next key
  1007. dwSubKey - index of key to enumerate
  1008. KeyInformationClass - enum for what sort of information to retrieve in the
  1009. enumeration -- Basic Information or Node Information
  1010. pKeyInfo - location to store retrieved data for caller
  1011. cbKeyInfo - size of caller's info buffer
  1012. pcbKeyInfo - size of data this function writes to buffer on return.
  1013. pfFreeState - out param -- if set to TRUE, caller should free pEnumState.
  1014. Return Value:
  1015. Returns NT_SUCCESS (0) for success; error-code for failure.
  1016. Notes:
  1017. This function essentially enumerates from the previous index requested
  1018. by the caller of RegEnumKeyEx to the new one. In most cases, this just
  1019. means one trip to the kernel -- i.e. if a caller goes from index 2 to 3,
  1020. or from 3 to 2, this is one trip to the kernel. However, if the caller goes
  1021. from 2 to 5, we'll have to do several enumerations on the way from 2 to 5.
  1022. Also, if the caller switches direction (i.e. starts off 0,1,2,3 and then
  1023. requests 1), a large penalty may be incurred. When switching from ascending
  1024. to descending, we have to enumerate all keys to the end and then before we
  1025. can then enumerate down to the caller's requested index. Switching from
  1026. descending to ascending is less expensive -- we know that the beginning
  1027. is at 0 for both user and machine keys, so we can simply set our indices to
  1028. 0 without enumerating anything. However, we must then enumerate to the
  1029. caller's requested index. Note that for all descending enumerations, we
  1030. must enumerate all the way to the end first before returning anything to the
  1031. caller.
  1032. --*/
  1033. {
  1034. NTSTATUS Status;
  1035. LONG lIncrement;
  1036. DWORD dwStart;
  1037. DWORD dwLimit;
  1038. EnumSubtreeState* pTreeState;
  1039. //
  1040. // If anything bad happens, this state should be freed
  1041. //
  1042. *pfFreeState = TRUE;
  1043. //
  1044. // Find out the limits (start, finish, increment) for
  1045. // our enumeration. The increment is either 1 or -1,
  1046. // depending on whether this is an ascending or descending
  1047. // enumeration. EnumStateSetLimits will take into account
  1048. // any changes in direction and set dwStart and dwLimit
  1049. // accordingly.
  1050. //
  1051. Status = EnumStateSetLimits(
  1052. pEnumState,
  1053. dwSubKey,
  1054. &dwStart,
  1055. &dwLimit,
  1056. &lIncrement);
  1057. if (!NT_SUCCESS(Status)) {
  1058. return Status;
  1059. }
  1060. //
  1061. // Get the next enum to give back to the caller
  1062. //
  1063. Status = EnumStateChooseNext(
  1064. pEnumState,
  1065. dwSubKey,
  1066. dwStart,
  1067. dwLimit,
  1068. lIncrement,
  1069. &pTreeState);
  1070. if (!NT_SUCCESS(Status)) {
  1071. return Status;
  1072. }
  1073. //
  1074. // We have retrieved information, so we should
  1075. // not free this state
  1076. //
  1077. if (!(pEnumState->UserState.Finished && pEnumState->MachineState.Finished)) {
  1078. *pfFreeState = FALSE;
  1079. }
  1080. //
  1081. // Remember the last key we enumerated
  1082. //
  1083. pEnumState->dwLastRequest = dwSubKey;
  1084. //
  1085. // Copy the retrieved information to the user's
  1086. // buffer.
  1087. //
  1088. Status = EnumSubtreeStateCopyKeyInfo(
  1089. pTreeState,
  1090. KeyInformationClass,
  1091. pKeyInfo,
  1092. cbKeyInfo,
  1093. pcbKeyInfo);
  1094. //
  1095. // The copy could fail if the user's buffer isn't big enough --
  1096. // if it succeeds, clear the name information for the subkey from
  1097. // which we retrieved the data so that the next time we're called
  1098. // we'll get the next subkey for that subtree.
  1099. //
  1100. if (NT_SUCCESS(Status)) {
  1101. EnumSubtreeStateClear(pTreeState);
  1102. }
  1103. return Status;
  1104. }
  1105. NTSTATUS EnumStateSetLimits(
  1106. EnumState* pEnumState,
  1107. DWORD dwSubKey,
  1108. LPDWORD pdwStart,
  1109. LPDWORD pdwLimit,
  1110. PLONG plIncrement)
  1111. /*++
  1112. Routine Description:
  1113. Gets the limits (start, finish, increment) for enumerating a given
  1114. subkey index
  1115. Arguments:
  1116. pEnumState - enumeration state on which to base our limits
  1117. dwSubKey - index of key which caller wants enumerated
  1118. pdwStart - out param -- result is the place at which to start
  1119. enumerating in order to find dwSubKey
  1120. pdwLimit - out param -- result is the place at which to stop
  1121. enumerating when looking for dwSubKey
  1122. plIncrement - out param -- increment to use for enumeration. It will
  1123. be set to 1 if the enumeration is upward (0,1,2...) or
  1124. -1 if it is downard (3,2,1,...).
  1125. Return Value:
  1126. Returns NT_SUCCESS (0) for success; error-code for failure.
  1127. Notes:
  1128. --*/
  1129. {
  1130. LONG lNewIncrement;
  1131. NTSTATUS Status;
  1132. BOOL fSameKey;
  1133. //
  1134. // set our increment to the direction which our state remembers
  1135. //
  1136. *plIncrement = pEnumState->Direction == ENUM_DIRECTION_FORWARD ? 1 : -1;
  1137. fSameKey = FALSE;
  1138. //
  1139. // Figure out what the new direction should be
  1140. // This is done by comparing the current request
  1141. // with the last request.
  1142. //
  1143. if (dwSubKey > pEnumState->dwLastRequest) {
  1144. lNewIncrement = 1;
  1145. } else if (dwSubKey < pEnumState->dwLastRequest) {
  1146. lNewIncrement = -1;
  1147. } else {
  1148. //
  1149. // We are enumerating a key that may already
  1150. // have been enumerated
  1151. //
  1152. fSameKey = TRUE;
  1153. lNewIncrement = *plIncrement;
  1154. }
  1155. //
  1156. // See if we've changed direction
  1157. //
  1158. if (lNewIncrement != *plIncrement) {
  1159. //
  1160. // If so, we should throw away all existing state and start from scratch
  1161. //
  1162. Status = EnumStateInit(
  1163. pEnumState,
  1164. pEnumState->hKey,
  1165. (-1 == lNewIncrement) ? dwSubKey : 0,
  1166. (-1 == lNewIncrement) ? ENUM_DIRECTION_BACKWARD : ENUM_DIRECTION_FORWARD,
  1167. NULL);
  1168. if (!NT_SUCCESS(Status)) {
  1169. return Status;
  1170. }
  1171. }
  1172. //
  1173. // By default, we start enumerating where we left off
  1174. //
  1175. *pdwStart = pEnumState->dwLastRequest;
  1176. //
  1177. // for state for which we have previously enumerated a key
  1178. //
  1179. if (ENUM_LOCATION_NONE != pEnumState->LastLocation) {
  1180. //
  1181. // We're going in the same direction as on the
  1182. // previous call. We should start
  1183. // one past our previous position. Note that we
  1184. // only start there if this is a different key --
  1185. // if we've already enumerated it we start at the
  1186. // same spot.
  1187. //
  1188. if (!fSameKey) {
  1189. *pdwStart += *plIncrement;
  1190. } else {
  1191. //
  1192. // If we're being asked for the same index
  1193. // multiple times they're probably deleting
  1194. // keys -- we should reset ourselves to
  1195. // the beginning so their enum will hit
  1196. // all the keys
  1197. //
  1198. //
  1199. // We're starting at zero, so set ourselves
  1200. // to start at the beginning
  1201. //
  1202. Status = EnumStateInit(
  1203. pEnumState,
  1204. pEnumState->hKey,
  1205. 0,
  1206. ENUM_DIRECTION_FORWARD,
  1207. NULL);
  1208. if (!NT_SUCCESS(Status)) {
  1209. return Status;
  1210. }
  1211. *plIncrement = 1;
  1212. pEnumState->Direction = ENUM_DIRECTION_FORWARD;
  1213. *pdwStart = 0;
  1214. }
  1215. } else {
  1216. //
  1217. // No previous calls were made for this state
  1218. //
  1219. if (ENUM_DIRECTION_BACKWARD == pEnumState->Direction) {
  1220. //
  1221. // For backwards enumerations, we want to get an
  1222. // accurate count of total subkeys and start there
  1223. //
  1224. Status = ClassKeyCountSubKeys(
  1225. pEnumState->hKey,
  1226. pEnumState->hkUserKey,
  1227. pEnumState->hkMachineKey,
  1228. 0,
  1229. pdwStart);
  1230. if (!NT_SUCCESS(Status)) {
  1231. return Status;
  1232. }
  1233. //
  1234. // Make sure we don't go past the end
  1235. //
  1236. if (dwSubKey >= *pdwStart) {
  1237. return STATUS_NO_MORE_ENTRIES;
  1238. }
  1239. //
  1240. // This is a zero-based index, so to
  1241. // put our start at the very end we must
  1242. // be one less than the number of keys
  1243. //
  1244. (*pdwStart)--;
  1245. *plIncrement = -1;
  1246. } else {
  1247. *plIncrement = 1;
  1248. }
  1249. }
  1250. //
  1251. // Set limit to be one past requested subkey
  1252. //
  1253. *pdwLimit = dwSubKey + *plIncrement;
  1254. return STATUS_SUCCESS;
  1255. }
  1256. NTSTATUS EnumStateChooseNext(
  1257. EnumState* pEnumState,
  1258. DWORD dwSubKey,
  1259. DWORD dwStart,
  1260. DWORD dwLimit,
  1261. LONG lIncrement,
  1262. EnumSubtreeState** ppTreeState)
  1263. /*++
  1264. Routine Description:
  1265. Iterates through registry keys to get the key requested by the caller
  1266. Arguments:
  1267. pEnumState - enumeration state on which to base our search
  1268. dwSubKey - index of key which caller wants enumerated
  1269. dwStart - The place at which to start
  1270. enumerating in order to find dwSubKey
  1271. dwLimit - The place at which to stop
  1272. enumerating when looking for dwSubKey
  1273. lIncrement - Increment to use for enumeration. It will
  1274. be set to 1 if the enumeration is upward (0,1,2...) or
  1275. -1 if it is downard (3,2,1,...).
  1276. ppTreeState - out param -- pointer to address of subtree state in which this regkey
  1277. was found -- each EnumState has two EnumSubtreeState's -- one for user
  1278. and one for machine.
  1279. Return Value:
  1280. Returns NT_SUCCESS (0) for success; error-code for failure.
  1281. Notes:
  1282. --*/
  1283. {
  1284. DWORD iCurrent;
  1285. NTSTATUS Status;
  1286. BOOL fClearLast;
  1287. Status = STATUS_NO_MORE_ENTRIES;
  1288. fClearLast = FALSE;
  1289. //
  1290. // We will now iterate from dwStart to dwLimit so that we can find the key
  1291. // requested by the caller
  1292. //
  1293. for (iCurrent = dwStart; iCurrent != dwLimit; iCurrent += lIncrement) {
  1294. BOOL fFoundKey;
  1295. BOOL fIgnoreFailure;
  1296. fFoundKey = FALSE;
  1297. fIgnoreFailure = FALSE;
  1298. Status = STATUS_NO_MORE_ENTRIES;
  1299. //
  1300. // Clear last subtree
  1301. //
  1302. if (fClearLast) {
  1303. EnumSubtreeStateClear(*ppTreeState);
  1304. }
  1305. //
  1306. // if key names aren't present, alloc space and get names
  1307. //
  1308. if (pEnumState->hkUserKey) {
  1309. if (pEnumState->UserState.pKeyInfo) {
  1310. fFoundKey = TRUE;
  1311. } else if (!(pEnumState->UserState.Finished)) {
  1312. // get user key info
  1313. Status = EnumClassKey(
  1314. pEnumState->hkUserKey,
  1315. &(pEnumState->UserState));
  1316. fFoundKey = NT_SUCCESS(Status);
  1317. //
  1318. // If there are no more subkeys for this subtree,
  1319. // mark it as finished
  1320. //
  1321. if (!NT_SUCCESS(Status)) {
  1322. if (STATUS_NO_MORE_ENTRIES != Status) {
  1323. return Status;
  1324. }
  1325. if (lIncrement > 0) {
  1326. pEnumState->UserState.Finished = TRUE;
  1327. } else {
  1328. pEnumState->UserState.iSubKey += lIncrement;
  1329. fIgnoreFailure = TRUE;
  1330. }
  1331. }
  1332. }
  1333. }
  1334. if (pEnumState->hkMachineKey) {
  1335. if (pEnumState->MachineState.pKeyInfo) {
  1336. fFoundKey = TRUE;
  1337. } else if (!(pEnumState->MachineState.Finished)) {
  1338. // get machine key info
  1339. Status = EnumClassKey(
  1340. pEnumState->hkMachineKey,
  1341. &(pEnumState->MachineState));
  1342. //
  1343. // If there are no more subkeys for this subtree,
  1344. // mark it as finished
  1345. //
  1346. if (NT_SUCCESS(Status)) {
  1347. fFoundKey = TRUE;
  1348. } else if (STATUS_NO_MORE_ENTRIES == Status) {
  1349. if (lIncrement > 0) {
  1350. pEnumState->MachineState.Finished = TRUE;
  1351. } else {
  1352. pEnumState->MachineState.iSubKey += lIncrement;
  1353. fIgnoreFailure = TRUE;
  1354. }
  1355. }
  1356. }
  1357. }
  1358. //
  1359. // If we found no keys in either user or machine locations, there are
  1360. // no more keys.
  1361. //
  1362. if (!fFoundKey) {
  1363. //
  1364. // For descending enumerations, we ignore STATUS_NO_MORE_ENTRIES
  1365. // and keep going until we find one.
  1366. //
  1367. if (fIgnoreFailure) {
  1368. continue;
  1369. }
  1370. return Status;
  1371. }
  1372. //
  1373. // If we already hit the bottom, skip to the end
  1374. //
  1375. if ((pEnumState->UserState.iSubKey == 0) &&
  1376. (pEnumState->MachineState.iSubKey == 0) &&
  1377. (lIncrement < 0)) {
  1378. iCurrent = dwLimit - lIncrement;
  1379. }
  1380. //
  1381. // Now we need to choose between keys in the machine hive and user hives --
  1382. // this call will choose which key to use.
  1383. //
  1384. Status = EnumStateCompareSubtrees(pEnumState, lIncrement, ppTreeState);
  1385. if (!NT_SUCCESS(Status)) {
  1386. pEnumState->dwLastRequest = dwSubKey;
  1387. return Status;
  1388. }
  1389. fClearLast = TRUE;
  1390. }
  1391. return Status;
  1392. }
  1393. NTSTATUS EnumStateCompareSubtrees(
  1394. EnumState* pEnumState,
  1395. LONG lIncrement,
  1396. EnumSubtreeState** ppSubtree)
  1397. /*++
  1398. Routine Description:
  1399. Compares the user and machine subtrees of an enumeration state
  1400. to see which of the two current keys in each hive should be
  1401. returned as the next key in an enumeration
  1402. Arguments:
  1403. pEnumState - enumeration state on which to base our search
  1404. lIncrement - Increment to use for enumeration. It will
  1405. be set to 1 if the enumeration is upward (0,1,2...) or
  1406. -1 if it is downard (3,2,1,...).
  1407. ppSubtree - out param -- pointer to address of subtree state where
  1408. key was found -- the name of the key can be extracted from it.
  1409. Return Value:
  1410. Returns NT_SUCCESS (0) for success; error-code for failure.
  1411. Notes:
  1412. --*/
  1413. {
  1414. //
  1415. // If both subtrees have a current subkey name, we'll need to compare
  1416. // the names
  1417. //
  1418. if (pEnumState->MachineState.pKeyInfo && pEnumState->UserState.pKeyInfo) {
  1419. UNICODE_STRING MachineKeyName;
  1420. UNICODE_STRING UserKeyName;
  1421. LONG lCompareResult;
  1422. MachineKeyName.Buffer = pEnumState->MachineState.pKeyInfo->Name;
  1423. MachineKeyName.Length = (USHORT) pEnumState->MachineState.pKeyInfo->NameLength;
  1424. UserKeyName.Buffer = pEnumState->UserState.pKeyInfo->Name;
  1425. UserKeyName.Length = (USHORT) pEnumState->UserState.pKeyInfo->NameLength;
  1426. //
  1427. // Do the comparison
  1428. //
  1429. lCompareResult =
  1430. RtlCompareUnicodeString(&UserKeyName, &MachineKeyName, TRUE) * lIncrement;
  1431. //
  1432. // User wins comparison
  1433. //
  1434. if (lCompareResult < 0) {
  1435. // choose user
  1436. *ppSubtree = &(pEnumState->UserState);
  1437. pEnumState->LastLocation = ENUM_LOCATION_USER;
  1438. } else if (lCompareResult > 0) {
  1439. //
  1440. // Machine wins choose machine
  1441. //
  1442. *ppSubtree = &(pEnumState->MachineState);
  1443. pEnumState->LastLocation = ENUM_LOCATION_MACHINE;
  1444. } else {
  1445. //
  1446. // Comparison returned equality -- the keys have the same
  1447. // name. This means the same key name exists in both machine and
  1448. // user, so we need to make a choice about which one we will enumerate.
  1449. // Policy for per-user class registration enumeration is to choose user, just
  1450. // as we do for other api's such as RegOpenKeyEx and RegCreateKeyEx.
  1451. //
  1452. if (!((pEnumState->MachineState.iSubKey == 0) && (lIncrement < 0))) {
  1453. pEnumState->MachineState.iSubKey += lIncrement;
  1454. } else {
  1455. pEnumState->MachineState.Finished = TRUE;
  1456. }
  1457. //
  1458. // Clear the machine state and move it to the next index -- we don't
  1459. // have to clear the user state yet because the state of whichever subtree
  1460. // was selected is cleared down below
  1461. //
  1462. EnumSubtreeStateClear(&(pEnumState->MachineState));
  1463. pEnumState->LastLocation = ENUM_LOCATION_USER;
  1464. *ppSubtree = &(pEnumState->UserState);
  1465. }
  1466. } else if (!(pEnumState->UserState.pKeyInfo) && !(pEnumState->MachineState.pKeyInfo)) {
  1467. //
  1468. // Neither subtree state has a subkey, so there are no subkeys
  1469. //
  1470. return STATUS_NO_MORE_ENTRIES;
  1471. } else if (pEnumState->MachineState.pKeyInfo) {
  1472. //
  1473. // Only machine has a subkey
  1474. //
  1475. *ppSubtree = &(pEnumState->MachineState);
  1476. pEnumState->LastLocation = ENUM_LOCATION_MACHINE;
  1477. } else {
  1478. //
  1479. // only user has a subkey
  1480. //
  1481. *ppSubtree = &(pEnumState->UserState);
  1482. pEnumState->LastLocation = ENUM_LOCATION_USER;
  1483. }
  1484. //
  1485. // change the state of the subtree which we selected
  1486. //
  1487. if (!(((*ppSubtree)->iSubKey == 0) && (lIncrement < 0))) {
  1488. (*ppSubtree)->iSubKey += lIncrement;
  1489. } else {
  1490. (*ppSubtree)->Finished = TRUE;
  1491. }
  1492. return STATUS_SUCCESS;
  1493. }
  1494. void EnumStateDestroy(StateObject* pObject)
  1495. {
  1496. EnumStateClear((EnumState*) pObject);
  1497. RegClassHeapFree(pObject);
  1498. }
  1499. VOID EnumStateClear(EnumState* pEnumState)
  1500. /*++
  1501. Routine Description:
  1502. Clears the enumeration state
  1503. Arguments:
  1504. pEnumState - enumeration state to clear
  1505. Return Value:
  1506. Returns NT_SUCCESS (0) for success; error-code for failure.
  1507. Notes:
  1508. --*/
  1509. {
  1510. //
  1511. // Close an existing reference to a second key
  1512. //
  1513. if (pEnumState->hkMachineKey && (pEnumState->hKey != pEnumState->hkMachineKey)) {
  1514. NtClose(pEnumState->hkMachineKey);
  1515. } else if (pEnumState->hkUserKey && (pEnumState->hKey != pEnumState->hkUserKey)) {
  1516. NtClose(pEnumState->hkUserKey);
  1517. }
  1518. //
  1519. // Free any heap memory held by our subtrees
  1520. //
  1521. EnumSubtreeStateClear(&(pEnumState->UserState));
  1522. EnumSubtreeStateClear(&(pEnumState->MachineState));
  1523. //
  1524. // reset everything in this state
  1525. //
  1526. memset(pEnumState, 0, sizeof(*pEnumState));
  1527. }
  1528. BOOL EnumStateIsEmpty(EnumState* pEnumState)
  1529. /*++
  1530. Routine Description:
  1531. Returns whether or not an enumeration state is empty.
  1532. An enumeration state is empty if it is not associated
  1533. with any particular registry key handle
  1534. Arguments:
  1535. pEnumState - enumeration state to clear
  1536. Return Value:
  1537. Returns NT_SUCCESS (0) for success; error-code for failure.
  1538. Notes:
  1539. --*/
  1540. {
  1541. return pEnumState->hKey == NULL;
  1542. }
  1543. NTSTATUS EnumStateCopy(
  1544. EnumState* pDestState,
  1545. EnumState* pEnumState)
  1546. /*++
  1547. Routine Description:
  1548. Copies an enumeration state for one hkey
  1549. to the state for another hkey -- note that it the
  1550. does not change the hkey referred to by the destination
  1551. state, it just makes pDestState->hKey's state the
  1552. same as pEnumState's
  1553. Arguments:
  1554. pDestState - enumeration state which is destination
  1555. of the copy
  1556. pEnumState - source enumeration for the copy
  1557. Return Value:
  1558. STATUS_SUCCESS for success, other error code on error
  1559. Notes:
  1560. --*/
  1561. {
  1562. NTSTATUS Status;
  1563. PKEY_NODE_INFORMATION pKeyInfoUser;
  1564. PKEY_NODE_INFORMATION pKeyInfoMachine;
  1565. Status = STATUS_SUCCESS;
  1566. //
  1567. // Copy simple data
  1568. //
  1569. pDestState->Direction = pEnumState->Direction;
  1570. pDestState->LastLocation = pEnumState->LastLocation;
  1571. pDestState->dwLastRequest = pEnumState->dwLastRequest;
  1572. pDestState->dwThreadId = pEnumState->dwThreadId;
  1573. //
  1574. // Free existing data before we overwrite it -- note that the pKeyInfo can point to a fixed buffer inside the state or
  1575. // a heap allocated buffer, so we must see which one it points to before we decide to free it
  1576. //
  1577. if (pDestState->UserState.pKeyInfo &&
  1578. (pDestState->UserState.pKeyInfo != (PKEY_NODE_INFORMATION) pDestState->UserState.KeyInfoBuffer)) {
  1579. RegClassHeapFree(pDestState->UserState.pKeyInfo);
  1580. pDestState->UserState.pKeyInfo = NULL;
  1581. }
  1582. if (pDestState->MachineState.pKeyInfo &&
  1583. (pDestState->MachineState.pKeyInfo != (PKEY_NODE_INFORMATION) pDestState->MachineState.KeyInfoBuffer)) {
  1584. RegClassHeapFree(pDestState->MachineState.pKeyInfo);
  1585. pDestState->MachineState.pKeyInfo = NULL;
  1586. }
  1587. //
  1588. // easy way to copy states -- we'll have to fix up below though since pKeyInfo can be
  1589. // self-referential.
  1590. //
  1591. memcpy(&(pDestState->UserState), &(pEnumState->UserState), sizeof(pEnumState->UserState));
  1592. memcpy(&(pDestState->MachineState), &(pEnumState->MachineState), sizeof(pEnumState->MachineState));
  1593. pKeyInfoUser = NULL;
  1594. pKeyInfoMachine = NULL;
  1595. //
  1596. // Copy new data -- as above, keep in mind that pKeyInfo can be self-referential, so check
  1597. // for that before deciding whether to allocate heap or use the internal fixed buffer of the
  1598. // structure.
  1599. //
  1600. if (pEnumState->UserState.pKeyInfo &&
  1601. ((pEnumState->UserState.pKeyInfo != (PKEY_NODE_INFORMATION) pEnumState->UserState.KeyInfoBuffer))) {
  1602. pKeyInfoUser = (PKEY_NODE_INFORMATION)
  1603. RegClassHeapAlloc(pEnumState->UserState.cbKeyInfo);
  1604. if (!pKeyInfoUser) {
  1605. Status = STATUS_NO_MEMORY;
  1606. }
  1607. pDestState->UserState.pKeyInfo = pKeyInfoUser;
  1608. RtlCopyMemory(pDestState->UserState.pKeyInfo,
  1609. pEnumState->UserState.pKeyInfo,
  1610. pEnumState->UserState.cbKeyInfo);
  1611. } else {
  1612. if (pDestState->UserState.pKeyInfo) {
  1613. pDestState->UserState.pKeyInfo = (PKEY_NODE_INFORMATION) pDestState->UserState.KeyInfoBuffer;
  1614. }
  1615. }
  1616. if (pEnumState->MachineState.pKeyInfo &&
  1617. ((pEnumState->MachineState.pKeyInfo != (PKEY_NODE_INFORMATION) pEnumState->MachineState.KeyInfoBuffer))) {
  1618. pKeyInfoMachine = (PKEY_NODE_INFORMATION)
  1619. RegClassHeapAlloc(pEnumState->MachineState.cbKeyInfo);
  1620. if (!pKeyInfoMachine) {
  1621. Status = STATUS_NO_MEMORY;
  1622. }
  1623. pDestState->MachineState.pKeyInfo = pKeyInfoMachine;
  1624. RtlCopyMemory(pDestState->MachineState.pKeyInfo,
  1625. pEnumState->MachineState.pKeyInfo,
  1626. pEnumState->MachineState.cbKeyInfo);
  1627. } else {
  1628. if (pDestState->MachineState.pKeyInfo) {
  1629. pDestState->MachineState.pKeyInfo = (PKEY_NODE_INFORMATION) pDestState->MachineState.KeyInfoBuffer;
  1630. }
  1631. }
  1632. //
  1633. // On error, make sure we clean up.
  1634. //
  1635. if (!NT_SUCCESS(Status)) {
  1636. if (pKeyInfoUser) {
  1637. RegClassHeapFree(pKeyInfoUser);
  1638. }
  1639. if (pKeyInfoMachine) {
  1640. RegClassHeapFree(pKeyInfoMachine);
  1641. }
  1642. }
  1643. return Status;
  1644. }
  1645. void EnumSubtreeStateClear(EnumSubtreeState* pTreeState)
  1646. /*++
  1647. Routine Description:
  1648. This function frees the key data associated with this
  1649. subtree state
  1650. Arguments:
  1651. pTreeState -- tree state to clear
  1652. Return Value: None.
  1653. Note:
  1654. --*/
  1655. {
  1656. //
  1657. // see if we're using pre-alloced buffer -- if not, free it
  1658. //
  1659. if (pTreeState->pKeyInfo && (((LPBYTE) pTreeState->pKeyInfo) != pTreeState->KeyInfoBuffer)) {
  1660. RegClassHeapFree(pTreeState->pKeyInfo);
  1661. }
  1662. pTreeState->pKeyInfo = NULL;
  1663. }
  1664. NTSTATUS EnumSubtreeStateCopyKeyInfo(
  1665. EnumSubtreeState* pTreeState,
  1666. KEY_INFORMATION_CLASS KeyInformationClass,
  1667. PVOID pDestKeyInfo,
  1668. ULONG cbDestKeyInfo,
  1669. PULONG pcbResult)
  1670. /*++
  1671. Routine Description:
  1672. Copies information about a key into a buffer supplied by the caller
  1673. Arguments:
  1674. pTreeState - subtree tate from which to copy
  1675. KeyInformationClass - the type of buffer supplied by the caller -- either
  1676. a KEY_NODE_INFORMATION or KEY_BASIC_INFORMATION structure
  1677. pDestKeyInfo - caller's buffer for key information
  1678. cbDestKeyInfo - size of caller's buffer
  1679. pcbResult - out param -- amount of data to be written to caller's buffer
  1680. Return Value:
  1681. Returns NT_SUCCESS (0) for success; error-code for failure.
  1682. Notes:
  1683. --*/
  1684. {
  1685. ULONG cbNeeded;
  1686. ASSERT((KeyInformationClass == KeyNodeInformation) ||
  1687. (KeyInformationClass == KeyBasicInformation));
  1688. //
  1689. // Find out how big the caller's buffer needs to be. This
  1690. // depends on whether the caller specified full or node information
  1691. // as well as the size of the variable size members of those
  1692. // structures
  1693. //
  1694. if (KeyNodeInformation == KeyInformationClass) {
  1695. PKEY_NODE_INFORMATION pNodeInformation;
  1696. //
  1697. // Copy fixed length pieces first -- caller expects them to
  1698. // be set even when the variable length members are not large enough
  1699. //
  1700. //
  1701. // Set ourselves to point to caller's buffer
  1702. //
  1703. pNodeInformation = (PKEY_NODE_INFORMATION) pDestKeyInfo;
  1704. //
  1705. // Copy all fixed-length pieces of structure
  1706. //
  1707. pNodeInformation->LastWriteTime = pTreeState->pKeyInfo->LastWriteTime;
  1708. pNodeInformation->TitleIndex = pTreeState->pKeyInfo->TitleIndex;
  1709. pNodeInformation->ClassOffset = pTreeState->pKeyInfo->ClassOffset;
  1710. pNodeInformation->ClassLength = pTreeState->pKeyInfo->ClassLength;
  1711. pNodeInformation->NameLength = pTreeState->pKeyInfo->NameLength;
  1712. //
  1713. // Take care of the size of the node information structure
  1714. //
  1715. cbNeeded = sizeof(KEY_NODE_INFORMATION);
  1716. if (cbDestKeyInfo < cbNeeded) {
  1717. return STATUS_BUFFER_TOO_SMALL;
  1718. }
  1719. //
  1720. // Add in the size of the variable length members
  1721. //
  1722. cbNeeded += pTreeState->pKeyInfo->NameLength;
  1723. cbNeeded += pTreeState->pKeyInfo->ClassLength;
  1724. cbNeeded -= sizeof(WCHAR); // the structure's Name member is already set to 1,
  1725. // so that one has already been accounted for in
  1726. // the size of the structure
  1727. } else {
  1728. PKEY_BASIC_INFORMATION pBasicInformation;
  1729. //
  1730. // Copy fixed length pieces first -- caller expects them to
  1731. // be set even when the variable length members are not large enough
  1732. //
  1733. //
  1734. // Set ourselves to point to caller's buffer
  1735. //
  1736. pBasicInformation = (PKEY_BASIC_INFORMATION) pDestKeyInfo;
  1737. //
  1738. // Copy all fixed-length pieces of structure
  1739. //
  1740. pBasicInformation->LastWriteTime = pTreeState->pKeyInfo->LastWriteTime;
  1741. pBasicInformation->TitleIndex = pTreeState->pKeyInfo->TitleIndex;
  1742. pBasicInformation->NameLength = pTreeState->pKeyInfo->NameLength;
  1743. cbNeeded = sizeof(KEY_BASIC_INFORMATION);
  1744. //
  1745. // Take care of the size of the basic information structure
  1746. //
  1747. if (cbDestKeyInfo < cbNeeded) {
  1748. return STATUS_BUFFER_TOO_SMALL;
  1749. }
  1750. //
  1751. // Add in the size of the variable length members
  1752. //
  1753. cbNeeded += pTreeState->pKeyInfo->NameLength;
  1754. cbNeeded -= sizeof(WCHAR); // the structure's Name member is already set to 1,
  1755. // so that one has already been accounted for in
  1756. // the size of the structure
  1757. }
  1758. //
  1759. // Store the amount needed for the caller
  1760. //
  1761. *pcbResult = cbNeeded;
  1762. //
  1763. // See if the caller supplied enough buffer -- leave if not
  1764. //
  1765. if (cbDestKeyInfo < cbNeeded) {
  1766. return STATUS_BUFFER_OVERFLOW;
  1767. }
  1768. //
  1769. // We copy variable-length information differently depending
  1770. // on which type of structure was passsed in
  1771. //
  1772. if (KeyNodeInformation == KeyInformationClass) {
  1773. PBYTE pDestClass;
  1774. PBYTE pSrcClass;
  1775. PKEY_NODE_INFORMATION pNodeInformation;
  1776. pNodeInformation = (PKEY_NODE_INFORMATION) pDestKeyInfo;
  1777. //
  1778. // Copy variable length pieces such as name and class
  1779. //
  1780. RtlCopyMemory(pNodeInformation->Name,
  1781. pTreeState->pKeyInfo->Name,
  1782. pTreeState->pKeyInfo->NameLength);
  1783. //
  1784. // Only copy the class if it exists
  1785. //
  1786. if (((LONG)pTreeState->pKeyInfo->ClassOffset) >= 0) {
  1787. pDestClass = ((PBYTE) pNodeInformation) + pTreeState->pKeyInfo->ClassOffset;
  1788. pSrcClass = ((PBYTE) pTreeState->pKeyInfo) + pTreeState->pKeyInfo->ClassOffset;
  1789. RtlCopyMemory(pDestClass, pSrcClass, pTreeState->pKeyInfo->ClassLength);
  1790. }
  1791. } else {
  1792. PKEY_BASIC_INFORMATION pBasicInformation;
  1793. //
  1794. // Set ourselves to point to caller's buffer
  1795. //
  1796. pBasicInformation = (PKEY_BASIC_INFORMATION) pDestKeyInfo;
  1797. //
  1798. // Copy variable length pieces -- only name is variable length
  1799. //
  1800. RtlCopyMemory(pBasicInformation->Name,
  1801. pTreeState->pKeyInfo->Name,
  1802. pTreeState->pKeyInfo->NameLength);
  1803. }
  1804. return STATUS_SUCCESS;
  1805. }
  1806. NTSTATUS EnumClassKey(
  1807. HKEY hKey,
  1808. EnumSubtreeState* pTreeState)
  1809. /*++
  1810. Routine Description:
  1811. Enumerates a subkey for a subtree state -- calls the kernel
  1812. Arguments:
  1813. hKey - key we want the kernel to enumerate
  1814. pTreeState - subtree state -- either a user or machine subtree
  1815. Return Value:
  1816. Returns NT_SUCCESS (0) for success; error-code for failure.
  1817. Notes:
  1818. --*/
  1819. {
  1820. PKEY_NODE_INFORMATION pCurrentKeyInfo;
  1821. NTSTATUS Status;
  1822. ASSERT(!(pTreeState->pKeyInfo));
  1823. //
  1824. // First try to use the buffer built in to the subtree state
  1825. //
  1826. pCurrentKeyInfo = (PKEY_NODE_INFORMATION) pTreeState->KeyInfoBuffer;
  1827. //
  1828. // Query for the necessary information about the supplied key.
  1829. //
  1830. Status = NtEnumerateKey( hKey,
  1831. pTreeState->iSubKey,
  1832. KeyNodeInformation,
  1833. pCurrentKeyInfo,
  1834. sizeof(pTreeState->KeyInfoBuffer),
  1835. &(pTreeState->cbKeyInfo));
  1836. ASSERT( Status != STATUS_BUFFER_TOO_SMALL );
  1837. //
  1838. // If the subtree state's buffer isn't big enough, we'll have
  1839. // to ask the heap to give us one.
  1840. //
  1841. if (STATUS_BUFFER_OVERFLOW == Status) {
  1842. pCurrentKeyInfo = RegClassHeapAlloc(pTreeState->cbKeyInfo);
  1843. //
  1844. // If the memory allocation fails, return a Registry Status.
  1845. //
  1846. if( ! pCurrentKeyInfo ) {
  1847. return STATUS_NO_MEMORY;
  1848. }
  1849. //
  1850. // Query for the necessary information about the supplied key.
  1851. //
  1852. Status = NtEnumerateKey( hKey,
  1853. pTreeState->iSubKey,
  1854. KeyNodeInformation,
  1855. pCurrentKeyInfo,
  1856. pTreeState->cbKeyInfo,
  1857. &(pTreeState->cbKeyInfo));
  1858. }
  1859. if (!NT_SUCCESS(Status)) {
  1860. return Status;
  1861. }
  1862. //
  1863. // set the subtree state's reference to point
  1864. // to the location of the data
  1865. //
  1866. pTreeState->pKeyInfo = pCurrentKeyInfo;
  1867. return STATUS_SUCCESS;
  1868. }
  1869. NTSTATUS GetSubKeyCount(
  1870. HKEY hkClassKey,
  1871. LPDWORD pdwUserSubKeys)
  1872. /*++
  1873. Routine Description:
  1874. Counts the number of subkeys under a key
  1875. Arguments:
  1876. hkClassKey - key whose subkeys we wish to count
  1877. pdwUserSubKeys - out param for number of subkeys
  1878. Return Value:
  1879. Returns NT_SUCCESS (0) for success; error-code for failure.
  1880. Notes:
  1881. --*/
  1882. {
  1883. NTSTATUS Status;
  1884. PKEY_CACHED_INFORMATION KeyCachedInfo;
  1885. ULONG BufferLength;
  1886. BYTE PrivateKeyCachedInfo[ sizeof( KEY_CACHED_INFORMATION ) ];
  1887. //
  1888. // Initialize out params
  1889. //
  1890. *pdwUserSubKeys = 0;
  1891. //
  1892. // Set up to query kernel for subkey information
  1893. //
  1894. KeyCachedInfo = (PKEY_CACHED_INFORMATION) PrivateKeyCachedInfo;
  1895. BufferLength = sizeof(PrivateKeyCachedInfo);
  1896. Status = QueryKeyInfo(
  1897. hkClassKey,
  1898. KeyCachedInformation,
  1899. &KeyCachedInfo,
  1900. BufferLength,
  1901. FALSE,
  1902. 0
  1903. );
  1904. if (NT_SUCCESS(Status)) {
  1905. //
  1906. // set the out param with the subkey data from the kernel call
  1907. //
  1908. *pdwUserSubKeys = KeyCachedInfo->SubKeys;
  1909. ASSERT( KeyCachedInfo == ( PKEY_CACHED_INFORMATION )PrivateKeyCachedInfo );
  1910. }
  1911. return Status;
  1912. }
  1913. NTSTATUS ClassKeyCountSubKeys(
  1914. HKEY hKey,
  1915. HKEY hkUser,
  1916. HKEY hkMachine,
  1917. DWORD cMax,
  1918. LPDWORD pcSubKeys)
  1919. /*++
  1920. Routine Description:
  1921. Counts the total number of subkeys of a special key -- i.e.
  1922. the sum of the subkeys in the user and machine portions
  1923. of that special key minus duplicates.
  1924. Arguments:
  1925. hkUser - user part of special key
  1926. hkMachine - machine part of special key
  1927. cMax - Maximum number of keys to count -- if
  1928. zero, this is ignored
  1929. pcSubKeys - out param -- count of subkeys
  1930. Return Value:
  1931. Returns NT_SUCCESS (0) for success; error-code for failure.
  1932. Notes:
  1933. This is INCREDIBLY expensive if either hkUser or hkMachine
  1934. has more than a few subkeys. It essentially merges two
  1935. sorted lists by enumerating in both the user and machine
  1936. locations, and viewing them as a merged list by doing
  1937. comparisons betweens items in each list --
  1938. separate user and machine pointers are advanced according
  1939. to the results of the comparison. This means that if there are
  1940. N keys under hkUser and M keys under hkMachine, this function
  1941. will make N+M calls to the kernel to enumerate the keys.
  1942. This is currently the only way to do this -- before, an approximation
  1943. was used in which the sum of the number of subkeys in the
  1944. user and machine versions was returned. This method didn't take
  1945. duplicates into account, and so it overestimated the number of keys.
  1946. This was not thought to be a problem since there is no guarantee
  1947. to callers that the number they receive is completely up to date,
  1948. but it turns out that there are applications that make that assumption
  1949. (such as regedt32) that do not function properly unless the
  1950. exact number is returned.
  1951. --*/
  1952. {
  1953. NTSTATUS Status;
  1954. BOOL fCheckUser;
  1955. BOOL fCheckMachine;
  1956. EnumSubtreeState UserTree;
  1957. EnumSubtreeState MachineTree;
  1958. DWORD cMachineKeys;
  1959. DWORD cUserKeys;
  1960. OBJECT_ATTRIBUTES Obja;
  1961. HKEY hkUserCount;
  1962. HKEY hkMachineCount;
  1963. HKEY hkNewKey;
  1964. UNICODE_STRING EmptyString = {0, 0, 0};
  1965. Status = STATUS_SUCCESS;
  1966. hkNewKey = NULL;
  1967. cMachineKeys = 0;
  1968. cUserKeys = 0;
  1969. //
  1970. // Initialize ourselves to check in both the user
  1971. // and machine hives for subkeys
  1972. //
  1973. fCheckUser = (hkUser != NULL);
  1974. fCheckMachine = (hkMachine != NULL);
  1975. memset(&UserTree, 0, sizeof(UserTree));
  1976. memset(&MachineTree, 0, sizeof(MachineTree));
  1977. //
  1978. // We can't be sure that the user key was opened
  1979. // with the right permissions so we'll open
  1980. // a version that has the correct permissions
  1981. //
  1982. if (fCheckUser && (hkUser == hKey)) {
  1983. InitializeObjectAttributes(
  1984. &Obja,
  1985. &EmptyString,
  1986. OBJ_CASE_INSENSITIVE,
  1987. hkUser,
  1988. NULL);
  1989. Status = NtOpenKey(
  1990. &hkNewKey,
  1991. KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
  1992. &Obja);
  1993. if (!NT_SUCCESS(Status)) {
  1994. return Status;
  1995. }
  1996. hkUserCount = hkNewKey;
  1997. } else {
  1998. hkUserCount = hkUser;
  1999. }
  2000. if (fCheckMachine && (hkMachine == hKey)) {
  2001. InitializeObjectAttributes(
  2002. &Obja,
  2003. &EmptyString,
  2004. OBJ_CASE_INSENSITIVE,
  2005. hkMachine,
  2006. NULL);
  2007. Status = NtOpenKey(
  2008. &hkNewKey,
  2009. KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
  2010. &Obja);
  2011. if (!NT_SUCCESS(Status)) {
  2012. return Status;
  2013. }
  2014. hkMachineCount = hkNewKey;
  2015. } else {
  2016. hkMachineCount = hkMachine;
  2017. }
  2018. //
  2019. // Now check to see how many keys are in the user subtree
  2020. //
  2021. if (fCheckUser) {
  2022. Status = GetSubKeyCount(hkUserCount, &cUserKeys);
  2023. if (!NT_SUCCESS(Status)) {
  2024. goto cleanup;
  2025. }
  2026. //
  2027. // We only need to enumerate the user portion if it has subkeys
  2028. //
  2029. fCheckUser = (cUserKeys != 0);
  2030. }
  2031. //
  2032. // Now check to see how many keys are in the user subtree
  2033. //
  2034. if (fCheckMachine) {
  2035. Status = GetSubKeyCount(hkMachineCount, &cMachineKeys);
  2036. if (!NT_SUCCESS(Status)) {
  2037. goto cleanup;
  2038. }
  2039. //
  2040. // We only need to enumerate the machine portion if it has subkeys
  2041. //
  2042. fCheckMachine = (cMachineKeys != 0);
  2043. }
  2044. if (!fCheckUser) {
  2045. *pcSubKeys = cMachineKeys;
  2046. Status = STATUS_SUCCESS;
  2047. goto cleanup;
  2048. }
  2049. if (!fCheckMachine) {
  2050. *pcSubKeys = cUserKeys;
  2051. Status = STATUS_SUCCESS;
  2052. goto cleanup;
  2053. }
  2054. ASSERT(fCheckMachine && fCheckUser);
  2055. *pcSubKeys = 0;
  2056. //
  2057. // Keep enumerating subkeys until one of the locations
  2058. // runs out of keys
  2059. //
  2060. for (;;) {
  2061. NTSTATUS EnumStatus;
  2062. //
  2063. // If we can still check in the user hive and we
  2064. // are missing user key info, query the kernel for it
  2065. //
  2066. if (!(UserTree.pKeyInfo)) {
  2067. EnumStatus = EnumClassKey(
  2068. hkUserCount,
  2069. &UserTree);
  2070. //
  2071. // If there are no more user subkeys, set our
  2072. // flag so that we no longer look in the user portion
  2073. // for subkeys
  2074. //
  2075. if (!NT_SUCCESS(EnumStatus)) {
  2076. if (STATUS_NO_MORE_ENTRIES == EnumStatus) {
  2077. *pcSubKeys += cMachineKeys;
  2078. Status = STATUS_SUCCESS;
  2079. break;
  2080. } else {
  2081. Status = EnumStatus;
  2082. break;
  2083. }
  2084. }
  2085. }
  2086. //
  2087. // if we can still check in the machine hive and
  2088. // we are missing machine info, query for it
  2089. //
  2090. if (!(MachineTree.pKeyInfo)) {
  2091. EnumStatus = EnumClassKey(
  2092. hkMachineCount,
  2093. &MachineTree);
  2094. //
  2095. // Turn off checking in machine if there are
  2096. // no more machine keys
  2097. //
  2098. if (!NT_SUCCESS(EnumStatus)) {
  2099. if (STATUS_NO_MORE_ENTRIES == EnumStatus) {
  2100. *pcSubKeys += cUserKeys;
  2101. Status = STATUS_SUCCESS;
  2102. break;
  2103. } else {
  2104. Status = EnumStatus;
  2105. break;
  2106. }
  2107. }
  2108. }
  2109. //
  2110. // If we have keys in both user and machine, we need to compare
  2111. // the key names to see when to advance our subtree pointers
  2112. //
  2113. {
  2114. LONG lCompare;
  2115. UNICODE_STRING MachineKeyName;
  2116. UNICODE_STRING UserKeyName;
  2117. MachineKeyName.Buffer = MachineTree.pKeyInfo->Name;
  2118. MachineKeyName.Length = (USHORT) MachineTree.pKeyInfo->NameLength;
  2119. UserKeyName.Buffer = UserTree.pKeyInfo->Name;
  2120. UserKeyName.Length = (USHORT) UserTree.pKeyInfo->NameLength;
  2121. //
  2122. // Do the comparison of user and machine keys
  2123. //
  2124. lCompare =
  2125. RtlCompareUnicodeString(&UserKeyName, &MachineKeyName, TRUE);
  2126. //
  2127. // User is smaller, so move our user pointer up and clear it
  2128. // so we'll query for user data next time
  2129. //
  2130. if (lCompare <= 0) {
  2131. EnumSubtreeStateClear(&UserTree);
  2132. UserTree.iSubKey++;
  2133. cUserKeys--;
  2134. }
  2135. //
  2136. // Machine is smaller, so move our user pointer up and clear it
  2137. // so we'll query for machine data next time
  2138. //
  2139. if (lCompare >= 0) {
  2140. EnumSubtreeStateClear(&MachineTree);
  2141. MachineTree.iSubKey++;
  2142. cMachineKeys--;
  2143. }
  2144. //
  2145. // Increase the total number of subkeys
  2146. //
  2147. (*pcSubKeys)++;
  2148. }
  2149. //
  2150. // Only enumerate up to max -- the caller
  2151. // doesn't need to go all the way to the end
  2152. //
  2153. if (cMax && (*pcSubKeys > cMax)) {
  2154. break;
  2155. }
  2156. }
  2157. //
  2158. // Free any buffer held by these subtree states
  2159. //
  2160. EnumSubtreeStateClear(&UserTree);
  2161. EnumSubtreeStateClear(&MachineTree);
  2162. cleanup:
  2163. if (hkNewKey) {
  2164. NtClose(hkNewKey);
  2165. }
  2166. return Status;
  2167. }
  2168. #endif // LOCAL