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.

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