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.

1374 lines
60 KiB

  1. /****************************************************************************************
  2. * MSI will call these APIs to ask TS to propogate changes from .Default to the TS hive.
  3. *
  4. * NTSTATUS TermServPrepareAppInstallDueMSI()
  5. *
  6. * NTSTATUS TermServProcessAppIntallDueMSI( BOOLEAN cleanup )
  7. *
  8. * These API need not be called in the same boot cycles, many boot cycles could
  9. * happen in between.
  10. *
  11. * Copyright (C) 1997-1999 Microsoft Corp.
  12. ****************************************************************************************/
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <ntexapi.h>
  17. #include <stdio.h>
  18. #include <windows.h>
  19. #include <winuser.h>
  20. #include <stdlib.h>
  21. #include <tsappcmp.h>
  22. #include <stdio.h>
  23. #include <fcntl.h>
  24. #include "KeyNode.h"
  25. #include "ValInfo.h"
  26. // real externs!
  27. extern "C" {
  28. void TermsrvLogRegInstallTime(void);
  29. }
  30. extern "C" {
  31. BOOL HKeyExistsInOmissionList(HKEY hKeyToCheck);
  32. }
  33. extern "C" {
  34. BOOL RegPathExistsInOmissionList(PWCHAR pwchKeyToCheck);
  35. }
  36. // forward declaration
  37. extern NTSTATUS DeleteReferenceHive(WCHAR *);
  38. extern NTSTATUS CreateReferenceHive( WCHAR *, WCHAR *);
  39. extern NTSTATUS DeltaDeleteKeys(WCHAR *, WCHAR *, WCHAR *);
  40. extern NTSTATUS DeltaUpdateKeys(WCHAR *, WCHAR *, WCHAR *);
  41. ULONG g_length_TERMSRV_USERREGISTRY_DEFAULT;
  42. ULONG g_length_TERMSRV_INSTALL;
  43. WCHAR g_debugFileName[MAX_PATH];
  44. FILE *g_debugFilePointer=NULL;
  45. BOOLEAN g_debugIO = FALSE;
  46. BOOLEAN KeyNode::debug=FALSE; // init the static
  47. #define TERMSRV_USERREGISTRY_DEFAULT TEXT("\\Registry\\USER\\.Default")
  48. // This is for debug I/O, output looks better with it.
  49. void Indent( ULONG indent)
  50. {
  51. for ( ULONG i = 1; i <indent ; i++ )
  52. {
  53. fwprintf( g_debugFilePointer, L" ");
  54. }
  55. }
  56. // a key name is written to the log file based on the contect of pBasicInfo
  57. void DebugKeyStamp( NTSTATUS status, KeyBasicInfo *pBasicInfo, int indent , WCHAR *pComments=L"" )
  58. {
  59. Indent(indent);
  60. fwprintf( g_debugFilePointer,L"%ws, status=%lx, %ws\n",pBasicInfo->NameSz(), status , pComments);
  61. fflush( g_debugFilePointer );
  62. DbgPrint("%ws\n",pBasicInfo->NameSz());
  63. }
  64. // a debug stamp is written to the log file, including the line number where error happened
  65. void DebugErrorStamp(NTSTATUS status , int lineNumber, ValueFullInfo *pValue=NULL)
  66. {
  67. fwprintf( g_debugFilePointer,
  68. L"ERROR ?!? status = %lx, linenumber:%d\n", status, lineNumber);
  69. if (pValue)
  70. {
  71. pValue->Print(g_debugFilePointer);
  72. }
  73. fflush(g_debugFilePointer);
  74. }
  75. #define KEY_IGNORED L"[key was ignored]"
  76. #define NO_KEY_HANDLE L"[No handle, key ignored]"
  77. void DebugInfo(NTSTATUS status , int lineNumber, KeyNode *pKey, WCHAR *comment)
  78. {
  79. if(g_debugIO && g_debugFilePointer)
  80. {
  81. fwprintf( g_debugFilePointer,
  82. L"ERROR ?!? status = %lx, linenumber:%d, KeyNode name=%ws, %ws\n", status,
  83. lineNumber , pKey->Name() , comment);
  84. fflush(g_debugFilePointer);
  85. }
  86. }
  87. // use this func to track the status value which is used to bail out in
  88. // case of an error.
  89. // this is only used in teh debug build, see below
  90. BOOL NT_SUCCESS_OR_ERROR_STAMP( NTSTATUS status, ULONG lineNumber)
  91. {
  92. if ( g_debugIO )
  93. {
  94. if ( ( (ULONG)status) >=0xC0000000 )
  95. {
  96. DebugErrorStamp( status, lineNumber );
  97. }
  98. }
  99. return ( (NTSTATUS)(status) >= 0 );
  100. }
  101. #ifdef DBG
  102. #define NT_SUCCESS_EX(Status) NT_SUCCESS_OR_ERROR_STAMP( (Status), __LINE__ )
  103. #define DEBUG_INFO(Status, pKey, comment ) DebugInfo( Status, __LINE__ , pKey , comment)
  104. #else
  105. #define NT_SUCCESS_EX(Status) NT_SUCCESS(Status)
  106. #define DEBUG_INFO(Stats, pKey , comment)
  107. #endif
  108. /***************************************************************************
  109. *
  110. * All three branch-walker functions use this method to alter the status code, and
  111. * if necessary, log an error message to the log file
  112. *
  113. ***************************************************************************/
  114. NTSTATUS AlterStatus( NTSTATUS status , int lineNumber )
  115. {
  116. switch( status )
  117. {
  118. case STATUS_ACCESS_DENIED:
  119. // this should never happen since we run in the system context
  120. if ( g_debugIO )
  121. {
  122. DebugErrorStamp( status, lineNumber );
  123. }
  124. status = STATUS_SUCCESS;
  125. break;
  126. case STATUS_SUCCESS:
  127. break;
  128. case STATUS_NO_MORE_ENTRIES:
  129. status = STATUS_SUCCESS;
  130. break;
  131. default:
  132. if ( g_debugIO )
  133. {
  134. DebugErrorStamp( status, lineNumber );
  135. }
  136. break;
  137. }
  138. return status;
  139. }
  140. /******************************************************************************
  141. *
  142. * Based on a special reg key/value init the debug flags and pointers which are
  143. * used to log debug info into a log file. When called with start=TRUE, the
  144. * relevant data structs are initialized. When called with start=FALSE, the log
  145. * file is closed.
  146. *
  147. ******************************************************************************/
  148. void InitDebug( BOOLEAN start)
  149. {
  150. if ( start )
  151. {
  152. KeyNode tsHiveNode (NULL, KEY_READ, TERMSRV_INSTALL );
  153. if ( NT_SUCCESS( tsHiveNode.Open() ) )
  154. {
  155. ValuePartialInfo debugValue( &tsHiveNode );
  156. if ( NT_SUCCESS( debugValue.Status() ) && NT_SUCCESS( debugValue.Query(L"TS_MSI_DEBUG") ) )
  157. {
  158. g_debugIO = TRUE;
  159. KeyNode::debug=TRUE;
  160. for (ULONG i =0; i < debugValue.Ptr()->DataLength/sizeof(WCHAR); i++)
  161. {
  162. g_debugFileName[i] = ((WCHAR*)(debugValue.Ptr()->Data))[i];
  163. }
  164. g_debugFileName[i] = L'\0';
  165. g_debugFilePointer = _wfopen( g_debugFileName, L"a+" );
  166. fwprintf( g_debugFilePointer, L"----\n");
  167. }
  168. }
  169. }
  170. else
  171. {
  172. if ( g_debugFilePointer )
  173. {
  174. fclose(g_debugFilePointer);
  175. }
  176. }
  177. }
  178. /***************************************************************************
  179. *
  180. * Function:
  181. * TermServPrepareAppInstallDueMSI()
  182. *
  183. * Description:
  184. * MSI service calls this function prior to starting an installation cycle.
  185. * When called, this function blows away the RefHive (in case it was around
  186. * with some stale data...), and then it creates a fresh copy of
  187. * .Default\Software as the new RefHive.
  188. *
  189. * Return:
  190. * NTSTATUS
  191. *
  192. ***************************************************************************/
  193. NTSTATUS TermServPrepareAppInstallDueMSI()
  194. {
  195. NTSTATUS status = STATUS_SUCCESS;
  196. WCHAR sourceHive[MAX_PATH];
  197. WCHAR referenceHive[MAX_PATH];
  198. WCHAR destinationHive[MAX_PATH];
  199. wcscpy(sourceHive, TERMSRV_USERREGISTRY_DEFAULT );
  200. wcscat(sourceHive, L"\\Software");
  201. g_length_TERMSRV_USERREGISTRY_DEFAULT = wcslen( TERMSRV_USERREGISTRY_DEFAULT );
  202. wcscpy(referenceHive, TERMSRV_INSTALL );
  203. wcscat(referenceHive, L"\\RefHive");
  204. g_length_TERMSRV_INSTALL = wcslen( TERMSRV_INSTALL );
  205. InitDebug( TRUE );
  206. if ( g_debugIO)
  207. {
  208. fwprintf( g_debugFilePointer,L"In %ws\n",
  209. L"TermServPrepareAppInstallDueMSI");
  210. fflush( g_debugFilePointer );
  211. }
  212. // delete the existing hive (if any )
  213. status = DeleteReferenceHive( referenceHive );
  214. if ( NT_SUCCESS( status ) )
  215. {
  216. // 1-COPY
  217. // copy all keys under .Default\Software into a special location
  218. // under our TS hive, let's call it the RefHive
  219. status = CreateReferenceHive(sourceHive, referenceHive);
  220. }
  221. InitDebug( FALSE );
  222. return status;
  223. }
  224. /**********************************************************************************
  225. *
  226. * Function:
  227. * TermServProcessAppInstallDueMSI
  228. *
  229. * Description:
  230. * MSI service calls this function after calling TermServPrepareAppInstallDueMSI(),
  231. * and after MSI finishing making an installation which updated the .Default
  232. * hive (since MSI runs in the system context).
  233. * This function will compare the content of .Default\SW to RefHive and then
  234. * first it will create all new (missing) keys and values. Then it will
  235. * compare any existing keys from .Default\SW with the equivalent RefHive, and
  236. * if value is different, it will delete the equivalent value from our TS hive
  237. * and then create a new value identical to what was found in .Default
  238. *
  239. * Return:
  240. * NTSTATUS
  241. *
  242. **********************************************************************************/
  243. NTSTATUS TermServProcessAppInstallDueMSI( BOOLEAN cleanup)
  244. {
  245. NTSTATUS status = STATUS_SUCCESS;
  246. WCHAR sourceHive[MAX_PATH];
  247. WCHAR referenceHive[MAX_PATH];
  248. WCHAR destinationHive[MAX_PATH];
  249. wcscpy(sourceHive, TERMSRV_USERREGISTRY_DEFAULT );
  250. wcscat(sourceHive, L"\\Software");
  251. g_length_TERMSRV_USERREGISTRY_DEFAULT = wcslen( TERMSRV_USERREGISTRY_DEFAULT );
  252. wcscpy(referenceHive, TERMSRV_INSTALL );
  253. wcscat(referenceHive, L"\\RefHive");
  254. g_length_TERMSRV_INSTALL = wcslen( TERMSRV_INSTALL );
  255. wcscpy(destinationHive, TERMSRV_INSTALL );
  256. wcscat(destinationHive, L"\\Software");
  257. InitDebug( TRUE );
  258. if ( g_debugIO)
  259. {
  260. fwprintf( g_debugFilePointer,L"In %ws, cleanup=%lx\n",
  261. L"TermServProcessAppIntallDueMSI", cleanup);
  262. fflush( g_debugFilePointer );
  263. }
  264. if ( !cleanup )
  265. {
  266. // 2-DELETE
  267. // compare .Dfeault keys to the equivalent keys in RefHive. If keys are
  268. // missing from .Default, then delete the equivalent keys from our
  269. // HKLM\...\TS\ hive
  270. status = DeltaDeleteKeys(sourceHive, referenceHive, destinationHive);
  271. if (NT_SUCCESS( status ) )
  272. {
  273. // Steps 3 and 4 are now combined.
  274. // 3-CREATE
  275. // compare .Default keys to the equivalent keys in RefHive, if keys are
  276. // present in .Default that are missing from RefHive, then, add those keys
  277. // to our HKLM\...\TS hive
  278. // 4-CHANGE
  279. // compare keys of .Default to RefHive. Those keys that are newer than
  280. // RefHive, then, update the equivalent keys in HKLM\...\TS
  281. status = DeltaUpdateKeys(sourceHive, referenceHive, destinationHive);
  282. if (NT_SUCCESS( status ))
  283. {
  284. // update the time stamp in our hive since we want the standared TS reg key
  285. // propogation to take place.
  286. TermsrvLogRegInstallTime();
  287. }
  288. }
  289. }
  290. else
  291. {
  292. // blow away the existing reference hive,
  293. status = DeleteReferenceHive( referenceHive );
  294. }
  295. InitDebug( FALSE );
  296. return status;
  297. }
  298. /*******************************************************************
  299. *
  300. * Function:
  301. * EnumerateAndCreateRefHive
  302. *
  303. * Parameters:
  304. * pSource points to the parent node, the branch we copy
  305. * pref points to our RefHive which we are creating as a ref image
  306. * pBasicInfo is a scratch pad passed around which is used to
  307. * extract basic Key information
  308. * pindextLevel is used to format the debug log output file
  309. *
  310. * Descritption:
  311. * Create a copy of the .Default\Sofwtare as our RefHive
  312. *
  313. * Return:
  314. * NTSTATUS
  315. *******************************************************************/
  316. NTSTATUS EnumerateAndCreateRefHive(
  317. IN KeyNode *pSource,
  318. IN KeyNode *pRef,
  319. IN KeyBasicInfo *pBasicInfo,
  320. IN ULONG *pIndentLevel
  321. )
  322. {
  323. NTSTATUS status=STATUS_SUCCESS;
  324. ULONG ulCount=0;
  325. UNICODE_STRING UniString;
  326. (*pIndentLevel)++;
  327. while( NT_SUCCESS(status))
  328. {
  329. ULONG ultemp;
  330. status = NtEnumerateKey( pSource->Key(),
  331. ulCount++,
  332. pBasicInfo->Type() , // keyInformationClass,
  333. pBasicInfo->Ptr(), // pKeyInfo,
  334. pBasicInfo->Size(), // keyInfoSize,
  335. &ultemp);
  336. if (NT_SUCCESS(status))
  337. {
  338. if ( g_debugIO)
  339. {
  340. DebugKeyStamp( status , pBasicInfo, *pIndentLevel );
  341. }
  342. // open a sub key
  343. KeyNode SourceSubKey( pSource, pBasicInfo);
  344. // create the Ref sub key
  345. KeyNode RefSubKey( pRef, pBasicInfo);
  346. if (NT_SUCCESS_EX( status = SourceSubKey.Open() ) )
  347. {
  348. if ( NT_SUCCESS_EX( status = RefSubKey.Create() ) )
  349. {
  350. NTSTATUS status3;
  351. KEY_FULL_INFORMATION *ptrInfo;
  352. ULONG size;
  353. if (NT_SUCCESS(SourceSubKey.Query( &ptrInfo, &size )))
  354. {
  355. ValueFullInfo valueFullInfo( &SourceSubKey );
  356. ValueFullInfo RefValue( &RefSubKey );
  357. if ( NT_SUCCESS_EX( status = valueFullInfo.Status())
  358. && NT_SUCCESS_EX(status = RefValue.Status()) )
  359. {
  360. for (ULONG ulkey = 0; ulkey < ptrInfo->Values; ulkey++)
  361. {
  362. status = NtEnumerateValueKey(SourceSubKey.Key(),
  363. ulkey,
  364. valueFullInfo.Type(),
  365. valueFullInfo.Ptr(),
  366. valueFullInfo.Size(),
  367. &ultemp);
  368. if (NT_SUCCESS( status ))
  369. {
  370. status = RefValue.Create( &valueFullInfo );
  371. // if status is not good, we bail out, since var "status" is set here
  372. }
  373. // else, no more entries left, we continue
  374. }
  375. }
  376. // else, out of memory, status is set, we bail out.
  377. }
  378. // else, no values are present, continue with sub-key enums
  379. if (NT_SUCCESS( status ) )
  380. {
  381. // enumerate sub key down.
  382. status = EnumerateAndCreateRefHive(
  383. &SourceSubKey,
  384. &RefSubKey,
  385. pBasicInfo,
  386. pIndentLevel
  387. );
  388. }
  389. }
  390. // else, an error, status is set, so we bail out
  391. }// else, open on source has failed, var-status is set, we bail out
  392. status = AlterStatus( status, __LINE__ );
  393. // else, an error, status is set, so we bail out
  394. }
  395. // else, no more left
  396. }
  397. (*pIndentLevel)--;
  398. return( status );
  399. }
  400. /*******************************************************************
  401. *
  402. * Function:
  403. * EnumerateAndDeltaDeleteKeys
  404. *
  405. * Parameters:
  406. * pSource points to a node under .Dfeault
  407. * pref points to a node under our RefHive
  408. * pDestination is a node under our TS\install\SW hive
  409. * pBasicInfo is a scratch pad passed around which is used to
  410. * extract basic Key information
  411. * pindextLevel is used to format the debug log output file
  412. *
  413. * Descritption:
  414. * compare source to ref, if keys/values in source are deleted, then
  415. * delete the equivalent key/value from destination
  416. *
  417. * Return:
  418. * NTSTATUS
  419. *******************************************************************/
  420. NTSTATUS EnumerateAndDeltaDeleteKeys(
  421. IN KeyNode *pSource, // this is under the latest updated .Default\SW hive
  422. IN KeyNode *pRef, // this was a ref-copy of .Default\SW before the update
  423. IN KeyNode *pDestination,// this is opur private TS-hive
  424. IN KeyBasicInfo *pBasicInfo,
  425. IN ULONG *pIndentLevel)
  426. {
  427. NTSTATUS status=STATUS_SUCCESS, st2;
  428. ULONG ulCount=0;
  429. UNICODE_STRING UniString;
  430. ULONG size;
  431. (*pIndentLevel)++;
  432. while (NT_SUCCESS(status))
  433. {
  434. ULONG ultemp;
  435. status = NtEnumerateKey( pRef->Key(),
  436. ulCount++,
  437. pBasicInfo->Type() , // keyInformationClass,
  438. pBasicInfo->Ptr(), // pKeyInfo,
  439. pBasicInfo->Size(), // keyInfoSize,
  440. &ultemp);
  441. // pBasicInfo was filled up thru NtEnumerateKey() above
  442. if (NT_SUCCESS(status))
  443. {
  444. if ( g_debugIO)
  445. {
  446. DebugKeyStamp( status , pBasicInfo, *pIndentLevel );
  447. }
  448. KeyNode RefSubKey( pRef, pBasicInfo);
  449. KeyNode SourceSubKey( pSource,pBasicInfo);
  450. KeyNode DestinationSubKey( pDestination, pBasicInfo);
  451. RefSubKey.Open();
  452. SourceSubKey.Open();
  453. DestinationSubKey.Open();
  454. if (NT_SUCCESS( RefSubKey.Status() ) )
  455. {
  456. if ( ! NT_SUCCESS( SourceSubKey.Status () ) )
  457. {
  458. // key is missing from the .Default\SW hive, we should delete
  459. // the same sub-tree from our TS\Install\SW hive
  460. if ( NT_SUCCESS( DestinationSubKey.Status()) )
  461. {
  462. if (!HKeyExistsInOmissionList((HKEY)(DestinationSubKey.Key())))
  463. {
  464. DestinationSubKey.DeleteSubKeys();
  465. NTSTATUS st = DestinationSubKey.Delete();
  466. if ( g_debugIO)
  467. {
  468. DebugKeyStamp( st, pBasicInfo, *pIndentLevel );
  469. }
  470. }
  471. else
  472. {
  473. DEBUG_INFO( status, &DestinationSubKey , KEY_IGNORED );
  474. }
  475. }
  476. // else
  477. // As long as the key is missing from
  478. // Ts\install\Hive, we will regard this condition as acceptable.
  479. }
  480. else
  481. {
  482. // see if any values have been deleted
  483. // don't bother unless the destination key exists, otherwise, no values
  484. // will be there to delete...
  485. if ( NT_SUCCESS( DestinationSubKey.Status() ) )
  486. {
  487. KEY_FULL_INFORMATION *ptrInfo;
  488. ULONG size;
  489. if (NT_SUCCESS_EX(status = RefSubKey.Query( &ptrInfo, &size )))
  490. {
  491. // from the key-full-information, create a key-value-full-information
  492. ValueFullInfo refValueFullInfo( &RefSubKey );
  493. ValueFullInfo sourceValue( &SourceSubKey );
  494. // if no allocation errors, then...
  495. if ( NT_SUCCESS_EX( status = refValueFullInfo.Status() )
  496. && NT_SUCCESS_EX( status = sourceValue.Status() ) )
  497. {
  498. for (ULONG ulkey = 0; ulkey < ptrInfo->Values; ulkey++)
  499. {
  500. if ( NT_SUCCESS_EX (
  501. status = NtEnumerateValueKey(RefSubKey.Key(),
  502. ulkey,
  503. refValueFullInfo.Type(),
  504. refValueFullInfo.Ptr(),
  505. refValueFullInfo.Size(),
  506. &ultemp) ) )
  507. {
  508. // for every value, see if the same value
  509. // exists in the SourceSubKey. If it doesn't
  510. // then delete the corresponding value from
  511. // TS's hive
  512. sourceValue.Query( refValueFullInfo.SzName() );
  513. // if .Default\SW is missing a value, then delete the
  514. // corresponding value from our TS\ hive
  515. if ( sourceValue.Status() == STATUS_OBJECT_NAME_NOT_FOUND )
  516. {
  517. ValuePartialInfo destinationValue( &DestinationSubKey);
  518. if (NT_SUCCESS_EX( status = destinationValue.Status () ) )
  519. {
  520. if (!HKeyExistsInOmissionList((HKEY)(DestinationSubKey.Key())))
  521. {
  522. destinationValue.Delete( refValueFullInfo.SzName() );
  523. }
  524. else
  525. {
  526. DEBUG_INFO( status, &DestinationSubKey , KEY_IGNORED );
  527. }
  528. }
  529. // else, alloc error, status is set
  530. }
  531. else
  532. {
  533. if ( !NT_SUCCESS_EX ( status = sourceValue.Status() ) )
  534. {
  535. if ( g_debugIO )
  536. {
  537. DebugErrorStamp(status, __LINE__ );
  538. }
  539. // else, we will bail out here since var-status is set
  540. }
  541. // else, no error
  542. }
  543. // if-else
  544. }
  545. // else, no more entries
  546. } // for loop
  547. }
  548. // else, we have an error due to no memory, var-status is set
  549. }
  550. // else, we have an error since we can not get info on this existing ref key, var-status is set
  551. if ( NT_SUCCESS( status ) )
  552. {
  553. // we were able to open the source key, which means that
  554. // key was not deleted from .default.
  555. // so keep enuming away...
  556. status = EnumerateAndDeltaDeleteKeys(
  557. &SourceSubKey,
  558. &RefSubKey,
  559. &DestinationSubKey,
  560. pBasicInfo ,
  561. pIndentLevel);
  562. }
  563. //else, status is bad, no point to traverse, we are bailing out
  564. }
  565. //else, there is no destination sub key to bother with deletion
  566. }
  567. // if-else
  568. }
  569. // else, ref had no more sub-keys
  570. status = AlterStatus( status, __LINE__ );
  571. }
  572. // else, no more entries
  573. }
  574. (*pIndentLevel)--;
  575. // the typical status would be: STATUS_NO_MORE_ENTRIES
  576. return status;
  577. }
  578. /*******************************************************************
  579. *
  580. * Function:
  581. * EnumerateAndDeltaUpdateKeys
  582. *
  583. * Parameters:
  584. * pSource points to a node under .Dfeault
  585. * pref points to a node under our RefHive
  586. * pDestination is a node under our TS\install\SW hive
  587. * pBasicInfo is a scratch pad passed around which is used to
  588. * extract basic Key information
  589. * pindextLevel is used to format the debug log output file
  590. *
  591. * Descritption:
  592. * compare source to ref, if new keys/values in source have been created
  593. * then create the equivalent keys in our Ts\Install\SW branch (pDestination)
  594. * Also, check all values in pSource to values in pRef, if not the same
  595. * then delete the equivalent pDestination and create a new value
  596. * identical to value from pSource
  597. *
  598. * Return:
  599. * NTSTATUS
  600. *******************************************************************/
  601. NTSTATUS EnumerateAndDeltaUpdateKeys(
  602. IN KeyNode *pSource, // this is under the latest updated .Default\SW hive
  603. IN KeyNode *pRef, // this was a ref-copy of .Default\SW before the update
  604. IN KeyNode *pDestination,// this is opur private TS-hive
  605. IN KeyBasicInfo *pBasicInfo,
  606. IN ULONG *pIndentLevel)
  607. {
  608. NTSTATUS status=STATUS_SUCCESS, st2;
  609. ULONG ulCount=0;
  610. UNICODE_STRING UniString;
  611. ULONG size;
  612. (*pIndentLevel)++;
  613. while (NT_SUCCESS_EX(status))
  614. {
  615. ULONG ultemp;
  616. status = NtEnumerateKey( pSource->Key(),
  617. ulCount++,
  618. pBasicInfo->Type() , // keyInformationClass,
  619. pBasicInfo->Ptr(), // pKeyInfo,
  620. pBasicInfo->Size(), // keyInfoSize,
  621. &ultemp);
  622. // pBasicInfo was filled up thru NtEnumerateKey() above
  623. if (NT_SUCCESS_EX(status))
  624. {
  625. if ( g_debugIO)
  626. {
  627. DebugKeyStamp( status , pBasicInfo, *pIndentLevel );
  628. }
  629. KeyNode RefSubKey( pRef, pBasicInfo);
  630. KeyNode SourceSubKey( pSource,pBasicInfo);
  631. // calling Open() on this may fail, and we will need to delete and recreate it if required.
  632. KeyNode *pDestinationSubKey = new KeyNode( pDestination, pBasicInfo);
  633. RefSubKey.Open();
  634. SourceSubKey.Open();
  635. if ( pDestinationSubKey )
  636. {
  637. pDestinationSubKey->Open();
  638. if (NT_SUCCESS_EX( status = SourceSubKey.Status() ) )
  639. {
  640. // key is missing from the ref-hive, we should add
  641. // the same sub-tree into our TS\Install\SW hive
  642. if ( RefSubKey.Status() == STATUS_OBJECT_NAME_NOT_FOUND
  643. || RefSubKey.Status() == STATUS_OBJECT_PATH_SYNTAX_BAD)
  644. {
  645. // @@@
  646. // we expect the key not to exist, if it does, then what? delete it?
  647. if ( !NT_SUCCESS( pDestinationSubKey->Status()) )
  648. {
  649. // here is what were are doing with the strings:
  650. // 1) get the path below the "\Registry\User\.Default", which would be
  651. // something like "\Software\SomeDir\SomeDirOther\etc", this is the sub-path
  652. // 2) create a new node at the destination, which would be something like:
  653. // \HKLM\SW\MS\Windows NT\CurrentVersion\TS\INstall + the sub path
  654. // we got above.
  655. // this is the trailing part of the key-path missing from our TS hive
  656. PWCHAR pwch;
  657. SourceSubKey.GetPath(&pwch);
  658. PWCHAR pDestinationSubPath = &pwch[g_length_TERMSRV_USERREGISTRY_DEFAULT ];
  659. PWCHAR pDestinationFullPath= new WCHAR [ g_length_TERMSRV_INSTALL +
  660. wcslen( pDestinationSubPath) + sizeof(WCHAR )];
  661. wcscpy( pDestinationFullPath, TERMSRV_INSTALL );
  662. wcscat( pDestinationFullPath, pDestinationSubPath );
  663. DELETE_AND_NULL( pDestinationSubKey );
  664. // create a new KeyNode object where the root will be TERMSRV_INSTALL,
  665. // below which we will create a sub-layer of nodes, or a single node.
  666. pDestinationSubKey = new KeyNode( NULL , pDestination->Masks(), pDestinationFullPath);
  667. // create the new key/branch/values
  668. BOOL bCreate = TRUE;
  669. if (wcslen(pDestinationFullPath) > sizeof(TERMSRV_INSTALL)/sizeof(WCHAR))
  670. {
  671. if (RegPathExistsInOmissionList(pDestinationFullPath + (sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) - 1))
  672. bCreate = FALSE;
  673. }
  674. if (bCreate)
  675. {
  676. status = pDestinationSubKey->CreateEx();
  677. if ( g_debugIO )
  678. {
  679. DebugKeyStamp( status, pBasicInfo, *pIndentLevel , L"[KEY WAS CREATED]");
  680. }
  681. }
  682. else
  683. {
  684. DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED );
  685. }
  686. }
  687. }
  688. else
  689. {
  690. // if we have anything but success, set status and bail out
  691. if ( !NT_SUCCESS_EX( status = RefSubKey.Status()) )
  692. {
  693. if ( g_debugIO )
  694. {
  695. DebugErrorStamp(status, __LINE__ );
  696. }
  697. }
  698. }
  699. // Key (if it is NEW) is NOT missing from destination hive at this point
  700. // either it did exist, or was created in the above block of code
  701. // check if there are any new values in this node.
  702. KEY_FULL_INFORMATION *ptrInfo;
  703. ULONG size;
  704. NTSTATUS st3 = SourceSubKey.Query( &ptrInfo, &size );
  705. if (NT_SUCCESS( st3 ))
  706. {
  707. ValueFullInfo sourceValueFullInfo( &SourceSubKey );
  708. if ( NT_SUCCESS_EX( status = sourceValueFullInfo.Status() ) )
  709. {
  710. for (ULONG ulkey = 0; ulkey < ptrInfo->Values; ulkey++)
  711. {
  712. status = NtEnumerateValueKey(SourceSubKey.Key(),
  713. ulkey,
  714. sourceValueFullInfo.Type(),
  715. sourceValueFullInfo.Ptr(),
  716. sourceValueFullInfo.Size(),
  717. &ultemp);
  718. // @@@
  719. if ( ! NT_SUCCESS( status ))
  720. {
  721. DebugErrorStamp( status, __LINE__ );
  722. }
  723. // if the ref key is missing a value, then add
  724. // value to the destination key.
  725. KEY_VALUE_PARTIAL_INFORMATION *pValuePartialInfo;
  726. ValuePartialInfo refValuePartialInfo( &RefSubKey );
  727. // It is important to realize that at this point, it is possible
  728. // that ref key did not exist, which follows that refvalue would also
  729. // not exist. The C++ objects RefSubKey and refValuePartialInfo do
  730. // exist as objects, but there is not counter part of actual registry
  731. // data in the registry, hence, the pointers in these object are NULL,
  732. // as expected. Still, NULL data should be interpreted as not present data.
  733. //
  734. // So, the below call refValuePartialInfo.Status() should return TRUE since
  735. // object was created successfully above, but query should return object not
  736. // found or invalid handle without actually calling the reg apis since
  737. // the reg key handle is null.
  738. //
  739. if ( NT_SUCCESS_EX( status = refValuePartialInfo.Status() ) )
  740. {
  741. refValuePartialInfo.Query( sourceValueFullInfo.SzName() );
  742. // if .Default\SW has a value that is missing from the ref hive, then add
  743. // corresponding value into our TS\ hive
  744. if ( !NT_SUCCESS( refValuePartialInfo.Status()) )
  745. {
  746. // make sure pDestinationSubKey exists, else, create the key first before we
  747. // write a value. It is possible that even though the key did exists in the ref
  748. // hive at the start, a new value was added for the first time, which means that the
  749. // ts hive is getting the key and the value for the first time.
  750. if ( !NT_SUCCESS( pDestinationSubKey->Status() ) )
  751. {
  752. // here is what were are doing with the strings:
  753. // 1) get the path below the "\Registry\User\.Default", which would be
  754. // something like "\Software\SomeDir\SomeDirOther\etc", this is the sub-path
  755. // 2) create a new node at the destination, which would be something like:
  756. // \HKLM\SW\MS\Windows NT\CurrentVersion\TS\INstall + the sub path
  757. // we got above.
  758. // this is the trailing part of the key-path missing from our TS hive
  759. PWCHAR pwch;
  760. SourceSubKey.GetPath( &pwch );
  761. PWCHAR pDestinationSubPath = &pwch[g_length_TERMSRV_USERREGISTRY_DEFAULT ];
  762. PWCHAR pDestinationFullPath= new WCHAR [ g_length_TERMSRV_INSTALL +
  763. wcslen( pDestinationSubPath) + sizeof(WCHAR )];
  764. wcscpy( pDestinationFullPath, TERMSRV_INSTALL );
  765. wcscat( pDestinationFullPath, pDestinationSubPath );
  766. DELETE_AND_NULL( pDestinationSubKey );
  767. // create a new KeyNode object where the root will be TERMSRV_INSTALL,
  768. // below which we will create a sub-layer of nodes, or a single node.
  769. pDestinationSubKey = new KeyNode( NULL , pDestination->Masks(), pDestinationFullPath);
  770. // create the new key/branch/values
  771. BOOL bCreate = TRUE;
  772. if (wcslen(pDestinationFullPath) > sizeof(TERMSRV_INSTALL)/sizeof(WCHAR))
  773. {
  774. if (RegPathExistsInOmissionList(pDestinationFullPath + (sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) - 1))
  775. bCreate = FALSE;
  776. }
  777. if (bCreate)
  778. {
  779. status = pDestinationSubKey->CreateEx();
  780. if ( g_debugIO )
  781. {
  782. DebugKeyStamp( status, pBasicInfo, *pIndentLevel, L"[KEY WAS CREATED]" );
  783. }
  784. }
  785. else
  786. {
  787. DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED );
  788. }
  789. }
  790. //else, no problem, key did exist and we don't need to create it
  791. // Create value at the destination node
  792. // by now, if we do have a key, then we create values for it but only if
  793. // this key is not pointing to a reg path that we are suppose to ignore due
  794. // to the path being mentioned in the omission list.
  795. if (pDestinationSubKey->Key() )
  796. {
  797. if (!HKeyExistsInOmissionList((HKEY)(pDestinationSubKey->Key())))
  798. {
  799. ValueFullInfo destinationValue( pDestinationSubKey );
  800. if ( NT_SUCCESS_EX( status = destinationValue.Status()) )
  801. {
  802. status = destinationValue.Create( &sourceValueFullInfo );
  803. NT_SUCCESS_EX( status );
  804. // if status is error, we bail out.
  805. }
  806. //else, out of memory, var-status is set and we bail out.
  807. }
  808. else
  809. {
  810. DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED );
  811. }
  812. }
  813. else
  814. {
  815. DEBUG_INFO( status, pDestinationSubKey , NO_KEY_HANDLE );
  816. }
  817. }
  818. else // values are not missing, see if they are the same
  819. {
  820. // compare the two data buffers, if the one from SourceSubKey is
  821. // different than the one from the RefSubKey, then delete
  822. // and create one in DestinationSubKey
  823. ValueFullInfo sourceValue( &SourceSubKey);
  824. ValueFullInfo refValue ( &RefSubKey );
  825. if (NT_SUCCESS_EX( status = refValue.Status())
  826. && NT_SUCCESS_EX( status = sourceValue.Status())
  827. )
  828. {
  829. sourceValue.Query( sourceValueFullInfo.SzName() );
  830. refValue.Query ( sourceValueFullInfo.SzName() );
  831. if (NT_SUCCESS( refValue.Status())
  832. && NT_SUCCESS( sourceValue.Status()))
  833. {
  834. BOOLEAN theSame = sourceValue.Compare( &refValue );
  835. if (! theSame )
  836. {
  837. // make sure pDestinationSubKey exists, else, create the key first before we
  838. // write a value. It is possible that even though the key did exists in the ref
  839. // hive at the start, a new value was added for the first time, which means that the
  840. // ts hive is getting the key and the value for the first time.
  841. if ( !NT_SUCCESS( pDestinationSubKey->Status() ) )
  842. {
  843. // here is what were are doing with the strings:
  844. // 1) get the path below the "\Registry\User\.Default", which would be
  845. // something like "\Software\SomeDir\SomeDirOther\etc", this is the sub-path
  846. // 2) create a new node at the destination, which would be something like:
  847. // \HKLM\SW\MS\Windows NT\CurrentVersion\TS\INstall + the sub path
  848. // we got above.
  849. // this is the trailing part of the key-path missing from our TS hive
  850. PWCHAR pwch;
  851. SourceSubKey.GetPath( &pwch );
  852. PWCHAR pDestinationSubPath = &pwch[g_length_TERMSRV_USERREGISTRY_DEFAULT ];
  853. PWCHAR pDestinationFullPath= new WCHAR [ g_length_TERMSRV_INSTALL +
  854. wcslen( pDestinationSubPath) + sizeof(WCHAR )];
  855. wcscpy( pDestinationFullPath, TERMSRV_INSTALL );
  856. wcscat( pDestinationFullPath, pDestinationSubPath );
  857. DELETE_AND_NULL( pDestinationSubKey );
  858. // create a new KeyNode object where the root will be TERMSRV_INSTALL,
  859. // below which we will create a sub-layer of nodes, or a single node.
  860. pDestinationSubKey = new KeyNode( NULL , pDestination->Masks(), pDestinationFullPath);
  861. // create the new key/branch/values
  862. BOOL bCreate = TRUE;
  863. if (wcslen(pDestinationFullPath) > sizeof(TERMSRV_INSTALL)/sizeof(WCHAR))
  864. {
  865. if (RegPathExistsInOmissionList(pDestinationFullPath + (sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) - 1))
  866. bCreate = FALSE;
  867. }
  868. if (bCreate)
  869. {
  870. status = pDestinationSubKey->CreateEx();
  871. if ( g_debugIO )
  872. {
  873. DebugKeyStamp( status, pBasicInfo, *pIndentLevel , L"KEY WAS CREATED");
  874. }
  875. }
  876. else
  877. {
  878. DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED );
  879. }
  880. }
  881. //else, no problem, key did exist and we don't need to create it
  882. // By now, if we do have a key, then we create values for it but only if
  883. // this key is not pointing to a reg path that we are suppose to ignore due
  884. // to the path being mentioned in the omission list.
  885. if (pDestinationSubKey->Key() )
  886. {
  887. if (!HKeyExistsInOmissionList((HKEY)(pDestinationSubKey->Key())))
  888. {
  889. ValueFullInfo destinationValue( pDestinationSubKey );
  890. if ( NT_SUCCESS( destinationValue.Status() ) )
  891. {
  892. // don't care if it exists or not, delete it first
  893. destinationValue.Delete( sourceValueFullInfo.SzName() );
  894. }
  895. // else, there is no destination value to delete
  896. // update/create item under destination
  897. // Create a destination value identical to the source value
  898. status = destinationValue.Create( &sourceValue );
  899. }
  900. else
  901. {
  902. DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED );
  903. }
  904. }
  905. else
  906. {
  907. DEBUG_INFO( status, pDestinationSubKey , NO_KEY_HANDLE );
  908. }
  909. // if status is error, we will bail out
  910. if (!NT_SUCCESS_EX( status ))
  911. {
  912. if (g_debugIO)
  913. {
  914. DebugErrorStamp(status, __LINE__,
  915. &sourceValue );
  916. }
  917. }
  918. }
  919. }
  920. // else, values don't exits, doesn't make sense, maybe some dbug code here?
  921. }
  922. // else, var-status is set, we bail out.
  923. }
  924. //if-else
  925. }
  926. //else, out of memory, var-status is set, we bail out
  927. }
  928. // for-loop
  929. }
  930. //else, out of memory, var-status is set, we bail out
  931. }
  932. else
  933. {
  934. // this sbould not really happen, but for now...
  935. if ( g_debugIO )
  936. {
  937. DebugErrorStamp( status, __LINE__ );
  938. }
  939. }
  940. // by now, either both source and destination nodes exist, or
  941. // a new destination node was just created above. In any case,
  942. // we can continue the traversal.
  943. if ( NT_SUCCESS( status ) )
  944. {
  945. status = EnumerateAndDeltaUpdateKeys(
  946. &SourceSubKey,
  947. &RefSubKey,
  948. pDestinationSubKey,
  949. pBasicInfo ,
  950. pIndentLevel);
  951. NT_SUCCESS_EX( status );
  952. }
  953. //else, we are bailing out
  954. }
  955. // else, var-status is set, we bail out.
  956. // done with this sub key,
  957. DELETE_AND_NULL( pDestinationSubKey );
  958. }
  959. else
  960. {
  961. status = STATUS_NO_MEMORY;
  962. }
  963. status = AlterStatus( status, __LINE__ );
  964. }
  965. // else, no more entries
  966. }
  967. // no more entries
  968. (*pIndentLevel)--;
  969. // the typical status would be: STATUS_NO_MORE_ENTRIES
  970. return status;
  971. }
  972. // delete the ref-hive as specific by the uniRef string
  973. NTSTATUS DeleteReferenceHive(WCHAR *uniRef)
  974. {
  975. if ( g_debugIO)
  976. {
  977. fwprintf( g_debugFilePointer,L"In %ws\n",
  978. L"----DeleteReferenceHive");
  979. fflush( g_debugFilePointer );
  980. }
  981. NTSTATUS status = STATUS_SUCCESS;
  982. KeyNode Old( NULL, KEY_WRITE | KEY_READ | DELETE, uniRef );
  983. if ( NT_SUCCESS( Old.Open() ) )
  984. {
  985. Old.DeleteSubKeys();
  986. status = Old.Delete(); // delete the head of the branch
  987. }
  988. Old.Close();
  989. return status;
  990. }
  991. /****************************************************************
  992. *
  993. * Function:
  994. * CreateReferenceHive
  995. *
  996. * Parameters:
  997. * uniSource (source ) string points to the node under .Default
  998. * uniRef (ref ) string point to TS\Install\RefHive
  999. * UniDest (Destination) string points to TS\Install\Software
  1000. *
  1001. * Description:
  1002. * from the .Default (source) hive, copy into TS\install\RefHive
  1003. * source hive is specified by the uniSoure string, and the
  1004. * ref-hive is specified by the uniRef string.
  1005. *
  1006. * Return:
  1007. * NTSTATUS, if successful, then STATUS_SUCCESS
  1008. *
  1009. ****************************************************************/
  1010. NTSTATUS CreateReferenceHive(WCHAR *uniSource, WCHAR *uniRef)
  1011. {
  1012. if ( g_debugIO)
  1013. {
  1014. fwprintf( g_debugFilePointer,L"In %ws\n",
  1015. L"----CreateReferenceHive");
  1016. fflush( g_debugFilePointer );
  1017. }
  1018. // 1-COPY
  1019. // copy all keys under .Default\Software into a special location
  1020. // under our TS hive, let's call it the RefHive
  1021. // This will act as the reference hive
  1022. NTSTATUS status = STATUS_SUCCESS;
  1023. ULONG indentLevel=0;
  1024. // start creating our cache Ref hive
  1025. KeyNode Ref( NULL, MAXIMUM_ALLOWED, uniRef );
  1026. // if we were able to create our RefHive, then continue...
  1027. if ( NT_SUCCESS_EX( status = Ref.Create() ) )
  1028. {
  1029. KeyNode Source(NULL, KEY_READ, uniSource );
  1030. // open the source reg-key-path
  1031. if (NT_SUCCESS_EX( status = Source.Open() ))
  1032. {
  1033. KeyBasicInfo kBasicInfo;
  1034. if (NT_SUCCESS_EX( status = kBasicInfo.Status() ))
  1035. {
  1036. // this will be a recursive call, so we are saving allocation
  1037. // cycles by passing kBasicInfo as scratch pad.
  1038. status = EnumerateAndCreateRefHive( &Source,
  1039. &Ref,
  1040. &kBasicInfo,
  1041. &indentLevel);
  1042. }
  1043. }
  1044. }
  1045. if ( status == STATUS_NO_MORE_ENTRIES)
  1046. {
  1047. status = STATUS_SUCCESS;
  1048. }
  1049. return status;
  1050. }
  1051. /************************************************************************
  1052. *
  1053. * Function:
  1054. * DeltaDeleteKeys(WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest)
  1055. *
  1056. * Parameters:
  1057. * uniSource (source ) string points to the node under .Default
  1058. * uniRef (ref ) string point to TS\Install\RefHive
  1059. * UniDest (Destination) string points to TS\Install\Software
  1060. *
  1061. * Description:
  1062. * compare .Dfeault keys to the equivalent keys in RefHive. If keys are
  1063. * missing from .Default, then delete the equivalent keys from our
  1064. * HKLM\...\TS\ hive
  1065. *
  1066. * Return:
  1067. * NTSTATUS, if successful, then STATS_SUCCESS
  1068. *
  1069. ************************************************************************/
  1070. NTSTATUS DeltaDeleteKeys(WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest)
  1071. {
  1072. if ( g_debugIO)
  1073. {
  1074. fwprintf( g_debugFilePointer,L"In %ws\n",
  1075. L"----DeltaDeleteKeys");
  1076. fflush( g_debugFilePointer );
  1077. }
  1078. // Step2-DELETE
  1079. // compare .Dfeault keys to the equivalent keys in RefHive. If keys are
  1080. // missing from .Default, then delete the equivalent keys from our
  1081. // HKLM\...\TS\ hive
  1082. KeyNode Source( NULL, KEY_READ, uniSource );
  1083. KeyNode Ref( NULL, MAXIMUM_ALLOWED, uniRef );
  1084. KeyNode Destination( NULL, MAXIMUM_ALLOWED, uniDest );
  1085. Source.Open();
  1086. Ref.Open();
  1087. Destination.Open();
  1088. ULONG indentLevel=0;
  1089. NTSTATUS status = STATUS_SUCCESS;
  1090. if ( NT_SUCCESS_EX( status = Source.Status() ) &&
  1091. NT_SUCCESS_EX( status = Ref.Status() ) &&
  1092. NT_SUCCESS_EX( status = Destination.Status() ) )
  1093. {
  1094. KeyBasicInfo basicInfo;
  1095. if( NT_SUCCESS_EX( status = basicInfo.Status() ) )
  1096. {
  1097. // walk and compare, if missing from Source, then delete from Destination
  1098. status = EnumerateAndDeltaDeleteKeys(
  1099. &Source,
  1100. &Ref,
  1101. &Destination,
  1102. &basicInfo,
  1103. &indentLevel);
  1104. }
  1105. }
  1106. if ( status == STATUS_NO_MORE_ENTRIES)
  1107. {
  1108. status = STATUS_SUCCESS;
  1109. }
  1110. return status;
  1111. }
  1112. /************************************************************************
  1113. *
  1114. * Function:
  1115. * DeltaUpdateKeys(WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest)
  1116. *
  1117. * Parameters:
  1118. * uniSource (source ) string points to the node under .Default
  1119. * uniRef (ref ) string point to TS\Install\RefHive
  1120. * UniDest (Destination) string points to TS\Install\Software
  1121. *
  1122. * Description:
  1123. * Step-3 CREATE/Update keys and values
  1124. * compare .Default keys to the equivalent keys in RefHive, if keys are
  1125. * present in .Default that are missing from RefHive, then, add those keys
  1126. * to our HKLM\...\TS hive. Do the same for the values.
  1127. * Then, compare values from .Default to values in .Ref. If values have
  1128. * changed, then delete the value from our destination hive and create a
  1129. * new one with the appropriate data from .Default
  1130. *
  1131. * Return:
  1132. * NTSTATUS, if successful, then STATS_SUCCESS
  1133. *
  1134. ************************************************************************/
  1135. NTSTATUS DeltaUpdateKeys (WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest)
  1136. {
  1137. if ( g_debugIO)
  1138. {
  1139. fwprintf( g_debugFilePointer,L"In %ws\n",
  1140. L"----DeltaUpdateKeys");
  1141. fflush( g_debugFilePointer );
  1142. }
  1143. // 3-CREATE
  1144. // compare .Default keys to the equivalent keys in RefHive, if keys are
  1145. // present in .Default that are missing from RefHive, then, add those keys
  1146. // to our HKLM\...\TS hive
  1147. KeyNode Source( NULL, KEY_READ, uniSource );
  1148. KeyNode Ref( NULL, MAXIMUM_ALLOWED, uniRef );
  1149. KeyNode Destination( NULL, MAXIMUM_ALLOWED, uniDest );
  1150. Source.Open();
  1151. Ref.Open();
  1152. Destination.Open();
  1153. NTSTATUS status;
  1154. ULONG indentLevel=0;
  1155. if ( NT_SUCCESS_EX( status = Source.Status() ) &&
  1156. NT_SUCCESS_EX( status = Ref.Status() ) &&
  1157. NT_SUCCESS_EX( status = Destination.Status() ) )
  1158. {
  1159. KeyBasicInfo basicInfo;
  1160. // Constructor in KeyBasicInfo above allocates memory for pInfo
  1161. // check if memory allocation of pInfo succeeded
  1162. status = basicInfo.Status();
  1163. if (status != STATUS_SUCCESS) {
  1164. return status;
  1165. }
  1166. // walk and compare, if missing from Source, then delete from Destination
  1167. status = EnumerateAndDeltaUpdateKeys(
  1168. &Source,
  1169. &Ref,
  1170. &Destination,
  1171. &basicInfo,
  1172. &indentLevel);
  1173. }
  1174. if ( status == STATUS_NO_MORE_ENTRIES)
  1175. {
  1176. status = STATUS_SUCCESS;
  1177. }
  1178. return status;
  1179. }