|
|
// Copyright (c) 1996-1999 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: svrsvc.cxx
//
// Contents: Code for CTrkSvrSvc
//
// Classes:
//
// Functions:
//
//
//
// History: 18-Nov-96 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include "trksvr.hxx"
#include "ntlsa.h"
#define THIS_FILE_NUMBER SVRSVC_CXX_FILE_NO
#if DBG
DWORD g_Debug = 0; #endif
const extern TCHAR s_tszKeyNameLinkTrack[] = TEXT("System\\CurrentControlSet\\Services\\TrkSvr\\Parameters");
// This is the SID used internally by CVerifyAuthentication
PSID CVerifyAuthentication::_psidAuthenticatedUsersGroup = NULL;
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::Initialize
//
// Initialize the TrkSvr service.
//
//+----------------------------------------------------------------------------
void CTrkSvrSvc::Initialize( SVCHOST_GLOBAL_DATA * pSvcsGlobalData ) {
__try { _cLowestAvailableThreads = _cAvailableThreads = MAX_SVR_THREADS;
_fInitializeCalled = TRUE; g_ptrksvr = this; _pSvcsGlobalData = pSvcsGlobalData;
// Initialize the object that manages the SCM.
_svcctrl.Initialize(TEXT("TrkSvr"), this); // Initialize registry-configurable parameters.
_configSvr.Initialize();
// If requested, prepare to log all operations (to a file)
if( _configSvr.UseOperationLog() ) _OperationLog.Initialize( _configSvr.GetOperationLog() );
TrkLog(( TRKDBG_SVR, TEXT("Distributed Link Tracking (Server) service starting on thread=%d(0x%x)"), GetCurrentThreadId(), GetCurrentThreadId() ));
// This is a hacked stub that looks and acts like the Win32 thread pool services
#ifdef PRIVATE_THREAD_POOL
{ HRESULT hr = S_OK; g_pworkman2 = new CThreadPoolStub; if( NULL == g_pworkman2 ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create the thread pool manager") )); TrkRaiseWin32Error( ERROR_NOT_ENOUGH_MEMORY ); }
hr = g_pworkman2->Initialize(); if( FAILED(hr) ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't initialize the thread pool manager") )); TrkRaiseException( hr ); } } #endif
// The denial checker provides protection against a denial-of-service
// attack, where a client floods us with calls.
_denial.Initialize(_configSvr.GetHistoryPeriod() );
// Initialize the helper class that we use for validating that callers
// are in the Authenticated Users group
CVerifyAuthentication::Initialize();
// This is also a denial checker, which protects against a
// single client making multiple create-volume calls.
_activeCreates.Initialize( _configSvr.GetSvrMaxRpcCalls() );
// This critsec protects _cWritesPerHour & _cftWritesPerHour.
// See CTrkSvrSvc::CheckWritesPerHour
_csWritesPerHour.Initialize();
// This maintains the "time" for purposes of refreshing entries.
_refreshSequence.Initialize();
// The cross-domain table
_cdt.Initialize();
// The intra-domain table
_idt.Initialize( &_configSvr, &_qtable );
// The volume table
_voltab.Initialize( &_configSvr, &_qtable );
// The quota manager
_qtable.Initialize(&_voltab, &_idt, this, &_configSvr );
// Set the quota timer. This was originally every 30 days, but is now
// every day. In order to maintain compatibility with the tests, we
// still use the GCPeriod value (30 days), but divide it by the new
// GCDivisor value (30) to get the correct period.
// This timer doesn't have a standard retry, because of the way
// we hesitate 30 minutes before doing anything. So retries are
// done explicitely.
_timerGC.Initialize(this, TEXT("NextGarbageCollectTime"), // This is a persistent timer
0, // Context ID
_configSvr.GetGCPeriod() / _configSvr.GetGCDivisor(), CNewTimer::NO_RETRY, 0, 0, 0 ); // No retries or max lifetime
_timerGC.SetRecurring(); TrkLog(( TRKDBG_VOLUME, TEXT("GC timer: %s"), (const TCHAR*) CDebugString(_timerGC) ));
// Used in the Timer method to determine if we should reset the
// move table counter value.
_MoveCounterReset.Initialize();
// Initialize ourself as an RPC server
_rpc.Initialize( _pSvcsGlobalData, &_configSvr );
// Tell the SCM that we're running.
_svcctrl.SetServiceStatus(SERVICE_RUNNING, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, NO_ERROR);
_OperationLog.Add( COperationLog::TRKSVR_START ); }
__except( BreakOnDebuggableException() ) { // Don't log an event for protseq-not-supported; this happens during a normal
// setup.
if( HRESULT_FROM_WIN32(RPC_S_PROTSEQ_NOT_SUPPORTED) != GetExceptionCode() ) { TrkReportEvent( EVENT_TRK_SERVICE_START_FAILURE, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CHexStringize( GetExceptionCode() )), NULL ); } TrkRaiseException( GetExceptionCode() ); } }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::UnInitialize
//
// Cancel any out-going RPCs, stop all timers, close everything down,
// and send a service_stopped to the SCM.
//
//+----------------------------------------------------------------------------
void CTrkSvrSvc::UnInitialize(HRESULT hr) { if (_fInitializeCalled) { _fInitializeCalled = FALSE;
// Cancel any out-going RPCs on threads in this service
if( NULL != g_pActiveThreadList ) g_pActiveThreadList->CancelAllRpc();
// stop classes that use threads first ...
_rpc.UnInitialize( _pSvcsGlobalData ); CVerifyAuthentication::Uninitialize();
_timerGC.UnInitialize(); _csWritesPerHour.UnInitialize();
// ... then release used resources
_qtable.UnInitialize(); _voltab.UnInitialize(); _idt.UnInitialize(); _cdt.UnInitialize(); _dbc.UnInitialize(); _denial.UnInitialize();
if (_configSvr.GetTestFlags() & TRK_TEST_FLAG_WAIT_ON_EXIT) { TrkLog((TRKDBG_ERROR, TEXT("Waiting 60 seconds before exitting for heap dump"))); Sleep(60000); }
#if PRIVATE_THREAD_POOL
{ g_pworkman2->UnInitialize(); delete g_pworkman2; g_pworkman2 = NULL; } #endif
g_ptrksvr = NULL;
// If the error is protseq-not-supported, ignore it. This is normal
// during setup.
if( (hr & 0x0FFF0000) == FACILITY_WIN32 ) hr = hr & ~(0x0FFF0000);
_svcctrl.SetServiceStatus(SERVICE_STOPPED, 0, HRESULT_FROM_WIN32(RPC_S_PROTSEQ_NOT_SUPPORTED) == hr ? 0 : hr ); //_svcctrl.UnInitialize();
} }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::ServiceHandler
//
// This method gets called by the SCM for notification of all service
// activity.
//
// NOTE: In services.exe, this method is called on the one and only ServiceHandler
// thread. So while we execute, no other service in this process can
// receive notifications. Thus it is important that we do nothing
// blocking or time-consuming here.
//
//+----------------------------------------------------------------------------
DWORD CTrkSvrSvc::ServiceHandler(DWORD dwControl, DWORD dwEventType, PVOID EventData, PVOID pData) { DWORD dwRet = NO_ERROR;
switch (dwControl) { case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP:
_fStopping = TRUE; _qtable.OnServiceStopRequest(); ServiceStopCallback( this, FALSE );
break; case SERVICE_CONTROL_PAUSE: break; case SERVICE_CONTROL_CONTINUE: break; case SERVICE_CONTROL_INTERROGATE: break; default: dwRet = ERROR_CALL_NOT_IMPLEMENTED; break; }
return(dwRet); }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::RaiseIfStopped
//
// This method raises an exception if a global flag is set indicating
// that we've received a service stop/shutdown request. This is used
// in places where we have a thread that could run for a while; we periodically
// call this method to prevent service stop from blocking.
//
//+----------------------------------------------------------------------------
void CTrkSvrSvc::RaiseIfStopped() { if ( * _svcctrl.GetStopFlagAddress() ) TrkRaiseException( TRK_E_SERVICE_STOPPING ); }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::CheckWritesPerHour
//
// Check _cWritesPerHour to see if we're writing to much to the DS.
// This is a simplistic algorithm in an effort to reduce risk. We
// just let _cWritesPerHour increment until it hits the max, then
// check to see when that count was started. If more than an
// hour ago, then reset the count & the clock.
//
//+----------------------------------------------------------------------------
BOOL CTrkSvrSvc::CheckWritesPerHour() { BOOL fExceeded = FALSE;
if( _cWritesPerHour >= _configSvr.GetMaxDSWritesPerHour() ) { _csWritesPerHour.Enter(); __try { // Check the count again, as it may have changed whil we
// were waiting for the critsec.
if( _cWritesPerHour >= _configSvr.GetMaxDSWritesPerHour() ) { CFILETIME cft; // Defaults to current time
// Did the "hour" for _cWritesPerHour actually start more
// than an hour ago?
cft.DecrementSeconds( _configSvr.GetMaxDSWritesPeriod() ); // An hour
if( cft > _cftWritesPerHour ) { TrkLog(( TRKDBG_SVR, TEXT("Resetting writes-per-hour clock (%d)"), _cWritesPerHour ));
// Yes, this write is OK, and we should reset the write time.
_cftWritesPerHour = CFILETIME(); _cWritesPerHour = 0; _Stats.cCurrentFailedWrites = 0; } else { TrkLog(( TRKDBG_WARNING, TEXT("Exceeded writes-per-hour (started at %s)"), (const TCHAR*) CDebugString(_cftWritesPerHour) ));
if( 0 == _Stats.cCurrentFailedWrites ) _Stats.cMaxDsWriteEvents++; _Stats.cCurrentFailedWrites++;
fExceeded = TRUE; } } } __finally { _csWritesPerHour.Leave(); } }
return fExceeded; }
void CTrkSvrSvc::Scan( IN const CDomainRelativeObjId * pdroidNotificationCurrent, OPTIONAL IN const CDomainRelativeObjId * pdroidNotificationNew, OPTIONAL IN const CDomainRelativeObjId & droidBirth, OUT CDomainRelativeObjId * pdroidList, IN int cdroidList, OUT int * pcSegments, IN OUT CDomainRelativeObjId * pdroidScan, OUT BOOL * pfStringDeleted ) { CDomainRelativeObjId droidNextBirth, droidNextNew; BOOL fFound = FALSE; BOOL fBirthSame = FALSE; BOOL fCycle = FALSE;
*pfStringDeleted = FALSE;
//
// loop through the string until the birth ids don't match, or
// we've run out of buffer space, or we get to the end of the string
//
do { if (pdroidNotificationCurrent && *pdroidScan == *pdroidNotificationCurrent) { TrkAssert(pdroidNotificationNew);
droidNextNew = *pdroidNotificationNew; droidNextBirth = droidBirth; fFound = TRUE; *pfStringDeleted = FALSE; } else { fFound = _idt.Query(*pdroidScan, &droidNextNew, &droidNextBirth, pfStringDeleted ); RaiseIfStopped(); }
if (fFound) { TrkLog((TRKDBG_MEND, TEXT("CTrkSvrSvc::Scan() - iSegment=%d, %s --> %s [%s] found"), *pcSegments, static_cast<const TCHAR*>(CAbbreviatedIDString(*pdroidScan)), static_cast<const TCHAR*>(CAbbreviatedIDString(droidNextNew)), static_cast<const TCHAR*>(CAbbreviatedIDString(droidNextBirth)) ));
// Check to see if we've already been here before.
// E.g., don't loop forever on A->Ba, B->Aa.
for( int j = 0; j < *pcSegments; j++ ) { if( pdroidList[ j ] == droidNextNew ) { TrkLog(( TRKDBG_MEND, TEXT("Cycle detected during mend (on %s)"), static_cast<const TCHAR*>(CAbbreviatedIDString(*pdroidScan)) )); fCycle = TRUE; break; } }
if( !fCycle ) { fBirthSame = droidNextBirth == droidBirth; if (fBirthSame) { pdroidList[ (*pcSegments)++ ] = *pdroidScan; *pdroidScan = droidNextNew; } else { // We can stop searching. We found a segment that starts
// with *pdroidScan, but it's from another string because
// it has a different birth ID.
TrkLog(( TRKDBG_MEND, TEXT("Birth IDs don't match: %s, %s"), (const TCHAR*) CDebugString(droidBirth), (const TCHAR*) CDebugString(droidNextBirth) )); } } } } while ( *pcSegments < cdroidList && fFound && fBirthSame && !fCycle );
if ( *pcSegments == cdroidList || fCycle ) { TrkRaiseException(TRK_E_TOO_MANY_UNSHORTENED_NOTIFICATIONS); }
}
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::MoveNotify
//
// Handle a move notify request from trkwks.
//
// This routine is complicated by DS replication. It is possible that two trksvr
// services may modify the same entry in the IDT within a replication window.
// The only way to prevent this is to design such that only the designated DC
// modifies entries. For this MoveNotify routine, that would mean that an
// entry would be added for each notification, the designated DC would then
// shorten the base entry, and delete this new one. That's not friendly
// to the DS, however, because deleted objects must continue to be stored
// for an extended period of time.
//
// Consequently, if this notify modifies an existing entry, this routine
// performs a modify rather than an add. For example, if a file is
// moved from A to B to C, and this routine is being called for that
// second move, it would just modify the existing entry from A->B to
// A->C.
//
// The risk here is that another DC will attempt to modify this entry
// within the same replication window. We don't have to worry though
// about another DC doing a notify; trkwks only sends to one DC.
// There are two cases to worry about. One is the case where another DC
// marks an entry to be deleted. If that happens after we do the modify
// here, then there is no problem; the entry is no longer needed anyway.
// If that happens before we do our modify here, then the delete flag
// will be lost. This case is rare, and the unnecessary entry won't
// stay in the table forever; it will be garbage collected.
//
// The other case of potential conflict is if an entry has not
// yet been counted; in this case the designated DC might count
// it and clear the uncounted flag. If our modify causes that
// flag to be uncleared, then the move table count would be corrupted.
// So if the uncounted flag is set, we do an add rather than a modify.
//
//+----------------------------------------------------------------------------
void CTrkSvrSvc::MoveNotify(const CDomainRelativeObjId &droidCurrent, const CDomainRelativeObjId &droidBirth, const CDomainRelativeObjId &droidNew, BOOL *pfQuotaExceeded ) { BOOL fAdded = FALSE, fModified = FALSE, fExists = FALSE; BOOL fDeleted = FALSE, fCounted = FALSE;
// ignore cross-domain moves for now
CDomainRelativeObjId droidNextNew; CDomainRelativeObjId droidNextBirth; CDomainRelativeObjId droidNewIDT, droidBirthIDT;
TrkLog((TRKDBG_MOVE, TEXT("CTrkSvrSvc::MoveNotify\n curr = %s\n new = %s\n birth = %s"), (const TCHAR*)CDebugString(droidCurrent), (const TCHAR*) CDebugString(droidNew), (const TCHAR*) CDebugString(droidBirth) ));
// Does the entry exist already?
fExists = _idt.Query( droidBirth, &droidNewIDT, &droidBirthIDT, &fDeleted, &fCounted );
#if DBG
if( fExists ) TrkLog(( TRKDBG_MOVE, TEXT("Birth entry already exists (%s, %s)"), fDeleted ? TEXT("deleted") : TEXT("not deleted"), fCounted ? TEXT("counted") : TEXT("not counted") )); #endif
if( fExists && fCounted && droidNewIDT == droidCurrent ) { TrkLog(( TRKDBG_MOVE, TEXT("Attempting to modify existing entry") ));
// The birth entry for this file already points to the source
// of the notify. We can just modify it.
fModified = _idt.Modify( droidBirth, droidNew, droidBirth ); }
// If the modify didn't work or wasn't attempted, then just add this
// new entry
if( !fModified ) fAdded = _idt.Add( droidCurrent, droidNew, droidBirth, pfQuotaExceeded );
TrkLog((TRKDBG_MEND, TEXT("CTrkSvrSvc::MoveNotify() %s %s --> %s [%s]"), fModified ? TEXT("modified") : (fAdded ? TEXT("added") : TEXT("couldn't be added") ), static_cast<const TCHAR*>(CAbbreviatedIDString(droidCurrent)), static_cast<const TCHAR*>(CAbbreviatedIDString(droidNew)), static_cast<const TCHAR*>(CAbbreviatedIDString(droidBirth)) ));
}
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::Search
//
// Given a droid, look up the new droid for that object, and look up
// the mcid of the machine that owns that droid's volume.
//
//+----------------------------------------------------------------------------
void CTrkSvrSvc::Search(/*in, out*/ TRK_FILE_TRACKING_INFORMATION *pSearch) { HRESULT hr = S_OK; CDomainRelativeObjId droidNew; CDomainRelativeObjId droidBirth; CMachineId mcidNew; BOOL fFoundObject;
IFDBG( TCHAR * ptszRoute=TEXT(""); )
TrkLog(( TRKDBG_MEND, TEXT("Searching for %s"), static_cast<const TCHAR*>(CAbbreviatedIDString(pSearch->droidLast)) ));
// If all the move notifies for a file have reached the DC, we can do a
// lookup based on the birth ID. But if one segment is missing, this would fail.
// So we look up based on the last ID first, and if that files try the birth ID.
// Try to map the last ID to the current droid.
fFoundObject = _idt.Query( pSearch->droidLast, &droidNew, &droidBirth); if ( fFoundObject ) { IFDBG( ptszRoute = TEXT("'last' found in IDT"); ) } else { // We couldn't find the last known ID. Try mapping
// from the birth ID.
fFoundObject = _idt.Query( pSearch->droidBirth, &droidNew, &droidBirth ); if( fFoundObject ) { IFDBG( ptszRoute = TEXT("'birth' found in IDT"); ) } }
// Did we find the new droid for the file?
if ( fFoundObject ) { // Yes, we found it. Is it really the same file (the birth ID matches)?
if( droidBirth != pSearch->droidBirth ) { TrkLog(( TRKDBG_MEND, TEXT("Birth ID unexpected:\n %s,\n %s,\n %s"), (const TCHAR*) CDebugString(droidBirth), (const TCHAR*) CDebugString(pSearch->droidBirth), (const TCHAR*) CDebugString(droidNew) )); pSearch->hr = TRK_E_NOT_FOUND; goto Exit; } // We have a good ID. This file may have multiple segments in the DS.
// Starting with the one we have, scan across any additional segments
// to find the most up-to-date droid.
CDomainRelativeObjId droidList[MAX_SHORTENABLE_SEGMENTS]; int cSegments = 0; BOOL fStringDeleted = FALSE; Scan( NULL, NULL, droidBirth, droidList, sizeof(droidList)/sizeof(droidList[0]), &cSegments, &droidNew, &fStringDeleted );
} else { // We couldn't find either the birth or last ID.
pSearch->hr = TRK_E_NOT_FOUND; TrkLog(( TRKDBG_MEND, TEXT("neither 'birth' nor 'last' found") )); }
// If we found the object in the move table, look up the machine ID in the
// volume table.
if (fFoundObject) { TrkLog((TRKDBG_MEND, TEXT("CTrkSvrSvc::Search( birth=%s last=%s ) successful, 'new=%s', %s"), static_cast<const TCHAR*>(CAbbreviatedIDString(pSearch->droidBirth)), static_cast<const TCHAR*>(CAbbreviatedIDString(pSearch->droidLast)), static_cast<const TCHAR*>(CAbbreviatedIDString(droidNew)), ptszRoute ));
// Find the volume that holds this droid.
pSearch->hr = _voltab.FindVolume( droidNew.GetVolumeId(), &mcidNew ); if( S_OK == pSearch->hr ) { // We found the volume.
TrkLog(( TRKDBG_MEND, TEXT("CTrkSvrSvc::Search, volid found (%s -> %s)"), (const TCHAR*) CDebugString(droidNew.GetVolumeId()), (const TCHAR*) CDebugString(mcidNew) ));
pSearch->hr = S_OK; pSearch->droidLast = droidNew; pSearch->mcidLast = mcidNew; } else { // We were able to find the object in the move table, but couldn't
// find the volume in the volume table.
TrkLog(( TRKDBG_MEND, TEXT("CTrkSvrSvc::Search, volid not found (%s, %08x)"), (const TCHAR*) CDebugString(droidNew.GetVolumeId()), pSearch->hr )); pSearch->hr = TRK_E_NOT_FOUND; }
} else { HRESULT hr = S_OK;
// We couldn't find the object in the move table.
TrkLog((TRKDBG_MEND, TEXT("CTrkSvrSvc::Search( birth=%s last=%s ) not found, %s"), static_cast<const TCHAR*>(CAbbreviatedIDString(pSearch->droidBirth)), static_cast<const TCHAR*>(CAbbreviatedIDString(pSearch->droidLast)), ptszRoute));
// As an optimization, try looking up the last volume anyway. When this
// search request fails, the trkwks service typically looks up the location
// of the volume ID in droidLast, so we do that lookup now instead of forcing
// trkwks to make a separate request.
hr = _voltab.FindVolume( pSearch->droidLast.GetVolumeId(), &mcidNew ); if( S_OK == hr ) { TrkLog(( TRKDBG_MEND, TEXT("CTrkSvrSvc::Search, but last volid found (%s -> %s)"), (const TCHAR*) CDebugString(pSearch->droidLast.GetVolumeId()), (const TCHAR*) CDebugString(mcidNew) ));
pSearch->hr = TRK_E_NOT_FOUND_BUT_LAST_VOLUME_FOUND; pSearch->mcidLast = mcidNew; } else { TrkLog(( TRKDBG_MEND, TEXT("CTrkSvrSvc::Search, volid not found either (%s, %08x)"), (const TCHAR*) CDebugString(pSearch->droidLast.GetVolumeId()), pSearch->hr )); pSearch->hr = TRK_E_NOT_FOUND_AND_LAST_VOLUME_NOT_FOUND; } }
Exit:
return; }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::old_Search
//
// This method is provided for compatibility with NT5/Beta2 clients. Those
// clients would do two RPCs, one to get the new droid, then another to map
// the volid in that droid to an mcid. In the modern SEARCH request, the
// client gets both back in a single call.
//
// The distinction between the two kinds of clients is made by the
// TRKSVR_MESSAGE_TYPE in the request. Old clients pass the value
// now defined as old_SEARCH, new clients pass the value SEARCH.
//
//+----------------------------------------------------------------------------
void CTrkSvrSvc::old_Search(/*in, out*/ old_TRK_FILE_TRACKING_INFORMATION *pSearch) { TRK_FILE_TRACKING_INFORMATION FileTrkInfo;
FileTrkInfo.droidBirth = pSearch->droidBirth; FileTrkInfo.droidLast = pSearch->droidLast;
Search(&FileTrkInfo); pSearch->hr = FileTrkInfo.hr; pSearch->droidLast = FileTrkInfo.droidLast; }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::Timer
//
// This callback method is called by the GC timer when it's time to do a
// GC.
//
// This method doesn't raise.
//
//+----------------------------------------------------------------------------
PTimerCallback::TimerContinuation CTrkSvrSvc::Timer( ULONG ulTimerContext ) { HRESULT hr = S_OK; BOOL fInvalidateCache = FALSE; TimerContinuation continuation = CONTINUE_TIMER; NTSTATUS Status = STATUS_SUCCESS;
TrkLog(( TRKDBG_SVR, TEXT("\nGC timer has fired") ));
__try { // Only the designated DC does garbage collecting.
if( !_qtable.IsDesignatedDc( TRUE ) ) // TRUE => raise on error
{ TrkLog(( TRKDBG_SVR, TEXT("Not GC-ing; not the designated DC") )); continuation = CONTINUE_TIMER; __leave; }
// See if this domain is too young to do anything.
if( _refreshSequence.GetSequenceNumber() < static_cast<SequenceNumber>(_configSvr.GetGCMinCycles()) ) { TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR, TEXT("Nothing to GC (%d)"), _refreshSequence.GetSequenceNumber() ));
continuation = CONTINUE_TIMER; __leave; }
// Is this the part one of the timer ?
if( !_fHesitatingBeforeGC ) { // Yes, this is part one. We'll do some work, then
// reset the timer for a small delay (so that we don't
// do a bunch of work during system initialization).
#if DBG
{ if( _configSvr.GetGCHesitation() > (5*60) ) TrkLog(( TRKDBG_SVR, TEXT("Hesitating for %d minutes before running GC"), _configSvr.GetGCHesitation() / 60 )); else TrkLog(( TRKDBG_SVR, TEXT("Hesitating for %d seconds before running GC"), _configSvr.GetGCHesitation() )); } #endif
_fHesitatingBeforeGC = TRUE;
_timerGC.ReInitialize( _configSvr.GetGCHesitation() ); // Doesn't raise
continuation = CONTINUE_TIMER;
} else { _fHesitatingBeforeGC = FALSE;
_Stats.cEntriesGCed = 0;
// Update the sequence number
_refreshSequence.IncrementSequenceNumber(); TrkLog(( TRKDBG_SVR, TEXT("Updated the GC counter to %d"), _refreshSequence.GetSequenceNumber() ));
// See if we need to invalidate the move table count cache (once a month).
// This is done for robustness, so that if the count gets out of sync for any
// reason, we self-correct. _MoveCounterReset holds the sequence number
// of the last time we did an invalidate.
if( (SequenceNumber) _MoveCounterReset.GetValue() >= _refreshSequence.GetSequenceNumber() ) { // Invalid value
TrkLog(( TRKDBG_WARNING, TEXT("_MoveCounterReset is invalid (%d, %d), resetting"), _MoveCounterReset.GetValue(), _refreshSequence.GetSequenceNumber() )); _MoveCounterReset.Set ( (DWORD) _refreshSequence.GetSequenceNumber() ); } else if( _MoveCounterReset.GetValue() + _configSvr.GetGCDivisor() <= _refreshSequence.GetSequenceNumber() ) { TrkLog(( TRKDBG_SVR | TRKDBG_GARBAGE_COLLECT, TEXT("Cache will be invalidated (%d)"), _MoveCounterReset.GetValue() )); fInvalidateCache = TRUE; }
// Calculate the seq number of the oldest entry to keep.
ULONG seqOldestToKeep = _refreshSequence.GetSequenceNumber() - _configSvr.GetGCMinCycles() + 1;
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR, TEXT("\nGarbage collecting all entries older than %d"), seqOldestToKeep));
// Delete old entries from the move table
_Stats.cEntriesGCed += (SHORT)_idt.GarbageCollect( _refreshSequence.GetSequenceNumber(), seqOldestToKeep, _svcctrl.GetStopFlagAddress() );
// And delete old entries from the volume table
_Stats.cEntriesGCed += (SHORT)_voltab.GarbageCollect( _refreshSequence.GetSequenceNumber(), seqOldestToKeep, _svcctrl.GetStopFlagAddress() );
_OperationLog.Add( COperationLog::TRKSVR_GC, S_OK, CMachineId(MCID_INVALID), seqOldestToKeep, _Stats.cEntriesGCed );
// Reset the timer to its normal period.
_timerGC.ReInitialize( _configSvr.GetGCPeriod() / _configSvr.GetGCDivisor() ); continuation = CONTINUE_TIMER; }
} __except( BreakOnDebuggableException() ) { hr = GetExceptionCode(); TrkLog(( TRKDBG_WARNING, TEXT("Ignoring exception in CTrkSvrSvc::Timer (%08x)"), hr )); _OperationLog.Add( COperationLog::TRKSVR_GC, hr, CMachineId(MCID_INVALID) ); }
// The Quota table's cached counts may be bad now that we've deleted
// entries from the tables.
if( fInvalidateCache ) { _qtable.InvalidateCache(); _MoveCounterReset.Set( (DWORD) _refreshSequence.GetSequenceNumber() ); }
TrkAssert( _timerGC.IsRecurring() ); return( continuation ); }
SequenceNumber CTrkSvrSvc::GetSequenceNumber( const CMachineId & mcidClient, const CVolumeId & volume ) { HRESULT hr; SequenceNumber seq; FILETIME ftLastRefresh; hr = _voltab.QueryVolume(mcidClient, volume, &seq, &ftLastRefresh);
if( S_OK != hr ) { // Raise on error. E.g. if mcidClient doesn't own this volume.
TrkLog(( TRKDBG_ERROR, TEXT("CTrkSvrSvc::GetSequenceNumber --> %08x"), hr )); TrkRaiseException(hr); }
return(seq); }
void CTrkSvrSvc::SetSequenceNumber( const CVolumeId & volume, // must ensure that validation already done for volume
SequenceNumber seq ) { HRESULT hr;
hr = _voltab.SetSequenceNumber( volume, seq ); if (hr != S_OK) { TrkRaiseException( hr ); } }
HRESULT CTrkSvrSvc::MoveNotify( const CMachineId & mcidClient, TRKSVR_CALL_MOVE_NOTIFICATION * pMove ) { HRESULT hr = S_OK; SequenceNumber seqExpected; CVolumeId volid;
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cMoveNotifyRequests) );
pMove->cProcessed = 0;
//
// ensure we have at least one notification because we assume that
// the current volume of the first notification is the same for all
// of the notifications in this rpc.
//
if (pMove->cNotifications == 0) { return(S_OK); }
// Get the machine for this volume and the sequence number expected
// ensure that the machine is actually the owner of the volume.
// (This will raise if mcidClient doesn't own this volid.)
volid = *pMove->pvolid; seqExpected = GetSequenceNumber(mcidClient, volid);
// Is this the sequence number we were expecting for this client volume?
TrkLog((TRKDBG_MOVE, TEXT("sequence no %d %sexpected for %s (%sforcing, expected %d)"), pMove->seq, seqExpected != pMove->seq ? TEXT("un") : TEXT(""), (const TCHAR*) CDebugString(volid), pMove->fForceSeqNumber ? TEXT("") : TEXT("not "), seqExpected ));
if( seqExpected != pMove->seq ) { // No, it's not the right sequence number.
if( !pMove->fForceSeqNumber ) { // The caller hasn't requested an override, so this is an error.
pMove->seq = seqExpected; return TRK_S_OUT_OF_SYNC; } }
//
// Before processing the actual move notifications, ensure that we
// have enough quota... assume that each move notify is going to
// to need one unit of quota (the writes will actually update
// the quota accurately.)
//
#ifdef VOL_QUOTA
if (pMove->cNotifications > GetAvailableNotificationQuota( ) ) { TrkRaiseException( TRK_E_NOTIFICATION_QUOTA_EXCEEDED ); } #endif
while (pMove->cProcessed < pMove->cNotifications) { // the only errors are fatal since we always make a record of
// a notification or merge it with an existing record
BOOL fQuotaExceeded = FALSE;
if( CheckWritesPerHour() ) { TrkLog(( TRKDBG_SVR, TEXT("Stopping move-notifications due to too many writes (%d)"), NumWritesThisHour() )); break; }
MoveNotify(CDomainRelativeObjId( volid, pMove->rgobjidCurrent[pMove->cProcessed] ), pMove->rgdroidBirth[pMove->cProcessed], pMove->rgdroidNew[pMove->cProcessed], &fQuotaExceeded );
if( fQuotaExceeded ) { hr = TRK_S_NOTIFICATION_QUOTA_EXCEEDED; break; }
pMove->cProcessed++; IncrementWritesPerHour();
RaiseIfStopped(); }
if( 0 != pMove->cProcessed ) { SetSequenceNumber( volid, pMove->seq + pMove->cProcessed ); TrkLog(( TRKDBG_SVR, TEXT("Updated sequence number to %d"), pMove->seq+pMove->cProcessed )); }
if( 0 != pMove->cNotifications ) { //TrkLog(( TRKDBG_WARNING, TEXT("pMove = %p"), pMove ));
pMove->cNotifications = 0; // don't need to send the data back
//TrkLog(( TRKDBG_WARNING, TEXT("Free rgdroidNew (%p)"), pMove->rgdroidNew ));
//MIDL_user_free( pMove->rgdroidNew );
pMove->rgdroidNew = NULL;
//TrkLog(( TRKDBG_WARNING, TEXT("Free rgobjidCurrent (%p)"), pMove->rgobjidCurrent ));
//MIDL_user_free( pMove->rgobjidCurrent );
pMove->rgobjidCurrent = NULL;
//TrkLog(( TRKDBG_WARNING, TEXT("Free rgdroidBirth (%p)"), pMove->rgdroidBirth ));
//MIDL_user_free( pMove->rgdroidBirth );
pMove->rgdroidBirth = NULL; }
return(hr); }
BOOL CTrkSvrSvc::VerifyMachineOwnsVolume( const CMachineId &mcid, const CVolumeId & volid ) { HRESULT hr; SequenceNumber seq; FILETIME ft;
hr = _voltab.QueryVolume( mcid, volid, &seq, &ft); if (hr != S_OK) return FALSE; else return TRUE; }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::DeleteNotify
//
// Process a delete-notify request from a client. This request provides
// information about a file that has been deleted, so that we can purge
// it from the move table.
//
//+----------------------------------------------------------------------------
void CTrkSvrSvc::DeleteNotify( const CMachineId & mcidClient, TRKSVR_CALL_DELETE * pDelete ) { CVolumeId vol; HRESULT hr = TRK_S_VOLUME_NOT_FOUND;
// Loop through all of the notifications in this batch.
for (ULONG i=0; i < pDelete->cdroidBirth; i++) { // Look up the current location of the file, and if it
// is on an owned volume, then allow the delete.
CDomainRelativeObjId droidCurrent; CDomainRelativeObjId droidBirth;
// Don't embark on a slow operation if the service is stopping.
RaiseIfStopped();
// If we've already written a lot to the DS in the past hour,
// abort so we don't flood the replication queue.
if( CheckWritesPerHour() ) { TrkLog(( TRKDBG_WARNING, TEXT("Stopping delete-notify due to too many writes") )); TrkRaiseException( TRK_E_SERVER_TOO_BUSY ); }
// Read the existing entry for this file.
if (_idt.Query(pDelete->adroidBirth[i], &droidCurrent, &droidBirth)) { // The entry exists.
TrkAssert(droidBirth == pDelete->adroidBirth[i]);
// See if this is the same volume that we checked on the
// previous iteration through the loop. If so, no need to
// look up again.
if (vol == droidCurrent.GetVolumeId()) { hr = S_OK; } else { // We need to check that whoever sent this delete-notify
// request really owns the volume.
vol = droidCurrent.GetVolumeId(); if( !VerifyMachineOwnsVolume( mcidClient, vol )) { TrkLog((TRKDBG_OBJID_DELETIONS, TEXT("DeleteNotify _voltab.QueryVolume( %s ) -> %s\n"), (const TCHAR*) CDebugString( vol ), GetErrorString(hr) ));
vol = CVolumeId(); } }
// If the volume is owned, go ahead with the deletion.
if (hr == S_OK) { BOOL f = _idt.Delete( pDelete->adroidBirth[i] );
TrkLog((TRKDBG_OBJID_DELETIONS, TEXT("DeleteNotify _idt.Delete( %s ) -> %s\n"), (const TCHAR*) CDebugString( pDelete->adroidBirth[i] ), f ? TEXT("Ok") : TEXT("Not Found") ));
if( f ) IncrementWritesPerHour(); } } // if (_idt.Query(pDelete->adroidBirth[i], &droidCurrent, &droidBirth))
else { // Attempted to delete an entry that doesn't exist.
TrkLog((TRKDBG_OBJID_DELETIONS, TEXT("DeleteNotify _idt.Query( droidBirth=%s ) not found\n"), (const TCHAR*) CDebugString( pDelete->adroidBirth[i] ) )); } }
if( 0 != pDelete->cdroidBirth ) { //MIDL_user_free( pDelete->adroidBirth );
//pDelete->adroidBirth = NULL;
pDelete->cdroidBirth = 0; }
}
void CTrkSvrSvc::Refresh( const CMachineId &mcidClient, TRKSVR_CALL_REFRESH * pRefresh ) { // save away the input and zero the output so we don't marshall a
// ton of refresh info back to the client
ULONG cSources = pRefresh->cSources; ULONG cVolumes = pRefresh->cVolumes;
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cRefreshRequests) );
pRefresh->cSources = 0; pRefresh->cVolumes = 0;
// Touch the move table entries
for (ULONG i=0; i < cSources ; i++) { // Ensure we're not overloading the replication log
if( CheckWritesPerHour() ) { TrkLog(( TRKDBG_SVR, TEXT("Aborting refresh due to writes-per-hour") )); TrkRaiseException( TRK_E_SERVER_TOO_BUSY ); }
// Touch the entry in the move table.
if( _idt.Touch( pRefresh->adroidBirth[i] )) IncrementWritesPerHour(); }
// Touch the volume table entries
for (i=0; i < cVolumes ; i++) { // Ensure we're not overloading the replication log
if( CheckWritesPerHour() ) { TrkLog(( TRKDBG_SVR, TEXT("Aborting refresh due to writes-per-hour") )); TrkRaiseException( TRK_E_SERVER_TOO_BUSY ); }
// Ensure this volume is owned by the machine.
// mikehill_test
if( !VerifyMachineOwnsVolume( mcidClient, pRefresh->avolid[i] )) { TrkLog(( TRKDBG_WARNING, TEXT("Machine can't touch volume it doesn't own (%s, %s)"), (const TCHAR*) CDebugString(mcidClient), (const TCHAR*) CDebugString(pRefresh->avolid[i]) )); continue; }
// Touch the entry in the volume table
if( _voltab.Touch( pRefresh->avolid[i] )) IncrementWritesPerHour(); }
}
void CTrkSvrSvc::Statistics( TRKSVR_STATISTICS *pStatistics ) { pStatistics->cSyncVolumeRequests = _Stats.cSyncVolumeRequests; pStatistics->cSyncVolumeErrors = _Stats.cSyncVolumeErrors; pStatistics->cSyncVolumeThreads = _Stats.cSyncVolumeThreads;
pStatistics->cCreateVolumeRequests = _Stats.cCreateVolumeRequests; pStatistics->cCreateVolumeErrors = _Stats.cCreateVolumeErrors; pStatistics->cClaimVolumeRequests = _Stats.cClaimVolumeRequests; pStatistics->cClaimVolumeErrors = _Stats.cClaimVolumeErrors; pStatistics->cQueryVolumeRequests = _Stats.cQueryVolumeRequests; pStatistics->cQueryVolumeErrors = _Stats.cQueryVolumeErrors; pStatistics->cFindVolumeRequests = _Stats.cFindVolumeRequests; pStatistics->cFindVolumeErrors = _Stats.cFindVolumeErrors; pStatistics->cTestVolumeRequests = _Stats.cTestVolumeRequests; pStatistics->cTestVolumeErrors = _Stats.cTestVolumeErrors;
pStatistics->cSearchRequests = _Stats.cSearchRequests; pStatistics->cSearchErrors = _Stats.cSearchErrors; pStatistics->cSearchThreads = _Stats.cSearchThreads;
pStatistics->cMoveNotifyRequests = _Stats.cMoveNotifyRequests; pStatistics->cMoveNotifyErrors = _Stats.cMoveNotifyErrors; pStatistics->cMoveNotifyThreads = _Stats.cMoveNotifyThreads;
pStatistics->cRefreshRequests = _Stats.cRefreshRequests; pStatistics->cRefreshErrors = _Stats.cRefreshErrors; pStatistics->cRefreshThreads = _Stats.cRefreshThreads; pStatistics->lRefreshCounter = _refreshSequence.GetSequenceNumber();
pStatistics->cDeleteNotifyRequests = _Stats.cDeleteNotifyRequests; pStatistics->cDeleteNotifyErrors = _Stats.cDeleteNotifyErrors; pStatistics->cDeleteNotifyThreads = _Stats.cDeleteNotifyThreads;
pStatistics->ftLastSuccessfulRequest = _Stats.cftLastSuccessfulRequest; pStatistics->ftServiceStart = _Stats.cftServiceStartTime;
//pStatistics->ulGCIterationPeriod = _Stats.ulGCIterationPeriod;
//pStatistics->cEntriesToGC = _Stats.cEntriesToGC;
pStatistics->cEntriesGCed = _Stats.cEntriesGCed;
pStatistics->hrLastError = _Stats.hrLastError; pStatistics->ftNextGC = _timerGC.QueryOriginalDueTime(); pStatistics->cLowestAvailableRpcThreads=_cLowestAvailableThreads; pStatistics->cAvailableRpcThreads = _cAvailableThreads; pStatistics->cMaxRpcThreads = MAX_SVR_THREADS;
pStatistics->cNumThreadPoolThreads = g_cThreadPoolThreads; pStatistics->cMostThreadPoolThreads = g_cThreadPoolMaxThreads; //pStatistics->SvcCtrlState = _svcctrl.GetState();
pStatistics->cMaxDsWriteEvents = _Stats.cMaxDsWriteEvents; pStatistics->cCurrentFailedWrites = _Stats.cCurrentFailedWrites;
_qtable.Statistics( pStatistics );
OSVERSIONINFO verinfo; memset( &verinfo, 0, sizeof(verinfo) ); verinfo.dwOSVersionInfoSize = sizeof(verinfo);
if( GetVersionEx( &verinfo )) { pStatistics->Version.dwMajor = verinfo.dwMajorVersion; pStatistics->Version.dwMinor = verinfo.dwMinorVersion; pStatistics->Version.dwBuildNumber = verinfo.dwBuildNumber; } else { TrkLog(( TRKDBG_ERROR, TEXT("Failed GetVersionInfo (%lu)"), GetLastError() )); }
return;
}
HRESULT CTrkSvrSvc::SyncVolume(const CMachineId & mcidClient, TRKSVR_SYNC_VOLUME * pSyncVolume, ULONG cUncountedCreates ) { HRESULT hr = S_OK;
switch (pSyncVolume->SyncType) { case CREATE_VOLUME:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cCreateVolumeRequests) );
if( CheckWritesPerHour() ) { hr = TRK_E_SERVER_TOO_BUSY; TrkLog(( TRKDBG_VOLTAB | TRKDBG_WARNING, TEXT("Rejected CreateVolume, too many writes (%d)"), NumWritesThisHour() )); } else { hr = _voltab.PreCreateVolume( mcidClient, pSyncVolume->secret, cUncountedCreates, &pSyncVolume->volume ); if( SUCCEEDED(hr) ) IncrementWritesPerHour(); }
if(hr == S_OK) { TrkLog((TRKDBG_VOLTAB, TEXT("CreateVolume(machine=%s secret=%s volid(out)=%s) -> VOLUME_OK"), (const TCHAR*) CDebugString(mcidClient), (const TCHAR*) CDebugString(pSyncVolume->secret), (const TCHAR*) CDebugString(pSyncVolume->volume) )); } else { InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cCreateVolumeErrors) );
TrkLog((TRKDBG_VOLTAB, TEXT("CreateVolume(machine=%s secret=%s) -> CreateFailed (%08x)"), (const TCHAR*) CDebugString(mcidClient), (const TCHAR*) CDebugString(pSyncVolume->secret), hr )); } break;
case QUERY_VOLUME:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cQueryVolumeRequests) );
hr = _voltab.QueryVolume( mcidClient, pSyncVolume->volume, &pSyncVolume->seq, &pSyncVolume->ftLastRefresh );
TrkLog((TRKDBG_VOLTAB, TEXT("QueryVolume(machine=%s volid=%s seq(out)=%d ftLastRefresh(out)=%d) -> %s"), (const TCHAR*) CDebugString(mcidClient), (const TCHAR*) CDebugString(pSyncVolume->volume), pSyncVolume->seq, pSyncVolume->ftLastRefresh.dwLowDateTime, GetErrorString(hr)));
if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cQueryVolumeErrors) );
break;
case FIND_VOLUME:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cFindVolumeRequests) );
hr = _voltab.FindVolume( pSyncVolume->volume, &pSyncVolume->machine );
TrkLog((TRKDBG_VOLTAB, TEXT("FindVolume(volid=%s machine(out)=%s) -> %s"), (const TCHAR*) CDebugString(pSyncVolume->volume), (const TCHAR*) CDebugString(pSyncVolume->machine), GetErrorString(hr)));
if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cFindVolumeErrors) );
break;
case CLAIM_VOLUME:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cClaimVolumeRequests) );
if( CheckWritesPerHour() ) { hr = TRK_E_SERVER_TOO_BUSY; TrkLog(( TRKDBG_VOLTAB | TRKDBG_WARNING, TEXT("Rejected ClaimVolume, too many writes (%d)"), NumWritesThisHour() )); } else { hr = _voltab.ClaimVolume( mcidClient, pSyncVolume->volume, pSyncVolume->secretOld, pSyncVolume->secret, &pSyncVolume->seq, &pSyncVolume->ftLastRefresh ); if( S_OK == hr ) // Might return TRK_S_VOLUME_NOT_FOUND
IncrementWritesPerHour(); }
TrkLog((TRKDBG_VOLTAB, TEXT("ClaimVolume(machine=%s volid=%s secret=%s->%s seq(out)=%d ftLastRefresh(out)=%s) -> %s"), (const TCHAR*) CDebugString(mcidClient), (const TCHAR*) CDebugString(pSyncVolume->volume), (const TCHAR*) CDebugString(pSyncVolume->secretOld), (const TCHAR*) CDebugString(pSyncVolume->secret), pSyncVolume->seq, (const TCHAR*) CDebugString(pSyncVolume->ftLastRefresh), GetErrorString(hr)));
if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cClaimVolumeErrors) );
break;
case TEST_VOLUME:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cTestVolumeRequests) ); if( !(_configSvr.GetTestFlags() & TRK_TEST_FLAG_ALLOC_TEST_VOLUME) ) { hr = E_NOTIMPL; break; }
if( CVolumeSecret() != pSyncVolume->secret ) { hr = _voltab.SetSecret( pSyncVolume->volume, pSyncVolume->secret ); if( FAILED(hr) ) break; }
hr = _voltab.SetSequenceNumber( pSyncVolume->volume, pSyncVolume->seq ); if( FAILED(hr) ) break;
if( CMachineId() != pSyncVolume->machine ) { hr = _voltab.SetMachine( pSyncVolume->volume, pSyncVolume->machine ); if ( FAILED(hr) ) break; }
if( SUCCEEDED(hr) ) IncrementWritesPerHour();
if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cTestVolumeErrors) );
break;
case DELETE_VOLUME:
//InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cDeleteVolumeRequests) );
if( CheckWritesPerHour() ) { hr = TRK_E_SERVER_TOO_BUSY; TrkLog(( TRKDBG_VOLTAB | TRKDBG_WARNING, TEXT("Rejected DeleteVolume, too many writes (%d)"), NumWritesThisHour() )); } else { hr = _voltab.DeleteVolume( mcidClient, pSyncVolume->volume ); if( SUCCEEDED(hr) ) IncrementWritesPerHour(); }
TrkLog((TRKDBG_VOLTAB, TEXT("DeleteVolume(machine=%s volid=%s -> %s"), (const TCHAR*) CDebugString(mcidClient), (const TCHAR*) CDebugString(pSyncVolume->volume), GetErrorString(hr)));
/*
if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cClaimVolumeErrors) ); */
break;
default: TrkAssert(0 && "unknown switch type in SyncVolume"); hr = TRK_S_VOLUME_NOT_FOUND; break; }
return(hr); }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::CountPrioritizedThread
//
// This routine looks at the number of active threads in the service,
// and based on the priority of the message, accepts or rejects
// the request.
//
//+----------------------------------------------------------------------------
//
// If we have 0 threads, then accept any pri <=9
// If we have 1 thread, then accept any pri <=9
// If we have 2 threads, then accept any pri <=7
// If we have 3 threads, then accept any pri <=6
// If we have 4 threads, then accept any pri <=5
// If we have 5 threads, then accept any pri <=4
// If we have 6 threads, then accept any pri <=3
// If we have 7 threads, then accept any pri <=0
// If we have 8 threads, then accept any pri <=0
// If we have 9 threads, then accept any pri <=0
// If we have 10 threads, don't accept any
//
BOOL CTrkSvrSvc::CountPrioritizedThread( const TRKSVR_MESSAGE_UNION * pMsg ) {
static LONG Accept[10] = { 0, 0, 0, 3, 4, 5, 6, 7, 9, 9 };
TrkAssert( ELEMENTS(Accept) == MAX_SVR_THREADS );
// Get the current count of available threads
LONG cAvailableThreads = InterlockedDecrement( &_cAvailableThreads ); TrkAssert( cAvailableThreads < MAX_SVR_THREADS );
// It's not worth a lock to protect this statistic, we'll just hope that we
// don't get pre-empted during the update.
_cLowestAvailableThreads = min( _cLowestAvailableThreads, cAvailableThreads );
// There's already too many threads (cAvailableThreads is negative), or this
// request is of insufficient priority, reject it.
if (cAvailableThreads < 0 || pMsg->Priority > Accept[ cAvailableThreads ]) { InterlockedIncrement( &_cAvailableThreads );
TrkLog(( TRKDBG_WARNING, TEXT("CountPrioritizedThread rejecting request (%d)"), cAvailableThreads )); return( FALSE ); }
return(TRUE); }
void CTrkSvrSvc::ReleasePrioritizedThread() { LONG l = InterlockedIncrement( &_cAvailableThreads );
TrkAssert( l >= 0 && l <= MAX_SVR_THREADS ); }
HRESULT CTrkSvrSvc::CreateVolume(const CMachineId & mcidClient, const TRKSVR_SYNC_VOLUME& pSyncVolume) { return _voltab.AddVolidToTable(pSyncVolume.volume, mcidClient, pSyncVolume.secret ); }
void CTrkSvrSvc::OnRequestStart( TRKSVR_MESSAGE_TYPE MsgType ) { switch( MsgType ) { case SEARCH: case old_SEARCH:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cSearchRequests) ); InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cSearchThreads) ); break;
case MOVE_NOTIFICATION:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cMoveNotifyRequests) ); InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cMoveNotifyThreads) ); break;
case REFRESH:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cRefreshRequests) ); InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cRefreshThreads) ); break;
case SYNC_VOLUMES:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cSyncVolumeRequests) ); InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cSyncVolumeThreads) ); break;
case DELETE_NOTIFY:
InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cDeleteNotifyRequests) ); InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cDeleteNotifyThreads) ); break;
case STATISTICS: break;
default: TrkLog(( TRKDBG_ERROR, TEXT("Invalid MsgType in CTrkSvrSvc::OnRequestStart(%d)"), MsgType )); TrkAssert( FALSE ); } }
void CTrkSvrSvc::OnRequestEnd( TRKSVR_MESSAGE_UNION * pMsg, const CMachineId &mcid, HRESULT hr ) { int i = 0;
__try { switch( pMsg->MessageType ) { case SEARCH: case old_SEARCH:
InterlockedDecrement( reinterpret_cast<LONG*>(&_Stats.cSearchThreads) ); if( FAILED(hr) || ( 1 <= pMsg->Search.cSearch && S_OK != pMsg->Search.pSearches->hr )) { InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cSearchErrors) ); } _OperationLog.Add( COperationLog::TRKSVR_SEARCH, hr, mcid, pMsg->Search.pSearches->droidBirth );
break;
case MOVE_NOTIFICATION:
InterlockedDecrement( reinterpret_cast<LONG*>(&_Stats.cMoveNotifyThreads) ); if( S_OK != hr ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cMoveNotifyErrors) ); _OperationLog.Add( COperationLog::TRKSVR_MOVE_NOTIFICATION, hr, mcid );
break;
case REFRESH:
InterlockedDecrement( reinterpret_cast<LONG*>(&_Stats.cRefreshThreads) ); if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cRefreshErrors) ); _OperationLog.Add( COperationLog::TRKSVR_REFRESH, hr, mcid, pMsg->Refresh.cSources, pMsg->Refresh.cVolumes );
break;
case SYNC_VOLUMES:
InterlockedDecrement( reinterpret_cast<LONG*>(&_Stats.cSyncVolumeThreads) ); if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cSyncVolumeErrors) ); _OperationLog.Add( COperationLog::TRKSVR_SYNC_VOLUMES, hr, mcid ); break;
case DELETE_NOTIFY:
InterlockedDecrement( reinterpret_cast<LONG*>(&_Stats.cDeleteNotifyThreads) ); if( FAILED(hr) ) InterlockedIncrement( reinterpret_cast<LONG*>(&_Stats.cDeleteNotifyErrors) ); _OperationLog.Add( COperationLog::TRKSVR_DELETE_NOTIFY, hr, mcid );
break;
case STATISTICS: break;
}
if( FAILED(hr) ) SetLastError( hr ); else if( STATISTICS != pMsg->MessageType ) _Stats.cftLastSuccessfulRequest = CFILETIME(); } __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_WARNING, TEXT("Ignoring exception in OnRequestEnd") )); }
return; }
//+----------------------------------------------------------------------------
//
// CTrkSvrSvc::SvrMessage
//
// This is the primary starting point for processing of the trksvr
// service's LnkSvrMessage RPC method. For the most part, it looks
// at the MessageType (the request is in the form of a union, with
// a message-type and type-appropriate parameters), then switches
// to specific handler routine.
//
//+----------------------------------------------------------------------------
HRESULT CTrkSvrSvc::SvrMessage( handle_t IDL_handle, TRKSVR_MESSAGE_UNION * pMsg) { HRESULT hr = S_OK; ULONG i; ULONG cSources; CMachineId mcidClient; SThreadFromPoolState OriginalThreadFromPoolState;
if (GetState() != SERVICE_RUNNING) { return(TRK_E_SERVICE_NOT_RUNNING); }
// If we're getting busy (wrt active threads), we may have
// to reject this request, based on how busy we are and the
// priority of the request.
if (!CountPrioritizedThread( pMsg )) { return(TRK_E_SERVER_TOO_BUSY); }
__try {
// Set thread-specific settings, saving the old settings.
OriginalThreadFromPoolState = InitializeThreadFromPool();
// Update statistics
OnRequestStart( pMsg->MessageType );
// Ensure the client is authenticated.
if( RequireSecureRPC() ) CVerifyAuthentication::VerifyAuthentication( IDL_handle );
if( STATISTICS == pMsg->MessageType ) { // All messages that come in to this routine are from a machine account
// (in which trkwks runs). The exception to this rule is the Statistics
// request, which comes from a user. We'll allow all Authenticated Users
// access to the statistics (they can access the DS tables by default
// anyway).
mcidClient = CMachineId( MCID_LOCAL ); } else { mcidClient = NULL != pMsg->ptszMachineID && !g_ptrksvr->RequireSecureRPC() ? CMachineId(pMsg->ptszMachineID) : CMachineId(IDL_handle); }
// Check for a client doing a denial-of-service attack.
if( RequireSecureRPC() ) // Always true except in testing
CheckClient(mcidClient);
// Switch on the message type.
switch (pMsg->MessageType) {
case SEARCH:
TrkLog((TRKDBG_MEND|TRKDBG_SVR, TEXT("SEARCH from \\\\%s"), (const TCHAR*) CDebugString( mcidClient )));
for (i=0; i<pMsg->Search.cSearch; i++) { TrkAssert( NULL != pMsg->Search.pSearches ); pMsg->Search.pSearches[i].hr = TRK_E_UNAVAILABLE; } for (i=0; i<pMsg->Search.cSearch; i++) { Search(&pMsg->Search.pSearches[i]); } break;
case old_SEARCH:
TrkLog((TRKDBG_MEND|TRKDBG_SVR, TEXT("old_SEARCH from \\\\%s"), (const TCHAR*) CDebugString( mcidClient )));
for (i=0; i<pMsg->old_Search.cSearch; i++) { pMsg->old_Search.pSearches[i].hr = TRK_E_UNAVAILABLE; } for (i=0; i<pMsg->old_Search.cSearch; i++) { old_Search(&pMsg->old_Search.pSearches[i]); } break;
case MOVE_NOTIFICATION:
TrkLog((TRKDBG_MOVE|TRKDBG_SVR, TEXT("MOVE_NOTIFICATION from \\\\%s (%d notifications)"), (const TCHAR*) CDebugString( mcidClient ), pMsg->MoveNotification.cNotifications ));
hr = MoveNotify( mcidClient, &pMsg->MoveNotification ); break;
case REFRESH:
TrkLog((TRKDBG_GARBAGE_COLLECT|TRKDBG_SVR, TEXT("REFRESH from \\\\%s"), (const TCHAR*) CDebugString( mcidClient )));
Refresh( mcidClient, &pMsg->Refresh );
break;
case SYNC_VOLUMES: {
BOOL fHaveCreateVolume = FALSE; ULONG cUncountedCreates = 0;
// Validate the number of volumes in this request to protect against an unruly
// client.
if(pMsg->SyncVolumes.cVolumes > NUM_VOLUMES) { TrkLog((TRKDBG_ERROR, TEXT("Number of volumes exceeded per machine limit %d"), pMsg->SyncVolumes.cVolumes)); TrkRaiseException( E_INVALIDARG ); }
// Pre-initialize the return buffer.
for (i=0; i < pMsg->SyncVolumes.cVolumes; i++) { pMsg->SyncVolumes.pVolumes[i].hr = TRK_S_VOLUME_NOT_FOUND; }
// Perform the sync for each of the volumes.
for (i=0; i < pMsg->SyncVolumes.cVolumes; i++) { // Perform the sync.
pMsg->SyncVolumes.pVolumes[i].hr = SyncVolume(mcidClient, pMsg->SyncVolumes.pVolumes + i, cUncountedCreates );
// Keep track of the number of CREATE_VOLUME sub-requests.
if(CREATE_VOLUME == pMsg->SyncVolumes.pVolumes[i].SyncType && pMsg->SyncVolumes.pVolumes[i].hr == S_OK) { fHaveCreateVolume = TRUE; cUncountedCreates++; }
}
// If there were successful CREATE_VOLUME sub-requests in this
// SYNC_VOLUME request, and the request is going to succeed
// overall, update the DS.
if(TRUE == fHaveCreateVolume && SUCCEEDED(hr) ) { for (i=0; i < pMsg->SyncVolumes.cVolumes; i++) { if(CREATE_VOLUME == pMsg->SyncVolumes.pVolumes[i].SyncType && S_OK == pMsg->SyncVolumes.pVolumes[i].hr) { TrkLog(( TRKDBG_VOLTAB, TEXT("Finishing CreateVolume for %s"), (const TCHAR*) CDebugString(pMsg->SyncVolumes.pVolumes[i].volume ))); pMsg->SyncVolumes.pVolumes[i].hr = CreateVolume(mcidClient, pMsg->SyncVolumes.pVolumes[i]); } }
}
break; }
case DELETE_NOTIFY:
TrkLog((TRKDBG_OBJID_DELETIONS|TRKDBG_SVR, TEXT("DELETE_NOTIFY from \\\\%s"), (const TCHAR*) CDebugString( mcidClient )));
DeleteNotify( mcidClient, &pMsg->Delete ); break;
case STATISTICS:
TrkLog(( TRKDBG_SVR, TEXT("TRKSVR_STATISTICS"), (const TCHAR*) CDebugString(mcidClient) ));
memset( &pMsg->Statistics, 0, sizeof(TRKSVR_STATISTICS) ); Statistics( &pMsg->Statistics );
break;
default: hr = TRK_E_UNKNOWN_SVR_MESSAGE_TYPE; break; } } __except(BreakOnDebuggableException()) { hr = GetExceptionCode(); TrkLog((TRKDBG_ERROR, TEXT("LnkSvrMessage exception %08X caught during %s"), hr, (const TCHAR*) CDebugString(pMsg->MessageType) )); }
// Update statistics
OnRequestEnd( pMsg, mcidClient, hr );
ReleasePrioritizedThread();
// Restore the thread-specif settings.
UnInitializeThreadFromPool( OriginalThreadFromPoolState );
return(hr); }
HRESULT StubLnkSvrMessage_Old( handle_t IDL_handle, TRKSVR_MESSAGE_UNION_OLD * pMsg) { HRESULT hr;
TrkLog((TRKDBG_SVR, TEXT("Received downlevel call: ... thunking")));
TRKSVR_MESSAGE_UNION Msg2;
Msg2.MessageType = pMsg->MessageType; Msg2.Priority = PRI_5;
switch (Msg2.MessageType) { case (SEARCH): Msg2.Search = pMsg->Search; break; case (MOVE_NOTIFICATION): Msg2.MoveNotification = pMsg->MoveNotification; break; case (REFRESH): Msg2.Refresh = pMsg->Refresh; break; case (SYNC_VOLUMES): Msg2.SyncVolumes = pMsg->SyncVolumes; break; case (DELETE_NOTIFY): Msg2.Delete = pMsg->Delete; break; }
Msg2.ptszMachineID = pMsg->ptszMachineID;
hr = StubLnkSvrMessage( IDL_handle, &Msg2 );
switch (Msg2.MessageType) { case (SEARCH): pMsg->Search = Msg2.Search; break; case (MOVE_NOTIFICATION): pMsg->MoveNotification = Msg2.MoveNotification; break; case (REFRESH): pMsg->Refresh = Msg2.Refresh; break; case (SYNC_VOLUMES): pMsg->SyncVolumes = Msg2.SyncVolumes ; break; case (DELETE_NOTIFY): pMsg->Delete = Msg2.Delete; break; }
pMsg->ptszMachineID = Msg2.ptszMachineID;
return(hr);
}
// must return a positive number (success code) if it doesn't want the caller
// to find another DC to do it on.
HRESULT StubLnkSvrMessage( handle_t IDL_handle, TRKSVR_MESSAGE_UNION * pMsg) { return( g_ptrksvr->SvrMessage( IDL_handle, pMsg )); }
void CTrkSvrRpcServer::Initialize( SVCHOST_GLOBAL_DATA * pSvcsGlobalData, CTrkSvrConfiguration * pTrkSvrConfig ) { RPC_STATUS rpcstatus; NET_API_STATUS netstatus;
// Ensure there's a tcp/ip binding handle
rpcstatus = RpcServerUseProtseq( const_cast<TCHAR*>(s_tszTrkSvrRpcProtocol), pTrkSvrConfig->GetSvrMaxRpcCalls(), NULL);
if (rpcstatus != RPC_S_OK && rpcstatus != RPC_S_DUPLICATE_ENDPOINT) { // Log an event, unless it's a not-supported error, which happens during
// a normal setup.
if( RPC_S_PROTSEQ_NOT_SUPPORTED != rpcstatus ) { TrkLog((TRKDBG_ERROR, TEXT("RpcServerUseProtseqEp %08x"), rpcstatus)); TrkReportInternalError( THIS_FILE_NUMBER, __LINE__, HRESULT_FROM_WIN32(rpcstatus), TRKREPORT_LAST_PARAM ); } TrkRaiseWin32Error(rpcstatus); }
// If we don't have a pSvcsGlobalData (we're not running in services.exe),
// tell RpcServerRegisterIfEx to automatically set up a listen thread.
CRpcServer::Initialize( Stubtrksvr_v1_0_s_ifspec, NULL == pSvcsGlobalData ? RPC_IF_AUTOLISTEN : 0, pTrkSvrConfig->GetSvrMaxRpcCalls(), RpcSecurityEnabled(), // fSetAuthInfo
s_tszTrkSvrRpcProtocol );
TrkLog(( TRKDBG_RPC, TEXT("Registered TrkSvr RPC server %s (%d)"), RpcSecurityEnabled() ? TEXT("") : TEXT("(without authorization)"), pTrkSvrConfig->GetSvrMaxRpcCalls() ));
}
void CTrkSvrRpcServer::UnInitialize( SVCHOST_GLOBAL_DATA * pSvcsGlobalData ) { TrkLog(( TRKDBG_RPC, TEXT("Unregistering TrkSvr RPC server") )); CRpcServer::UnInitialize( ); TrkLog(( TRKDBG_RPC, TEXT("Unregistered TrkSvr RPC server") )); }
CMachineId::CMachineId(handle_t ClientBinding) { RPC_STATUS RpcStatus; NTSTATUS status; BOOL f; HANDLE hToken = NULL; PTOKEN_USER pToken; BYTE * pbToken = NULL; PSID LocalSystemSid = NULL;
PUNICODE_STRING pUserName = NULL; PUNICODE_STRING pUserDomainName = NULL;
NET_API_STATUS NetStatus;
WCHAR *pwszDomain = NULL; BOOLEAN fIsWorkGroup; LPBYTE pbAccountInfo = NULL; BOOL fImpersonating = FALSE;
// Begin impersonating the user.
RpcStatus = RpcImpersonateClient(ClientBinding); if (RpcStatus != RPC_S_OK) TrkRaiseWin32Error(RpcStatus); fImpersonating = TRUE;
__try { WCHAR wszCurrentUser[ UNLEN+1 ]; WCHAR wszDomainName[ CNLEN+1 ];
// Get the local domain name (so we can verify it against the user's).
NetStatus = NetpGetDomainNameEx(&pwszDomain, &fIsWorkGroup); if (NetStatus != NO_ERROR) { pwszDomain = NULL; TrkReportInternalError( THIS_FILE_NUMBER, __LINE__, HRESULT_FROM_WIN32(NetStatus), TRKREPORT_LAST_PARAM ); TrkRaiseWin32Error(NetStatus); } TrkAssert( !fIsWorkGroup );
// Get the user's name & domain (possible because we're
// impersonating).
status = LsaGetUserName( &pUserName, &pUserDomainName ); if( !NT_SUCCESS(status) ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get user name (%08x)"), status )); TrkRaiseNtStatus( status ); }
memcpy( wszCurrentUser, pUserName->Buffer, pUserName->Length ); wszCurrentUser[ pUserName->Length / sizeof(WCHAR) ] = L'\0';
// Ensure this is a user in this domain
{ UNICODE_STRING LocalDomainName = { 0, 0, NULL }; RtlInitUnicodeString( &LocalDomainName, pwszDomain );
if( 0 != RtlCompareUnicodeString( pUserDomainName, &LocalDomainName, TRUE )) { TrkLog(( TRKDBG_WARNING, TEXT("User %s is from another domain"), wszCurrentUser )); TrkRaiseException( TRK_E_UNKNOWN_SID ); } }
// The user name should end in a '$', since it should be a
// machine account.
if (wszCurrentUser[ pUserName->Length / sizeof(WCHAR) - 1] != L'$') { // No dollar suffix, it's probably local system on the DC
DWORD SizeRequired; // Get the user's SID
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open thread token for %ws (%08x)"), wszCurrentUser, HRESULT_FROM_WIN32(GetLastError()) )); TrkRaiseLastError(); } if (!GetTokenInformation( hToken, TokenUser, NULL, 0, &SizeRequired ) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get thread token information (1) for %ws (%08x)"), wszCurrentUser, HRESULT_FROM_WIN32(GetLastError()) )); TrkRaiseLastError(); }
pbToken = new BYTE [SizeRequired]; if (pbToken == NULL) TrkRaiseWin32Error(ERROR_NOT_ENOUGH_MEMORY);
pToken = (PTOKEN_USER) pbToken;
if (!GetTokenInformation( hToken, TokenUser, pToken, SizeRequired, &SizeRequired )) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get thread token information (2) for %ws (%08x)"), wszCurrentUser, HRESULT_FROM_WIN32(GetLastError()) )); TrkRaiseLastError(); }
// Get the local system SID
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (! AllocateAndInitializeSid( &NtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &LocalSystemSid )) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't Alloc/Init SID for %ws (%08x)"), wszCurrentUser, HRESULT_FROM_WIN32(GetLastError()) )); TrkRaiseLastError(); }
// Verify that the user is local system.
BOOL fEqual = EqualSid( pToken->User.Sid, LocalSystemSid );
if (!fEqual) { TrkLog(( TRKDBG_ERROR, TEXT("Unknown SID: %ws"), wszCurrentUser )); TrkRaiseException(TRK_E_UNKNOWN_SID); }
*this = CMachineId(MCID_LOCAL); Normalize(); AssertValid(); } // if (wszCurrentUser[ pUserName->Length / sizeof(WCHAR) - 1] != L'$')
else { // We don't need to be impersonating any longer, potentially (though
// not likely) ACLs on the SAM could prevent the impersonated user
// from making this call. So we might as well revert now.
TrkAssert( fImpersonating ); if( fImpersonating ) { RpcRevertToSelf(); fImpersonating = FALSE; }
// Get account info for this user, so we can
// verify that it's a machine account.
NetStatus = NetUserGetInfo( NULL, // Check on this server
wszCurrentUser, 1, // Get USER_INFO_1
&pbAccountInfo ); if (NetStatus != 0) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get user %s info (%08x)"), wszCurrentUser, NetStatus)); TrkRaiseWin32Error(NetStatus); } TrkLog(( TRKDBG_SVR, TEXT("\nUser is %s"), wszCurrentUser)); if ((((USER_INFO_1*)pbAccountInfo)->usri1_flags & (UF_WORKSTATION_TRUST_ACCOUNT | UF_SERVER_TRUST_ACCOUNT)) == 0) { // if the account is not a workstation or backup dc account, fail
TrkRaiseException( TRK_E_CALLER_NOT_MACHINE_ACCOUNT ); } // overwrite the $
wszCurrentUser[ pUserName->Length / sizeof(WCHAR) - 1] = L'\0'; if (_tcslen(wszCurrentUser) + 1 > sizeof(_szMachine)) TrkRaiseException(TRK_E_IMPERSONATED_COMPUTERNAME_TOO_LONG); else if( TEXT('\0') == wszCurrentUser[0] ) TrkRaiseException( TRK_E_NULL_COMPUTERNAME ); memset(&_szMachine, 0, sizeof(_szMachine));
// Convert the Unicode computer name into Ansi, using
// the OEMCP codepage (NetBios/computer names are always
// in OEMCP, not Window/Ansi).
if( 0 == WideCharToMultiByte( CP_OEMCP, 0, wszCurrentUser, -1, _szMachine, sizeof(_szMachine), NULL, NULL )) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't convert machine name %s to multi-byte (%lu)"), wszCurrentUser, GetLastError() )); TrkRaiseLastError(); }
TrkLog(( TRKDBG_WARNING, TEXT("Converted machine name: %hs (from %s, ") MCID_BYTE_FORMAT_STRING, _szMachine, wszCurrentUser, (BYTE)_szMachine[0], (BYTE)_szMachine[1], (BYTE)_szMachine[2], (BYTE)_szMachine[3], (BYTE)_szMachine[4], (BYTE)_szMachine[5], (BYTE)_szMachine[6], (BYTE)_szMachine[7], (BYTE)_szMachine[8], (BYTE)_szMachine[9], (BYTE)_szMachine[10], (BYTE)_szMachine[11], (BYTE)_szMachine[12], (BYTE)_szMachine[13], (BYTE)_szMachine[14], (BYTE)_szMachine[15] ));
Normalize(); AssertValid(); } // if (wszCurrentUser[ pUserName->Length / ... else
} __finally { if( fImpersonating ) { RpcRevertToSelf(); fImpersonating = FALSE; }
if (hToken != NULL) { CloseHandle(hToken); } if (pbToken != NULL) { delete [] pbToken; } if (LocalSystemSid != NULL) { FreeSid( LocalSystemSid ); }
if (pUserName != NULL) { LsaFreeMemory(pUserName->Buffer); LsaFreeMemory(pUserName); } if (pUserDomainName != NULL) { LsaFreeMemory(pUserDomainName->Buffer); LsaFreeMemory(pUserDomainName); }
if (pbAccountInfo != NULL) { NetApiBufferFree(pbAccountInfo); }
if( NULL != pwszDomain ) NetApiBufferFree(pwszDomain); } }
|