|
|
/****************************************************************************************
* MSI will call these APIs to ask TS to propogate changes from .Default to the TS hive. * * NTSTATUS TermServPrepareAppInstallDueMSI() * * NTSTATUS TermServProcessAppIntallDueMSI( BOOLEAN cleanup ) * * These API need not be called in the same boot cycles, many boot cycles could * happen in between. * * Copyright (C) 1997-1999 Microsoft Corp. ****************************************************************************************/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntexapi.h>
#include <stdio.h>
#include <windows.h>
#include <winuser.h>
#include <stdlib.h>
#include <tsappcmp.h>
#include <stdio.h>
#include <fcntl.h>
#include "KeyNode.h"
#include "ValInfo.h"
// real externs!
extern "C" { void TermsrvLogRegInstallTime(void); }
extern "C" { BOOL HKeyExistsInOmissionList(HKEY hKeyToCheck); }
extern "C" { BOOL RegPathExistsInOmissionList(PWCHAR pwchKeyToCheck); }
// forward declaration
extern NTSTATUS DeleteReferenceHive(WCHAR *); extern NTSTATUS CreateReferenceHive( WCHAR *, WCHAR *); extern NTSTATUS DeltaDeleteKeys(WCHAR *, WCHAR *, WCHAR *); extern NTSTATUS DeltaUpdateKeys(WCHAR *, WCHAR *, WCHAR *);
ULONG g_length_TERMSRV_USERREGISTRY_DEFAULT; ULONG g_length_TERMSRV_INSTALL; WCHAR g_debugFileName[MAX_PATH]; FILE *g_debugFilePointer=NULL; BOOLEAN g_debugIO = FALSE; BOOLEAN KeyNode::debug=FALSE; // init the static
#define TERMSRV_USERREGISTRY_DEFAULT TEXT("\\Registry\\USER\\.Default")
// This is for debug I/O, output looks better with it.
void Indent( ULONG indent) {
for ( ULONG i = 1; i <indent ; i++ ) { fwprintf( g_debugFilePointer, L" "); } }
// a key name is written to the log file based on the contect of pBasicInfo
void DebugKeyStamp( NTSTATUS status, KeyBasicInfo *pBasicInfo, int indent , WCHAR *pComments=L"" ) { Indent(indent); fwprintf( g_debugFilePointer,L"%ws, status=%lx, %ws\n",pBasicInfo->NameSz(), status , pComments); fflush( g_debugFilePointer ); DbgPrint("%ws\n",pBasicInfo->NameSz()); }
// a debug stamp is written to the log file, including the line number where error happened
void DebugErrorStamp(NTSTATUS status , int lineNumber, ValueFullInfo *pValue=NULL) { fwprintf( g_debugFilePointer, L"ERROR ?!? status = %lx, linenumber:%d\n", status, lineNumber); if (pValue) { pValue->Print(g_debugFilePointer); } fflush(g_debugFilePointer); }
#define KEY_IGNORED L"[key was ignored]"
#define NO_KEY_HANDLE L"[No handle, key ignored]"
void DebugInfo(NTSTATUS status , int lineNumber, KeyNode *pKey, WCHAR *comment) { if(g_debugIO && g_debugFilePointer) { fwprintf( g_debugFilePointer, L"ERROR ?!? status = %lx, linenumber:%d, KeyNode name=%ws, %ws\n", status, lineNumber , pKey->Name() , comment); fflush(g_debugFilePointer); } }
// use this func to track the status value which is used to bail out in
// case of an error.
// this is only used in teh debug build, see below
BOOL NT_SUCCESS_OR_ERROR_STAMP( NTSTATUS status, ULONG lineNumber) { if ( g_debugIO ) { if ( ( (ULONG)status) >=0xC0000000 ) { DebugErrorStamp( status, lineNumber ); } }
return ( (NTSTATUS)(status) >= 0 ); }
#ifdef DBG
#define NT_SUCCESS_EX(Status) NT_SUCCESS_OR_ERROR_STAMP( (Status), __LINE__ )
#define DEBUG_INFO(Status, pKey, comment ) DebugInfo( Status, __LINE__ , pKey , comment)
#else
#define NT_SUCCESS_EX(Status) NT_SUCCESS(Status)
#define DEBUG_INFO(Stats, pKey , comment)
#endif
/***************************************************************************
* * All three branch-walker functions use this method to alter the status code, and * if necessary, log an error message to the log file * ***************************************************************************/ NTSTATUS AlterStatus( NTSTATUS status , int lineNumber ) { switch( status ) { case STATUS_ACCESS_DENIED: // this should never happen since we run in the system context
if ( g_debugIO ) { DebugErrorStamp( status, lineNumber ); } status = STATUS_SUCCESS; break;
case STATUS_SUCCESS: break;
case STATUS_NO_MORE_ENTRIES: status = STATUS_SUCCESS; break;
default: if ( g_debugIO ) { DebugErrorStamp( status, lineNumber ); } break;
} return status; }
/******************************************************************************
* * Based on a special reg key/value init the debug flags and pointers which are * used to log debug info into a log file. When called with start=TRUE, the * relevant data structs are initialized. When called with start=FALSE, the log * file is closed. * ******************************************************************************/ void InitDebug( BOOLEAN start) { if ( start ) { KeyNode tsHiveNode (NULL, KEY_READ, TERMSRV_INSTALL ); if ( NT_SUCCESS( tsHiveNode.Open() ) ) { ValuePartialInfo debugValue( &tsHiveNode ); if ( NT_SUCCESS( debugValue.Status() ) && NT_SUCCESS( debugValue.Query(L"TS_MSI_DEBUG") ) ) { g_debugIO = TRUE; KeyNode::debug=TRUE; for (ULONG i =0; i < debugValue.Ptr()->DataLength/sizeof(WCHAR); i++) { g_debugFileName[i] = ((WCHAR*)(debugValue.Ptr()->Data))[i]; } g_debugFileName[i] = L'\0'; g_debugFilePointer = _wfopen( g_debugFileName, L"a+" ); fwprintf( g_debugFilePointer, L"----\n"); } } } else { if ( g_debugFilePointer ) { fclose(g_debugFilePointer); } } }
/***************************************************************************
* * Function: * TermServPrepareAppInstallDueMSI() * * Description: * MSI service calls this function prior to starting an installation cycle. * When called, this function blows away the RefHive (in case it was around * with some stale data...), and then it creates a fresh copy of * .Default\Software as the new RefHive. * * Return: * NTSTATUS * ***************************************************************************/ NTSTATUS TermServPrepareAppInstallDueMSI() { NTSTATUS status = STATUS_SUCCESS;
WCHAR sourceHive[MAX_PATH]; WCHAR referenceHive[MAX_PATH]; WCHAR destinationHive[MAX_PATH];
wcscpy(sourceHive, TERMSRV_USERREGISTRY_DEFAULT ); wcscat(sourceHive, L"\\Software"); g_length_TERMSRV_USERREGISTRY_DEFAULT = wcslen( TERMSRV_USERREGISTRY_DEFAULT );
wcscpy(referenceHive, TERMSRV_INSTALL ); wcscat(referenceHive, L"\\RefHive"); g_length_TERMSRV_INSTALL = wcslen( TERMSRV_INSTALL );
InitDebug( TRUE );
if ( g_debugIO) { fwprintf( g_debugFilePointer,L"In %ws\n", L"TermServPrepareAppInstallDueMSI"); fflush( g_debugFilePointer ); }
// delete the existing hive (if any )
status = DeleteReferenceHive( referenceHive );
if ( NT_SUCCESS( status ) ) { // 1-COPY
// copy all keys under .Default\Software into a special location
// under our TS hive, let's call it the RefHive
status = CreateReferenceHive(sourceHive, referenceHive);
}
InitDebug( FALSE );
return status; }
/**********************************************************************************
* * Function: * TermServProcessAppInstallDueMSI * * Description: * MSI service calls this function after calling TermServPrepareAppInstallDueMSI(), * and after MSI finishing making an installation which updated the .Default * hive (since MSI runs in the system context). * This function will compare the content of .Default\SW to RefHive and then * first it will create all new (missing) keys and values. Then it will * compare any existing keys from .Default\SW with the equivalent RefHive, and * if value is different, it will delete the equivalent value from our TS hive * and then create a new value identical to what was found in .Default * * Return: * NTSTATUS * **********************************************************************************/ NTSTATUS TermServProcessAppInstallDueMSI( BOOLEAN cleanup) { NTSTATUS status = STATUS_SUCCESS; WCHAR sourceHive[MAX_PATH]; WCHAR referenceHive[MAX_PATH]; WCHAR destinationHive[MAX_PATH];
wcscpy(sourceHive, TERMSRV_USERREGISTRY_DEFAULT ); wcscat(sourceHive, L"\\Software"); g_length_TERMSRV_USERREGISTRY_DEFAULT = wcslen( TERMSRV_USERREGISTRY_DEFAULT );
wcscpy(referenceHive, TERMSRV_INSTALL ); wcscat(referenceHive, L"\\RefHive"); g_length_TERMSRV_INSTALL = wcslen( TERMSRV_INSTALL );
wcscpy(destinationHive, TERMSRV_INSTALL ); wcscat(destinationHive, L"\\Software");
InitDebug( TRUE );
if ( g_debugIO) { fwprintf( g_debugFilePointer,L"In %ws, cleanup=%lx\n", L"TermServProcessAppIntallDueMSI", cleanup); fflush( g_debugFilePointer ); }
if ( !cleanup ) { // 2-DELETE
// compare .Dfeault keys to the equivalent keys in RefHive. If keys are
// missing from .Default, then delete the equivalent keys from our
// HKLM\...\TS\ hive
status = DeltaDeleteKeys(sourceHive, referenceHive, destinationHive); if (NT_SUCCESS( status ) ) { // Steps 3 and 4 are now combined.
// 3-CREATE
// compare .Default keys to the equivalent keys in RefHive, if keys are
// present in .Default that are missing from RefHive, then, add those keys
// to our HKLM\...\TS hive
// 4-CHANGE
// compare keys of .Default to RefHive. Those keys that are newer than
// RefHive, then, update the equivalent keys in HKLM\...\TS
status = DeltaUpdateKeys(sourceHive, referenceHive, destinationHive);
if (NT_SUCCESS( status )) { // update the time stamp in our hive since we want the standared TS reg key
// propogation to take place.
TermsrvLogRegInstallTime();
} } } else { // blow away the existing reference hive,
status = DeleteReferenceHive( referenceHive ); }
InitDebug( FALSE );
return status; }
/*******************************************************************
* * Function: * EnumerateAndCreateRefHive * * Parameters: * pSource points to the parent node, the branch we copy * pref points to our RefHive which we are creating as a ref image * pBasicInfo is a scratch pad passed around which is used to * extract basic Key information * pindextLevel is used to format the debug log output file * * Descritption: * Create a copy of the .Default\Sofwtare as our RefHive * * Return: * NTSTATUS *******************************************************************/ NTSTATUS EnumerateAndCreateRefHive( IN KeyNode *pSource, IN KeyNode *pRef, IN KeyBasicInfo *pBasicInfo, IN ULONG *pIndentLevel ) { NTSTATUS status=STATUS_SUCCESS; ULONG ulCount=0;
UNICODE_STRING UniString;
(*pIndentLevel)++;
while( NT_SUCCESS(status)) { ULONG ultemp;
status = NtEnumerateKey( pSource->Key(), ulCount++, pBasicInfo->Type() , // keyInformationClass,
pBasicInfo->Ptr(), // pKeyInfo,
pBasicInfo->Size(), // keyInfoSize,
&ultemp);
if (NT_SUCCESS(status)) { if ( g_debugIO) { DebugKeyStamp( status , pBasicInfo, *pIndentLevel ); } // open a sub key
KeyNode SourceSubKey( pSource, pBasicInfo);
// create the Ref sub key
KeyNode RefSubKey( pRef, pBasicInfo);
if (NT_SUCCESS_EX( status = SourceSubKey.Open() ) ) { if ( NT_SUCCESS_EX( status = RefSubKey.Create() ) ) { NTSTATUS status3; KEY_FULL_INFORMATION *ptrInfo; ULONG size; if (NT_SUCCESS(SourceSubKey.Query( &ptrInfo, &size ))) { ValueFullInfo valueFullInfo( &SourceSubKey ); ValueFullInfo RefValue( &RefSubKey ); if ( NT_SUCCESS_EX( status = valueFullInfo.Status()) && NT_SUCCESS_EX(status = RefValue.Status()) ) { for (ULONG ulkey = 0; ulkey < ptrInfo->Values; ulkey++) { status = NtEnumerateValueKey(SourceSubKey.Key(), ulkey, valueFullInfo.Type(), valueFullInfo.Ptr(), valueFullInfo.Size(), &ultemp); if (NT_SUCCESS( status )) { status = RefValue.Create( &valueFullInfo ); // if status is not good, we bail out, since var "status" is set here
} // else, no more entries left, we continue
} } // else, out of memory, status is set, we bail out.
} // else, no values are present, continue with sub-key enums
if (NT_SUCCESS( status ) ) { // enumerate sub key down.
status = EnumerateAndCreateRefHive( &SourceSubKey, &RefSubKey, pBasicInfo, pIndentLevel ); } } // else, an error, status is set, so we bail out
}// else, open on source has failed, var-status is set, we bail out
status = AlterStatus( status, __LINE__ ); // else, an error, status is set, so we bail out
} // else, no more left
}
(*pIndentLevel)--;
return( status ); }
/*******************************************************************
* * Function: * EnumerateAndDeltaDeleteKeys * * Parameters: * pSource points to a node under .Dfeault * pref points to a node under our RefHive * pDestination is a node under our TS\install\SW hive * pBasicInfo is a scratch pad passed around which is used to * extract basic Key information * pindextLevel is used to format the debug log output file * * Descritption: * compare source to ref, if keys/values in source are deleted, then * delete the equivalent key/value from destination * * Return: * NTSTATUS *******************************************************************/ NTSTATUS EnumerateAndDeltaDeleteKeys( IN KeyNode *pSource, // this is under the latest updated .Default\SW hive
IN KeyNode *pRef, // this was a ref-copy of .Default\SW before the update
IN KeyNode *pDestination,// this is opur private TS-hive
IN KeyBasicInfo *pBasicInfo, IN ULONG *pIndentLevel) { NTSTATUS status=STATUS_SUCCESS, st2; ULONG ulCount=0;
UNICODE_STRING UniString; ULONG size;
(*pIndentLevel)++;
while (NT_SUCCESS(status)) { ULONG ultemp;
status = NtEnumerateKey( pRef->Key(), ulCount++, pBasicInfo->Type() , // keyInformationClass,
pBasicInfo->Ptr(), // pKeyInfo,
pBasicInfo->Size(), // keyInfoSize,
&ultemp);
// pBasicInfo was filled up thru NtEnumerateKey() above
if (NT_SUCCESS(status)) { if ( g_debugIO) { DebugKeyStamp( status , pBasicInfo, *pIndentLevel ); } KeyNode RefSubKey( pRef, pBasicInfo); KeyNode SourceSubKey( pSource,pBasicInfo); KeyNode DestinationSubKey( pDestination, pBasicInfo);
RefSubKey.Open(); SourceSubKey.Open(); DestinationSubKey.Open();
if (NT_SUCCESS( RefSubKey.Status() ) ) { if ( ! NT_SUCCESS( SourceSubKey.Status () ) ) { // key is missing from the .Default\SW hive, we should delete
// the same sub-tree from our TS\Install\SW hive
if ( NT_SUCCESS( DestinationSubKey.Status()) ) { if (!HKeyExistsInOmissionList((HKEY)(DestinationSubKey.Key()))) { DestinationSubKey.DeleteSubKeys(); NTSTATUS st = DestinationSubKey.Delete();
if ( g_debugIO) { DebugKeyStamp( st, pBasicInfo, *pIndentLevel ); } } else { DEBUG_INFO( status, &DestinationSubKey , KEY_IGNORED ); } } // else
// As long as the key is missing from
// Ts\install\Hive, we will regard this condition as acceptable.
} else { // see if any values have been deleted
// don't bother unless the destination key exists, otherwise, no values
// will be there to delete...
if ( NT_SUCCESS( DestinationSubKey.Status() ) ) {
KEY_FULL_INFORMATION *ptrInfo; ULONG size; if (NT_SUCCESS_EX(status = RefSubKey.Query( &ptrInfo, &size ))) { // from the key-full-information, create a key-value-full-information
ValueFullInfo refValueFullInfo( &RefSubKey ); ValueFullInfo sourceValue( &SourceSubKey ); // if no allocation errors, then...
if ( NT_SUCCESS_EX( status = refValueFullInfo.Status() ) && NT_SUCCESS_EX( status = sourceValue.Status() ) ) { for (ULONG ulkey = 0; ulkey < ptrInfo->Values; ulkey++) { if ( NT_SUCCESS_EX ( status = NtEnumerateValueKey(RefSubKey.Key(), ulkey, refValueFullInfo.Type(), refValueFullInfo.Ptr(), refValueFullInfo.Size(), &ultemp) ) ) { // for every value, see if the same value
// exists in the SourceSubKey. If it doesn't
// then delete the corresponding value from
// TS's hive
sourceValue.Query( refValueFullInfo.SzName() ); // if .Default\SW is missing a value, then delete the
// corresponding value from our TS\ hive
if ( sourceValue.Status() == STATUS_OBJECT_NAME_NOT_FOUND ) { ValuePartialInfo destinationValue( &DestinationSubKey);
if (NT_SUCCESS_EX( status = destinationValue.Status () ) ) { if (!HKeyExistsInOmissionList((HKEY)(DestinationSubKey.Key()))) { destinationValue.Delete( refValueFullInfo.SzName() ); } else { DEBUG_INFO( status, &DestinationSubKey , KEY_IGNORED ); } } // else, alloc error, status is set
} else { if ( !NT_SUCCESS_EX ( status = sourceValue.Status() ) ) { if ( g_debugIO ) { DebugErrorStamp(status, __LINE__ ); } // else, we will bail out here since var-status is set
} // else, no error
} // if-else
} // else, no more entries
} // for loop
} // else, we have an error due to no memory, var-status is set
} // else, we have an error since we can not get info on this existing ref key, var-status is set
if ( NT_SUCCESS( status ) ) { // we were able to open the source key, which means that
// key was not deleted from .default.
// so keep enuming away...
status = EnumerateAndDeltaDeleteKeys( &SourceSubKey, &RefSubKey, &DestinationSubKey, pBasicInfo , pIndentLevel); } //else, status is bad, no point to traverse, we are bailing out
} //else, there is no destination sub key to bother with deletion
} // if-else
} // else, ref had no more sub-keys
status = AlterStatus( status, __LINE__ ); } // else, no more entries
}
(*pIndentLevel)--;
// the typical status would be: STATUS_NO_MORE_ENTRIES
return status; }
/*******************************************************************
* * Function: * EnumerateAndDeltaUpdateKeys * * Parameters: * pSource points to a node under .Dfeault * pref points to a node under our RefHive * pDestination is a node under our TS\install\SW hive * pBasicInfo is a scratch pad passed around which is used to * extract basic Key information * pindextLevel is used to format the debug log output file * * Descritption: * compare source to ref, if new keys/values in source have been created * then create the equivalent keys in our Ts\Install\SW branch (pDestination) * Also, check all values in pSource to values in pRef, if not the same * then delete the equivalent pDestination and create a new value * identical to value from pSource * * Return: * NTSTATUS *******************************************************************/ NTSTATUS EnumerateAndDeltaUpdateKeys( IN KeyNode *pSource, // this is under the latest updated .Default\SW hive
IN KeyNode *pRef, // this was a ref-copy of .Default\SW before the update
IN KeyNode *pDestination,// this is opur private TS-hive
IN KeyBasicInfo *pBasicInfo, IN ULONG *pIndentLevel) { NTSTATUS status=STATUS_SUCCESS, st2; ULONG ulCount=0;
UNICODE_STRING UniString; ULONG size;
(*pIndentLevel)++;
while (NT_SUCCESS_EX(status)) { ULONG ultemp;
status = NtEnumerateKey( pSource->Key(), ulCount++, pBasicInfo->Type() , // keyInformationClass,
pBasicInfo->Ptr(), // pKeyInfo,
pBasicInfo->Size(), // keyInfoSize,
&ultemp);
// pBasicInfo was filled up thru NtEnumerateKey() above
if (NT_SUCCESS_EX(status)) { if ( g_debugIO) { DebugKeyStamp( status , pBasicInfo, *pIndentLevel ); } KeyNode RefSubKey( pRef, pBasicInfo); KeyNode SourceSubKey( pSource,pBasicInfo);
// calling Open() on this may fail, and we will need to delete and recreate it if required.
KeyNode *pDestinationSubKey = new KeyNode( pDestination, pBasicInfo);
RefSubKey.Open(); SourceSubKey.Open();
if ( pDestinationSubKey ) { pDestinationSubKey->Open();
if (NT_SUCCESS_EX( status = SourceSubKey.Status() ) ) { // key is missing from the ref-hive, we should add
// the same sub-tree into our TS\Install\SW hive
if ( RefSubKey.Status() == STATUS_OBJECT_NAME_NOT_FOUND || RefSubKey.Status() == STATUS_OBJECT_PATH_SYNTAX_BAD) { // @@@
// we expect the key not to exist, if it does, then what? delete it?
if ( !NT_SUCCESS( pDestinationSubKey->Status()) ) { // here is what were are doing with the strings:
// 1) get the path below the "\Registry\User\.Default", which would be
// something like "\Software\SomeDir\SomeDirOther\etc", this is the sub-path
// 2) create a new node at the destination, which would be something like:
// \HKLM\SW\MS\Windows NT\CurrentVersion\TS\INstall + the sub path
// we got above.
// this is the trailing part of the key-path missing from our TS hive
PWCHAR pwch; SourceSubKey.GetPath(&pwch); PWCHAR pDestinationSubPath = &pwch[g_length_TERMSRV_USERREGISTRY_DEFAULT ];
PWCHAR pDestinationFullPath= new WCHAR [ g_length_TERMSRV_INSTALL + wcslen( pDestinationSubPath) + sizeof(WCHAR )]; wcscpy( pDestinationFullPath, TERMSRV_INSTALL ); wcscat( pDestinationFullPath, pDestinationSubPath );
DELETE_AND_NULL( pDestinationSubKey ); // create a new KeyNode object where the root will be TERMSRV_INSTALL,
// below which we will create a sub-layer of nodes, or a single node.
pDestinationSubKey = new KeyNode( NULL , pDestination->Masks(), pDestinationFullPath);
// create the new key/branch/values
BOOL bCreate = TRUE; if (wcslen(pDestinationFullPath) > sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) { if (RegPathExistsInOmissionList(pDestinationFullPath + (sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) - 1)) bCreate = FALSE; }
if (bCreate) { status = pDestinationSubKey->CreateEx();
if ( g_debugIO ) { DebugKeyStamp( status, pBasicInfo, *pIndentLevel , L"[KEY WAS CREATED]"); } } else { DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED ); } } } else { // if we have anything but success, set status and bail out
if ( !NT_SUCCESS_EX( status = RefSubKey.Status()) ) { if ( g_debugIO ) { DebugErrorStamp(status, __LINE__ ); } } }
// Key (if it is NEW) is NOT missing from destination hive at this point
// either it did exist, or was created in the above block of code
// check if there are any new values in this node.
KEY_FULL_INFORMATION *ptrInfo; ULONG size;
NTSTATUS st3 = SourceSubKey.Query( &ptrInfo, &size );
if (NT_SUCCESS( st3 )) { ValueFullInfo sourceValueFullInfo( &SourceSubKey );
if ( NT_SUCCESS_EX( status = sourceValueFullInfo.Status() ) ) { for (ULONG ulkey = 0; ulkey < ptrInfo->Values; ulkey++) { status = NtEnumerateValueKey(SourceSubKey.Key(), ulkey, sourceValueFullInfo.Type(), sourceValueFullInfo.Ptr(), sourceValueFullInfo.Size(), &ultemp); // @@@
if ( ! NT_SUCCESS( status )) { DebugErrorStamp( status, __LINE__ ); }
// if the ref key is missing a value, then add
// value to the destination key.
KEY_VALUE_PARTIAL_INFORMATION *pValuePartialInfo; ValuePartialInfo refValuePartialInfo( &RefSubKey );
// It is important to realize that at this point, it is possible
// that ref key did not exist, which follows that refvalue would also
// not exist. The C++ objects RefSubKey and refValuePartialInfo do
// exist as objects, but there is not counter part of actual registry
// data in the registry, hence, the pointers in these object are NULL,
// as expected. Still, NULL data should be interpreted as not present data.
//
// So, the below call refValuePartialInfo.Status() should return TRUE since
// object was created successfully above, but query should return object not
// found or invalid handle without actually calling the reg apis since
// the reg key handle is null.
//
if ( NT_SUCCESS_EX( status = refValuePartialInfo.Status() ) ) { refValuePartialInfo.Query( sourceValueFullInfo.SzName() ); // if .Default\SW has a value that is missing from the ref hive, then add
// corresponding value into our TS\ hive
if ( !NT_SUCCESS( refValuePartialInfo.Status()) ) { // make sure pDestinationSubKey exists, else, create the key first before we
// write a value. It is possible that even though the key did exists in the ref
// hive at the start, a new value was added for the first time, which means that the
// ts hive is getting the key and the value for the first time.
if ( !NT_SUCCESS( pDestinationSubKey->Status() ) ) { // here is what were are doing with the strings:
// 1) get the path below the "\Registry\User\.Default", which would be
// something like "\Software\SomeDir\SomeDirOther\etc", this is the sub-path
// 2) create a new node at the destination, which would be something like:
// \HKLM\SW\MS\Windows NT\CurrentVersion\TS\INstall + the sub path
// we got above.
// this is the trailing part of the key-path missing from our TS hive
PWCHAR pwch; SourceSubKey.GetPath( &pwch ); PWCHAR pDestinationSubPath = &pwch[g_length_TERMSRV_USERREGISTRY_DEFAULT ]; PWCHAR pDestinationFullPath= new WCHAR [ g_length_TERMSRV_INSTALL + wcslen( pDestinationSubPath) + sizeof(WCHAR )]; wcscpy( pDestinationFullPath, TERMSRV_INSTALL ); wcscat( pDestinationFullPath, pDestinationSubPath ); DELETE_AND_NULL( pDestinationSubKey ); // create a new KeyNode object where the root will be TERMSRV_INSTALL,
// below which we will create a sub-layer of nodes, or a single node.
pDestinationSubKey = new KeyNode( NULL , pDestination->Masks(), pDestinationFullPath); // create the new key/branch/values
BOOL bCreate = TRUE; if (wcslen(pDestinationFullPath) > sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) { if (RegPathExistsInOmissionList(pDestinationFullPath + (sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) - 1)) bCreate = FALSE; }
if (bCreate) { status = pDestinationSubKey->CreateEx(); if ( g_debugIO ) { DebugKeyStamp( status, pBasicInfo, *pIndentLevel, L"[KEY WAS CREATED]" ); } } else { DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED ); } } //else, no problem, key did exist and we don't need to create it
// Create value at the destination node
// by now, if we do have a key, then we create values for it but only if
// this key is not pointing to a reg path that we are suppose to ignore due
// to the path being mentioned in the omission list.
if (pDestinationSubKey->Key() ) { if (!HKeyExistsInOmissionList((HKEY)(pDestinationSubKey->Key()))) { ValueFullInfo destinationValue( pDestinationSubKey ); if ( NT_SUCCESS_EX( status = destinationValue.Status()) ) { status = destinationValue.Create( &sourceValueFullInfo ); NT_SUCCESS_EX( status ); // if status is error, we bail out.
} //else, out of memory, var-status is set and we bail out.
} else { DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED ); } } else { DEBUG_INFO( status, pDestinationSubKey , NO_KEY_HANDLE ); }
} else // values are not missing, see if they are the same
{ // compare the two data buffers, if the one from SourceSubKey is
// different than the one from the RefSubKey, then delete
// and create one in DestinationSubKey
ValueFullInfo sourceValue( &SourceSubKey); ValueFullInfo refValue ( &RefSubKey );
if (NT_SUCCESS_EX( status = refValue.Status()) && NT_SUCCESS_EX( status = sourceValue.Status()) ) { sourceValue.Query( sourceValueFullInfo.SzName() ); refValue.Query ( sourceValueFullInfo.SzName() ); if (NT_SUCCESS( refValue.Status()) && NT_SUCCESS( sourceValue.Status())) { BOOLEAN theSame = sourceValue.Compare( &refValue ); if (! theSame ) {
// make sure pDestinationSubKey exists, else, create the key first before we
// write a value. It is possible that even though the key did exists in the ref
// hive at the start, a new value was added for the first time, which means that the
// ts hive is getting the key and the value for the first time.
if ( !NT_SUCCESS( pDestinationSubKey->Status() ) ) { // here is what were are doing with the strings:
// 1) get the path below the "\Registry\User\.Default", which would be
// something like "\Software\SomeDir\SomeDirOther\etc", this is the sub-path
// 2) create a new node at the destination, which would be something like:
// \HKLM\SW\MS\Windows NT\CurrentVersion\TS\INstall + the sub path
// we got above.
// this is the trailing part of the key-path missing from our TS hive
PWCHAR pwch; SourceSubKey.GetPath( &pwch ); PWCHAR pDestinationSubPath = &pwch[g_length_TERMSRV_USERREGISTRY_DEFAULT ];
PWCHAR pDestinationFullPath= new WCHAR [ g_length_TERMSRV_INSTALL + wcslen( pDestinationSubPath) + sizeof(WCHAR )]; wcscpy( pDestinationFullPath, TERMSRV_INSTALL ); wcscat( pDestinationFullPath, pDestinationSubPath ); DELETE_AND_NULL( pDestinationSubKey ); // create a new KeyNode object where the root will be TERMSRV_INSTALL,
// below which we will create a sub-layer of nodes, or a single node.
pDestinationSubKey = new KeyNode( NULL , pDestination->Masks(), pDestinationFullPath); // create the new key/branch/values
BOOL bCreate = TRUE; if (wcslen(pDestinationFullPath) > sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) { if (RegPathExistsInOmissionList(pDestinationFullPath + (sizeof(TERMSRV_INSTALL)/sizeof(WCHAR)) - 1)) bCreate = FALSE; }
if (bCreate) { status = pDestinationSubKey->CreateEx(); if ( g_debugIO ) { DebugKeyStamp( status, pBasicInfo, *pIndentLevel , L"KEY WAS CREATED"); } } else { DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED ); }
} //else, no problem, key did exist and we don't need to create it
// By now, if we do have a key, then we create values for it but only if
// this key is not pointing to a reg path that we are suppose to ignore due
// to the path being mentioned in the omission list.
if (pDestinationSubKey->Key() ) { if (!HKeyExistsInOmissionList((HKEY)(pDestinationSubKey->Key()))) { ValueFullInfo destinationValue( pDestinationSubKey ); if ( NT_SUCCESS( destinationValue.Status() ) ) { // don't care if it exists or not, delete it first
destinationValue.Delete( sourceValueFullInfo.SzName() ); } // else, there is no destination value to delete
// update/create item under destination
// Create a destination value identical to the source value
status = destinationValue.Create( &sourceValue ); } else { DEBUG_INFO( status, pDestinationSubKey , KEY_IGNORED ); } } else { DEBUG_INFO( status, pDestinationSubKey , NO_KEY_HANDLE ); }
// if status is error, we will bail out
if (!NT_SUCCESS_EX( status )) { if (g_debugIO) { DebugErrorStamp(status, __LINE__, &sourceValue ); } } } } // else, values don't exits, doesn't make sense, maybe some dbug code here?
} // else, var-status is set, we bail out.
} //if-else
} //else, out of memory, var-status is set, we bail out
} // for-loop
} //else, out of memory, var-status is set, we bail out
} else { // this sbould not really happen, but for now...
if ( g_debugIO ) { DebugErrorStamp( status, __LINE__ ); } }
// by now, either both source and destination nodes exist, or
// a new destination node was just created above. In any case,
// we can continue the traversal.
if ( NT_SUCCESS( status ) ) { status = EnumerateAndDeltaUpdateKeys( &SourceSubKey, &RefSubKey, pDestinationSubKey, pBasicInfo , pIndentLevel);
NT_SUCCESS_EX( status ); } //else, we are bailing out
} // else, var-status is set, we bail out.
// done with this sub key,
DELETE_AND_NULL( pDestinationSubKey ); } else { status = STATUS_NO_MEMORY; }
status = AlterStatus( status, __LINE__ ); } // else, no more entries
} // no more entries
(*pIndentLevel)--; // the typical status would be: STATUS_NO_MORE_ENTRIES
return status; }
// delete the ref-hive as specific by the uniRef string
NTSTATUS DeleteReferenceHive(WCHAR *uniRef) { if ( g_debugIO) { fwprintf( g_debugFilePointer,L"In %ws\n", L"----DeleteReferenceHive"); fflush( g_debugFilePointer ); }
NTSTATUS status = STATUS_SUCCESS;
KeyNode Old( NULL, KEY_WRITE | KEY_READ | DELETE, uniRef ); if ( NT_SUCCESS( Old.Open() ) ) { Old.DeleteSubKeys(); status = Old.Delete(); // delete the head of the branch
} Old.Close();
return status; }
/****************************************************************
* * Function: * CreateReferenceHive * * Parameters: * uniSource (source ) string points to the node under .Default * uniRef (ref ) string point to TS\Install\RefHive * UniDest (Destination) string points to TS\Install\Software * * Description: * from the .Default (source) hive, copy into TS\install\RefHive * source hive is specified by the uniSoure string, and the * ref-hive is specified by the uniRef string. * * Return: * NTSTATUS, if successful, then STATUS_SUCCESS * ****************************************************************/ NTSTATUS CreateReferenceHive(WCHAR *uniSource, WCHAR *uniRef) { if ( g_debugIO) { fwprintf( g_debugFilePointer,L"In %ws\n", L"----CreateReferenceHive"); fflush( g_debugFilePointer ); }
// 1-COPY
// copy all keys under .Default\Software into a special location
// under our TS hive, let's call it the RefHive
// This will act as the reference hive
NTSTATUS status = STATUS_SUCCESS; ULONG indentLevel=0;
// start creating our cache Ref hive
KeyNode Ref( NULL, MAXIMUM_ALLOWED, uniRef );
// if we were able to create our RefHive, then continue...
if ( NT_SUCCESS_EX( status = Ref.Create() ) ) { KeyNode Source(NULL, KEY_READ, uniSource ); // open the source reg-key-path
if (NT_SUCCESS_EX( status = Source.Open() )) { KeyBasicInfo kBasicInfo; if (NT_SUCCESS_EX( status = kBasicInfo.Status() )) { // this will be a recursive call, so we are saving allocation
// cycles by passing kBasicInfo as scratch pad.
status = EnumerateAndCreateRefHive( &Source, &Ref, &kBasicInfo, &indentLevel); } } }
if ( status == STATUS_NO_MORE_ENTRIES) { status = STATUS_SUCCESS; }
return status; }
/************************************************************************
* * Function: * DeltaDeleteKeys(WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest) * * Parameters: * uniSource (source ) string points to the node under .Default * uniRef (ref ) string point to TS\Install\RefHive * UniDest (Destination) string points to TS\Install\Software * * Description: * compare .Dfeault keys to the equivalent keys in RefHive. If keys are * missing from .Default, then delete the equivalent keys from our * HKLM\...\TS\ hive * * Return: * NTSTATUS, if successful, then STATS_SUCCESS * ************************************************************************/ NTSTATUS DeltaDeleteKeys(WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest) { if ( g_debugIO) { fwprintf( g_debugFilePointer,L"In %ws\n", L"----DeltaDeleteKeys"); fflush( g_debugFilePointer ); }
// Step2-DELETE
// compare .Dfeault keys to the equivalent keys in RefHive. If keys are
// missing from .Default, then delete the equivalent keys from our
// HKLM\...\TS\ hive
KeyNode Source( NULL, KEY_READ, uniSource ); KeyNode Ref( NULL, MAXIMUM_ALLOWED, uniRef ); KeyNode Destination( NULL, MAXIMUM_ALLOWED, uniDest );
Source.Open(); Ref.Open(); Destination.Open();
ULONG indentLevel=0; NTSTATUS status = STATUS_SUCCESS;
if ( NT_SUCCESS_EX( status = Source.Status() ) && NT_SUCCESS_EX( status = Ref.Status() ) && NT_SUCCESS_EX( status = Destination.Status() ) ) { KeyBasicInfo basicInfo; if( NT_SUCCESS_EX( status = basicInfo.Status() ) ) { // walk and compare, if missing from Source, then delete from Destination
status = EnumerateAndDeltaDeleteKeys( &Source, &Ref, &Destination, &basicInfo, &indentLevel); } }
if ( status == STATUS_NO_MORE_ENTRIES) { status = STATUS_SUCCESS; }
return status; }
/************************************************************************
* * Function: * DeltaUpdateKeys(WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest) * * Parameters: * uniSource (source ) string points to the node under .Default * uniRef (ref ) string point to TS\Install\RefHive * UniDest (Destination) string points to TS\Install\Software * * Description: * Step-3 CREATE/Update keys and values * compare .Default keys to the equivalent keys in RefHive, if keys are * present in .Default that are missing from RefHive, then, add those keys * to our HKLM\...\TS hive. Do the same for the values. * Then, compare values from .Default to values in .Ref. If values have * changed, then delete the value from our destination hive and create a * new one with the appropriate data from .Default * * Return: * NTSTATUS, if successful, then STATS_SUCCESS * ************************************************************************/ NTSTATUS DeltaUpdateKeys (WCHAR *uniSource, WCHAR *uniRef, WCHAR *uniDest) { if ( g_debugIO) { fwprintf( g_debugFilePointer,L"In %ws\n", L"----DeltaUpdateKeys"); fflush( g_debugFilePointer ); } // 3-CREATE
// compare .Default keys to the equivalent keys in RefHive, if keys are
// present in .Default that are missing from RefHive, then, add those keys
// to our HKLM\...\TS hive
KeyNode Source( NULL, KEY_READ, uniSource ); KeyNode Ref( NULL, MAXIMUM_ALLOWED, uniRef ); KeyNode Destination( NULL, MAXIMUM_ALLOWED, uniDest );
Source.Open(); Ref.Open(); Destination.Open();
NTSTATUS status; ULONG indentLevel=0;
if ( NT_SUCCESS_EX( status = Source.Status() ) && NT_SUCCESS_EX( status = Ref.Status() ) && NT_SUCCESS_EX( status = Destination.Status() ) ) {
KeyBasicInfo basicInfo;
// Constructor in KeyBasicInfo above allocates memory for pInfo
// check if memory allocation of pInfo succeeded
status = basicInfo.Status(); if (status != STATUS_SUCCESS) { return status; }
// walk and compare, if missing from Source, then delete from Destination
status = EnumerateAndDeltaUpdateKeys( &Source, &Ref, &Destination, &basicInfo, &indentLevel); }
if ( status == STATUS_NO_MORE_ENTRIES) { status = STATUS_SUCCESS; }
return status; }
|