|
|
// Copyright (c) 1996-1999 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: common.cxx
//
// Contents: Code common to Tracking (Workstation) Service and
// Tracking (Server) Service.
//
// Classes:
//
// Functions:
//
//
//
// History: 18-Nov-96 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include "trklib.hxx"
#include "ntlsa.h" // LsaGetUserName
#if MAX_COMPUTERNAME_LENGTH != 15
#error MAX_COMPUTERNAME_LENGTH assumed to be 15
#endif
//DWORD g_ftIndex;
const TCHAR s_HexGuidString[] = TEXT("%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X"); #define CCH_HEXGUID_STRING (8+4+4+2*8)
EXTERN_C const CLSID CLSID_TrackFile = {0x8790c947, 0xa30b, 0x11d0, {0x8c, 0xab, 0x00, 0xc0, 0x4f, 0xd9, 0x0f, 0x85} };
#if DBG
#define TRK_E_ERROR_MAP(tr,hr) { tr, hr, TEXT(#tr) }
#else
#define TRK_E_ERROR_MAP(tr,hr) { tr, hr }
#endif
// Map of TRK_E_ type error codes to HRESULTs, and in the debug build to strings.
const TrkEMap g_TrkEMap[] = { TRK_E_ERROR_MAP(TRK_S_OUT_OF_SYNC, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ), TRK_E_ERROR_MAP(TRK_E_CORRUPT_LOG, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ), TRK_E_ERROR_MAP(TRK_E_TIMER_REGISTRY_CORRUPT, HRESULT_FROM_WIN32(ERROR_REGISTRY_CORRUPT) ), TRK_E_ERROR_MAP(TRK_E_REGISTRY_REFRESH_CORRUPT, HRESULT_FROM_WIN32(ERROR_REGISTRY_CORRUPT) ), TRK_E_ERROR_MAP(TRK_E_CORRUPT_IDT, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ), TRK_E_ERROR_MAP(TRK_E_DB_CONNECT_ERROR, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ), TRK_E_ERROR_MAP(TRK_E_DN_TOO_LONG, CO_E_PATHTOOLONG ), TRK_E_ERROR_MAP(TRK_E_DOMAIN_COMPUTER_NAMES_TOO_LONG, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ), TRK_E_ERROR_MAP(TRK_E_BAD_USERNAME_NO_SLASH, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ), TRK_E_ERROR_MAP(TRK_E_UNKNOWN_SID, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ), TRK_E_ERROR_MAP(TRK_E_IMPERSONATED_COMPUTERNAME_TOO_LONG, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ), TRK_E_ERROR_MAP(TRK_E_UNKNOWN_SVR_MESSAGE_TYPE, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ), TRK_E_ERROR_MAP(TRK_E_FAIL_TEST, E_FAIL ), TRK_E_ERROR_MAP(TRK_E_DENIAL_OF_SERVICE_ATTACK, HRESULT_FROM_WIN32(ERROR_RETRY) ), TRK_E_ERROR_MAP(TRK_E_SERVICE_NOT_RUNNING, HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) ), TRK_E_ERROR_MAP(TRK_E_TOO_MANY_UNSHORTENED_NOTIFICATIONS, HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) ), TRK_E_ERROR_MAP(TRK_E_CORRUPT_CLNTSYNC, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ), TRK_E_ERROR_MAP(TRK_E_COMPUTER_NAME_TOO_LONG, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ), TRK_E_ERROR_MAP(TRK_E_SERVICE_STOPPING, HRESULT_FROM_WIN32(ERROR_NO_TRACKING_SERVICE) ), TRK_E_ERROR_MAP(TRK_E_BIRTHIDS_DONT_MATCH, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ), TRK_E_ERROR_MAP(TRK_E_CORRUPT_VOLTAB, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ), TRK_E_ERROR_MAP(TRK_E_INTERNAL_ERROR, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ), TRK_E_ERROR_MAP(TRK_E_PATH_TOO_LONG, CO_E_PATHTOOLONG ), TRK_E_ERROR_MAP(TRK_E_GET_MACHINE_NAME_FAIL, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ), TRK_E_ERROR_MAP(TRK_E_SET_VOLUME_STATE_FAIL, HRESULT_FROM_WIN32(ERROR_INTERNAL_DB_CORRUPTION) ), TRK_E_ERROR_MAP(TRK_E_VOLUME_ACCESS_DENIED, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ), TRK_E_ERROR_MAP(TRK_S_VOLUME_NOT_FOUND, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) ), TRK_E_ERROR_MAP(TRK_S_VOLUME_NOT_OWNED, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) ), TRK_E_ERROR_MAP(TRK_E_REFERRAL, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ), TRK_E_ERROR_MAP(TRK_E_NOT_FOUND, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ), TRK_E_ERROR_MAP(TRK_E_UNAVAILABLE, HRESULT_FROM_WIN32(ERROR_CONNECTION_UNAVAIL) ), TRK_E_ERROR_MAP(TRK_E_TIMEOUT, HRESULT_FROM_WIN32(ERROR_SERVICE_REQUEST_TIMEOUT) ), TRK_E_ERROR_MAP(TRK_E_VOLUME_QUOTA_EXCEEDED, HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_QUOTA) ), TRK_E_ERROR_MAP(TRK_S_NOTIFICATION_QUOTA_EXCEEDED, HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_QUOTA) ), TRK_E_ERROR_MAP(TRK_E_SERVER_TOO_BUSY, HRESULT_FROM_WIN32(RPC_S_SERVER_TOO_BUSY) ), TRK_E_ERROR_MAP(TRK_E_INVALID_VOLUME_ID, HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR) ), TRK_E_ERROR_MAP(TRK_E_POTENTIAL_FILE_FOUND, HRESULT_FROM_WIN32(ERROR_POTENTIAL_FILE_FOUND) ), TRK_E_ERROR_MAP(TRK_E_NULL_COMPUTERNAME, HRESULT_FROM_WIN32(ERROR_INVALID_COMPUTERNAME) ), TRK_E_ERROR_MAP(TRK_E_NOT_FOUND_AND_LAST_VOLUME_NOT_FOUND, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ), TRK_E_ERROR_MAP(TRK_E_NOT_FOUND_BUT_LAST_VOLUME_FOUND, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ) };
//+----------------------------------------------------------------------------
//
// Function: GetErrorString
//
// Synopsis: Map an TRK_E_ error to a descriptive string.
//
//+----------------------------------------------------------------------------
#if DBG
const TCHAR * GetErrorString(HRESULT hr) { ULONG iError;
if( S_OK == hr ) return( TEXT("S_OK") );
for( iError = 0; iError < sizeof(g_TrkEMap)/sizeof(*g_TrkEMap); iError++ ) { if( g_TrkEMap[iError].tr == hr ) return( g_TrkEMap[iError].ptszDescription ); }
return( TEXT("Not a TRK_E_ error") ); } #endif
//+----------------------------------------------------------------------------
//
// Function: MapTR2HR
//
// Synopsis: Map a TRK_E_ type error code to an HRESULT. If the input is
// already a non-trk HRESULT, the input will be returned unchanged.
//
//+----------------------------------------------------------------------------
HRESULT MapTR2HR( HRESULT tr ) { ULONG iError; HRESULT hr = tr;
if( S_OK == hr ) return( hr );
// Convert TRK_E_ error codes into an HRESULT
for( iError = 0; iError < sizeof(g_TrkEMap)/sizeof(*g_TrkEMap); iError++ ) { if( g_TrkEMap[iError].tr == hr ) { hr = g_TrkEMap[iError].hr; break; } }
// If this HRESULT is actually an NTSTATUS, then convert it to a Win32
// error, then back to an HRESULT.
if( FACILITY_NT_BIT & hr ) { if( STATUS_VOLUME_NOT_UPGRADED == hr ) hr = HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); else hr = HRESULT_FROM_WIN32( RtlNtStatusToDosError(hr & ~FACILITY_NT_BIT) ); }
return( hr ); }
//+----------------------------------------------------------------------------
//
// HexStringizeGuid
//
// Optimized conversion from a GUID to a string.
//
//+----------------------------------------------------------------------------
inline void HexStringizeByte( BYTE b, TCHAR* &rptsz ) { static const TCHAR _tszLookup[] = { TEXT("0123456789ABCDEF") };
*rptsz++ = _tszLookup[ b >> 4 ]; *rptsz++ = _tszLookup[ b & 0xF ]; }
void HexStringizeGuid(const GUID &g, TCHAR * & rptsz) {
HexStringizeByte( HIGH_BYTE(HIGH_WORD(g.Data1)), rptsz ); HexStringizeByte( LO_BYTE(HIGH_WORD(g.Data1)), rptsz ); HexStringizeByte( HIGH_BYTE(LO_WORD(g.Data1)), rptsz ); HexStringizeByte( LO_BYTE(LO_WORD(g.Data1)), rptsz );
HexStringizeByte( HIGH_BYTE(g.Data2), rptsz ); HexStringizeByte( LO_BYTE(g.Data2), rptsz );
HexStringizeByte( HIGH_BYTE(g.Data3), rptsz ); HexStringizeByte( LO_BYTE(g.Data3), rptsz );
for( int i = 0; i < sizeof(g.Data4); i++ ) HexStringizeByte( g.Data4[i], rptsz );
*rptsz = TEXT('\0');
}
//+----------------------------------------------------------------------------
//
// HexUnstringizeGuid
//
// Convert a string to a GUID. This is not used as often as HexStringizeGuid,
// so it uses the CRT.
//
//+----------------------------------------------------------------------------
BOOL HexUnstringizeGuid(const TCHAR * &ptsz, GUID * pg) { DWORD Data1; DWORD Data2; DWORD Data3; DWORD Data40; DWORD Data41; DWORD Data42; DWORD Data43; DWORD Data44; DWORD Data45; DWORD Data46; DWORD Data47;
if( 11 != _stscanf( ptsz, s_HexGuidString, &Data1, &Data2, &Data3, &Data40, &Data41, &Data42, &Data43, &Data44, &Data45, &Data46, &Data47)) { return( FALSE ); }
pg->Data1 = Data1; pg->Data2 = (WORD)Data2; pg->Data3 = (WORD)Data3; pg->Data4[0] = (BYTE)Data40; pg->Data4[1] = (BYTE)Data41; pg->Data4[2] = (BYTE)Data42; pg->Data4[3] = (BYTE)Data43; pg->Data4[4] = (BYTE)Data44; pg->Data4[5] = (BYTE)Data45; pg->Data4[6] = (BYTE)Data46; pg->Data4[7] = (BYTE)Data47;
ptsz += CCH_HEXGUID_STRING;
return( TRUE ); }
TCHAR * wcstotcs(TCHAR *ptszBuf, const WCHAR *pwsz) { #ifdef UNICODE
wcscpy(ptszBuf, pwsz); #else
wcstombs(ptszBuf, pwsz, (wcslen(pwsz)+1)*sizeof(WCHAR)); #endif
return(ptszBuf); }
CHAR * tcstombs(CHAR *pszBuf, const TCHAR *ptsz) { #ifdef UNICODE
wcstombs(pszBuf, ptsz, (_tcslen(ptsz)+1)*sizeof(CHAR)); #else
strcpy(pszBuf, ptsz); #endif
return(pszBuf); }
WCHAR * tcstowcs(WCHAR *pwszBuf, const TCHAR *ptsz) { #ifdef UNICODE
wcscpy(pwszBuf, ptsz); #else
mbstowcs(pwszBuf, ptsz, (_tcslen(ptsz)+1)*sizeof(WCHAR)); #endif
return(pwszBuf); }
TCHAR * mbstotcs(TCHAR *ptszBuf, const CHAR *psz) { #ifdef UNICODE
mbstowcs(ptszBuf, psz, (strlen(psz)+1)*sizeof(WCHAR)); #else
_tcscpy(ptszBuf, psz); #endif
return(ptszBuf); }
DWORD TrkTimeUnits(const SYSTEMTIME &st) { CFILETIME cft( st );
// 2**32 * 100e-9 = 429.4967296 seconds
//
// 32bit int can last 1844674407371 seconds = 58494.24173551 years
return( cft.HighDateTime() ); }
DWORD TrkTimeUnits(const CFILETIME &cft) { // 2**32 * 100e-9 = 429.4967296 seconds
//
// 32bit int can last 1844674407371 seconds = 58494.24173551 years
return( cft.HighDateTime() ); }
#if DBG
void CMachineId::AssertValid() { }
#endif // #if DBG
//+----------------------------------------------------------------------------
//
// CMachineId::CMachineId( ptsz )
//
// Instantiate a mcid from a computer name, e.g. "mymachine".
//
//+----------------------------------------------------------------------------
CMachineId::CMachineId(const TCHAR * ptszPath) { HRESULT hr = E_FAIL; int nReturn;
// Zero everything out first
new(this) CMachineId;
// Ensure that this isn't a real path, it should
// have been pre-processed already.
TrkAssert( _tcslen(ptszPath) < 2 || ( TEXT('\\') != ptszPath[0] && TEXT(':') != ptszPath[1] ) );
// Ensure that it's not too long.
if (_tcslen(ptszPath) > MAX_COMPUTERNAME_LENGTH ) TrkRaiseException( TRK_E_COMPUTER_NAME_TOO_LONG );
#ifndef _UNICODE
#error Ansi build not supported.
#endif
// Convert the Unicode machine name into Ansi, using
// the OEM code page (the netbios/computer name is always
// OEM code page, not the Windows Ansi code page).
nReturn = WideCharToMultiByte( CP_OEMCP, 0, ptszPath, -1, _szMachine, sizeof(_szMachine), NULL, NULL );
if( 0 == nReturn ) TrkRaiseLastError();
TrkLog(( TRKDBG_WARNING, TEXT("Machine name is: %hs (from %s)"), _szMachine, ptszPath ));
Normalize(); // Guarantee a terminator
}
//+----------------------------------------------------------------------------
//
// CMachineId::CMachineId( type )
//
// Initialize an mcid. The type indicates if the mcid should be for the
// local computer, a DC (possibly doing a rediscovery), or invalid.
//
//+----------------------------------------------------------------------------
CMachineId::CMachineId(MCID_CREATE_TYPE type) { DWORD dwSize = MAX_COMPUTERNAME_LENGTH+1; CHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1]; int nReturn;
TrkAssert(type == MCID_LOCAL || type == MCID_INVALID || type == MCID_DOMAIN || type == MCID_DOMAIN_REDISCOVERY || type == MCID_PDC_REQUIRED);
// Basic initialization
new(this) CMachineId;
if (type == MCID_INVALID) goto Exit;
switch (type) { case MCID_LOCAL: { WCHAR wszComputerName[ MAX_COMPUTERNAME_LENGTH + 1 ];
// Create an MCID of the local machine
// We can't call GetComputerNameA, because it uses
// RtlUnicodeStringToAnsiString and consequently returns
// a Windows/Ansi string, when it should be returning an
// OEM string.
if (!GetComputerNameW(wszComputerName, &dwSize)) TrkRaiseException(HRESULT_FROM_WIN32(GetLastError()));
nReturn = WideCharToMultiByte( CP_OEMCP, 0, wszComputerName, -1, szComputerName, sizeof(szComputerName), NULL, NULL ); if( 0 == nReturn ) TrkRaiseLastError(); }
break;
case MCID_DOMAIN: case MCID_DOMAIN_REDISCOVERY: case MCID_PDC_REQUIRED:
// Create an MCID for a DC
DWORD dwErr; PDOMAIN_CONTROLLER_INFOW pdci;
// DsGetDcName gets us the appropriate DC computer name
// (of the form \\machine). We call the W version
// because the A version returns an Ansi string,
// rather than an OEM string.
dwErr = DsGetDcNameW(NULL, NULL, NULL, NULL, DS_RETURN_FLAT_NAME | DS_BACKGROUND_ONLY | DS_DIRECTORY_SERVICE_REQUIRED | DS_WRITABLE_REQUIRED | (type == MCID_DOMAIN_REDISCOVERY ? DS_FORCE_REDISCOVERY : 0 ) | (type == MCID_PDC_REQUIRED ? DS_PDC_REQUIRED : 0 ), &pdci); if (dwErr != NO_ERROR) { TrkRaiseWin32Error(dwErr); }
// Validate the returned name.
TrkAssert(pdci->DomainControllerName && pdci->DomainControllerName[0] == L'\\' && pdci->DomainControllerName[1] == L'\\');
dwSize = wcslen(pdci->DomainControllerName + 2); if ( dwSize + 1 > sizeof(_szMachine)) { NetApiBufferFree(pdci); TrkRaiseException(HRESULT_FROM_WIN32(ERROR_INVALID_NAME)); }
// Keep the returned name.
nReturn = WideCharToMultiByte( CP_OEMCP, 0, &pdci->DomainControllerName[2], -1, _szMachine, sizeof(_szMachine), NULL, NULL ); if( 0 == nReturn ) TrkRaiseLastError();
NetApiBufferFree(pdci);
goto Exit; } // switch
if (dwSize + 1 <= sizeof(_szMachine)) { strcpy(_szMachine, szComputerName); Normalize(); } else { TrkRaiseException(HRESULT_FROM_WIN32(ERROR_INVALID_NAME)); }
Exit:
Normalize(); // Guarantee a terminator
return; }
#ifndef UNICODE
extern "C" NET_API_STATUS NetpGetDomainName ( IN LPWSTR *ComputerNamePtr); #endif
//+----------------------------------------------------------------------------
//
// CMachineId::GetLocalAuthName
//
// Returns the authentication name for use in secure RPC (to be used in
// the RpcBindingSetAuthInfo on the server). The name
// is of the form DOMAIN\MACHINE$, where DOMAIN is the local domain
// and MACHINE is the contents of this CMachineId
//
//+----------------------------------------------------------------------------
void CMachineId::GetLocalAuthName(RPC_TCHAR * ptszAuthName, DWORD cchBuf) const { RaiseIfInvalid(); // To get domain name: if you link to netlib.lib, you can call NetpGetDomainName,
// which does all the work for you. Or you could copy the code
// from \nt\private\net\netlib\domname.c
NET_API_STATUS Status; WCHAR * pwszDomain; DWORD dwErr; PDOMAIN_CONTROLLER_INFOA pdci = NULL;
__try { // Get the domain name ...
Status = NetpGetDomainName(&pwszDomain); if (Status != NO_ERROR) { pwszDomain = NULL; TrkRaiseWin32Error(Status); }
// and validate it.
if (cchBuf < wcslen(pwszDomain) + 1 + strlen(_szMachine) + 1 + 1) { TrkRaiseException(TRK_E_DOMAIN_COMPUTER_NAMES_TOO_LONG); }
// Copy the domain name then the machine name into the return buffer.
wcstotcs((TCHAR*)ptszAuthName, pwszDomain); _tcscat((TCHAR*)ptszAuthName, TEXT("\\")); mbstotcs(_tcschr((TCHAR*)ptszAuthName, 0), _szMachine); _tcscat((TCHAR*)ptszAuthName, TEXT("$"));
} __finally { if (pwszDomain != NULL) { NetApiBufferFree(pwszDomain); } } }
//+----------------------------------------------------------------------------
//
// GetFileTimeNow
//
// Get the current FILETIME (UTC).
//
//+----------------------------------------------------------------------------
FILETIME GetFileTimeNow() { SYSTEMTIME st; FILETIME ft;
GetSystemTime(&st); SystemTimeToFileTime(&st, &ft);
return(ft); }
//+----------------------------------------------------------------------------
//
// CDomainRelativeObjId::Stringize
//
// Stringize a droid.
//
//+----------------------------------------------------------------------------
TCHAR * CDomainRelativeObjId::Stringize( TCHAR * ptszOutBuf, DWORD cchBuf ) const { TCHAR *ptszBuf = ptszOutBuf; _volume.Stringize(ptszBuf); /*in, out, c++ reference*/ _object.Stringize(ptszBuf); /*in, out, c++ reference*/
TrkAssert(_tcslen(ptszOutBuf) + 1 < cchBuf);
return(ptszOutBuf); }
//+----------------------------------------------------------------------------
//
// CTrkRegistryKey::Delete
//
// Common code to delete a registry key, relative to _hkey.
//
//+----------------------------------------------------------------------------
LONG CTrkRegistryKey::Delete( const TCHAR *ptszName ) { LONG lRet = 0; _cs.Enter(); __try { // Open _hkey if it's not already.
lRet = Open();
// And delete the value
if( ERROR_SUCCESS == lRet ) { RegDeleteValue( _hkey, ptszName ); Close(); } } __finally { _cs.Leave(); }
return( lRet ); }
//+----------------------------------------------------------------------------
//
// CTrkRegistryKey::SetDword
//
// Set a REG_DWORD value under _hkey.
//
//+---------------------------------------------------------------------------0
LONG CTrkRegistryKey::SetDword( const TCHAR *ptszName, DWORD dw ) { LONG lRet = 0;
_cs.Enter(); __try { // Open _hkey if it's not already
lRet = Open();
// And set the value
if ( ERROR_SUCCESS == lRet ) { lRet = RegSetValueEx( _hkey, ptszName, 0, REG_DWORD, reinterpret_cast<CONST BYTE *>(&dw), sizeof(dw) ); if( ERROR_SUCCESS != lRet ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't set registry value for %s (%lu)"), ptszName, lRet )); RegDeleteKey( _hkey, ptszName ); }
Close(); } } __finally { _cs.Leave(); }
return( lRet ); }
//+----------------------------------------------------------------------------
//
// CTrkRegistryKey::GetDword
//
// Get a REG_DWORD value from _hkey.
//
//+---------------------------------------------------------------------------0
LONG CTrkRegistryKey::GetDword( const TCHAR *ptszName, DWORD *pdwRead, DWORD dwDefault ) { LONG lRet; DWORD dwRead; DWORD cbData = sizeof(*pdwRead); DWORD dwType;
*pdwRead = dwDefault;
_cs.Enter(); __try { // Open _hkey if it's not already.
lRet = Open(); if( ERROR_SUCCESS != lRet ) __leave;
// Get the DWORD
lRet = RegQueryValueEx( _hkey, ptszName, 0, &dwType, reinterpret_cast<BYTE*>(&dwRead), &cbData );
if( ERROR_SUCCESS == lRet ) { // Validate the type
if( REG_DWORD != dwType || sizeof(dwRead) != cbData ) { TrkLog(( TRKDBG_ERROR, TEXT("Wrong type/size (%d/%d) for registry value %s"), dwType, cbData, ptszName )); RegDeleteKey( _hkey, ptszName ); } else { *pdwRead = dwRead; } } else if( ERROR_FILE_NOT_FOUND == lRet || ERROR_PATH_NOT_FOUND == lRet ) { lRet = ERROR_SUCCESS; } else { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't read %s from registry (%lu)"), ptszName, lRet )); RegDeleteKey( _hkey, ptszName ); __leave; } } __finally { Close(); _cs.Leave(); }
return( lRet ); }
//+----------------------------------------------------------------------------
//
// ThreadPoolCallbackFunction
//
// This function is passed as the callback function to
// RegisterWaitForSingleObjectEx. The context is a PWorkItem pointer.
//
// Arguments:
// [pvWorkItem]
// The Context parameter from RegisterWaitForSingleObjectEx.
// Is a PWorkItem*
// [fTimeout]
// We always register INFINITE as the timeout, so this value
// should always be FALSE.
//
//+----------------------------------------------------------------------------
VOID NTAPI ThreadPoolCallbackFunction( PVOID pvWorkItem, BOOLEAN fTimeout ) { SThreadFromPoolState state; PWorkItem *pWorkItem = reinterpret_cast<PWorkItem*>(pvWorkItem);
TrkLog(( TRKDBG_WORKMAN, TEXT("Enter ThreadPoolCallbackFunction for %s (%p/%p)"), pWorkItem->_tszWorkItemSig, pWorkItem, *reinterpret_cast<UINT_PTR*>(pWorkItem) ));
TrkAssert( FALSE == fTimeout );
// Make sure we never raise back into the thread pool.
__try { // Update thread-count stats. Note that this isn't
// thread-safe, but for private statistics it's not worth
// creating a critsec.
InterlockedIncrement( reinterpret_cast<LONG*>(&g_cThreadPoolThreads) ); if( g_cThreadPoolThreads > g_cThreadPoolMaxThreads ) g_cThreadPoolMaxThreads = g_cThreadPoolThreads;
// Set our necessary thread-specific settings
state = InitializeThreadFromPool();
// Process the signal
pWorkItem->DoWork(); } __except( BREAK_THEN_RETURN( EXCEPTION_EXECUTE_HANDLER )) { TrkLog(( TRKDBG_ERROR, TEXT("Unexpected exception on thread pool callback (%08x)"), GetExceptionCode() )); }
TrkLog(( TRKDBG_WORKMAN, TEXT("Exit ThreadPoolCallbackFunction for %s (%p)"), pWorkItem->_tszWorkItemSig, pWorkItem ));
InterlockedDecrement( reinterpret_cast<LONG*>(&g_cThreadPoolThreads) );
//IFDBG( TrkRtlCheckForOrphanedCriticalSections( GetCurrentThread() ));
// Restore the original thread-specific settings
UnInitializeThreadFromPool( state ); }
// The work item callback function (used for RtlQueueWorkItem)
// just calls to the function above.
VOID NTAPI ThreadPoolWorkItemFunction( PVOID pvWorkItem ) { ThreadPoolCallbackFunction( pvWorkItem, FALSE ); }
//+----------------------------------------------------------------------------
//
// RunningAsAdministratorHack
//
// This routine is only used by test/debug hooks. It checks to see if
// the thread is running as an administrative user by seeing if we can
// get write access to the service's parameters key in the registry.
//
//+----------------------------------------------------------------------------
BOOL RunningAsAdministratorHack() { LONG lResult = 0; HKEY hkey = NULL; BOOL fReturn = FALSE;
lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, s_tszKeyNameLinkTrack, 0, // Options, reserved must be zero
KEY_ALL_ACCESS, &hkey ); if( ERROR_SUCCESS == lResult ) { fReturn = TRUE; RegCloseKey( hkey ); }
return( fReturn );
}
//+----------------------------------------------------------------------------
//
// EnablePrivilege
//
// Enable the specified privielge in the current access token if
// it is available.
//
//+----------------------------------------------------------------------------
BOOL EnablePrivilege( const TCHAR *ptszPrivilegeName ) { BOOL fSuccess = FALSE; HANDLE hToken = INVALID_HANDLE_VALUE; LUID luid; TOKEN_PRIVILEGES token_privileges;
// Get the process token.
if( !OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { TrkLog(( TRKDBG_ERROR, TEXT("Failed OpenProcessToken (%lu)"), GetLastError() )); goto Exit; }
// Look up the name of this privilege.
if( !LookupPrivilegeValue( (LPTSTR) NULL, ptszPrivilegeName, &luid )) { TrkLog(( TRKDBG_ERROR, TEXT("Failed LookupPrivilegeValue (%lu)"), GetLastError() )); goto Exit; }
// Enable the privilege.
token_privileges.PrivilegeCount = 1; token_privileges.Privileges[0].Luid = luid; token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &token_privileges, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL);
// The return value doesn't tell us anything useful. We have to check GetLastError
// for ERROR_SUCCESS or ERROR_NOT_ALL_ASSIGNED.
if( ERROR_SUCCESS != GetLastError() ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't adjust process token privileges (%lu)"), GetLastError() )); goto Exit; }
fSuccess = TRUE;
Exit:
if( INVALID_HANDLE_VALUE != hToken ) CloseHandle( hToken );
return( fSuccess );
}
#if DBG
CDebugString::CDebugString(const TRKSVR_MESSAGE_TYPE MsgType) { switch(MsgType) { case old_SEARCH: _tcscpy( _tsz, TEXT("old_SEARCH") ); break; case SEARCH: _tcscpy( _tsz, TEXT("SEARCH") ); break; case MOVE_NOTIFICATION: _tcscpy( _tsz, TEXT("MOVE_NOTIFICATION") ); break; case REFRESH: _tcscpy( _tsz, TEXT("REFRESH") ); break; case SYNC_VOLUMES: _tcscpy( _tsz, TEXT("SYNC_VOLUMES") ); break; case DELETE_NOTIFY: _tcscpy( _tsz, TEXT("DELETE_NOTIFY") ); break; case STATISTICS: _tcscpy( _tsz, TEXT("STATISTICS") ); break; default: _tcscpy( _tsz, TEXT("UNKNOWN") ); break; } }
#endif // #if DBG
#if DBG
CDebugString::CDebugString(LONG VolIndex, const PFILE_NOTIFY_INFORMATION pNotifyInfo ) { TCHAR *ptsz = _tsz; _tsz[0] = TEXT('\0');
switch( pNotifyInfo->Action ) { case FILE_ACTION_ADDED: _tcscat( _tsz, TEXT("Added ")); break; case FILE_ACTION_REMOVED: _tcscat( _tsz, TEXT("Removed ")); break; case FILE_ACTION_MODIFIED: _tcscat( _tsz, TEXT("Modified ")); break; case FILE_ACTION_RENAMED_OLD_NAME: _tcscat( _tsz, TEXT("Rename old name ")); break; case FILE_ACTION_RENAMED_NEW_NAME: _tcscat( _tsz, TEXT("Rename new name ")); break; case FILE_ACTION_ADDED_STREAM: _tcscat( _tsz, TEXT("Added stream ")); break; case FILE_ACTION_REMOVED_STREAM: _tcscat( _tsz, TEXT("Removed stream ")); break; case FILE_ACTION_MODIFIED_STREAM: _tcscat( _tsz, TEXT("Modified stream ")); break; case FILE_ACTION_REMOVED_BY_DELETE: _tcscat( _tsz, TEXT("Removed by delete ")); break; case FILE_ACTION_ID_NOT_TUNNELLED: _tcscat( _tsz, TEXT("OID not tunnelled ")); break; case FILE_ACTION_TUNNELLED_ID_COLLISION: _tcscat( _tsz, TEXT("OID tunnel collision ")); break; default: _stprintf( _tsz, TEXT("Unknown action (0x%x)"), pNotifyInfo->Action ); break; }
ptsz = _tsz + _tcslen(_tsz);
// The name length for an object ID is always 72
if( pNotifyInfo->FileNameLength != 72 ) _stprintf( ptsz, TEXT(" name length=%d"), pNotifyInfo->FileNameLength ); else { // Stringize the path of this object ID
CDomainRelativeObjId droidBirth; CObjId objid( FOI_OBJECTID, *(FILE_OBJECTID_INFORMATION*)pNotifyInfo->FileName );
ptsz[0] = VolChar(VolIndex); ptsz[1] = TEXT(':'); ptsz[2] = TEXT('\0'); FindLocalPath( VolIndex, objid, &droidBirth, &ptsz[2] ); ptsz += _tcslen(ptsz); ptsz += _stprintf( ptsz, TEXT(" - %c:"), VolChar(VolIndex) ); _stprintf( ptsz, TEXT("%s"), CDebugString(objid)._tsz ); ptsz += _tcslen(ptsz); }
}
#endif // #if DBG
//+----------------------------------------------------------------------------
//
// CActiveThreadList::AddCurrent
//
// Add the current thread to the list of threads maintained by this class.
//
//+----------------------------------------------------------------------------
HRESULT CActiveThreadList::AddCurrent( ) { HRESULT hr = S_OK; HANDLE hThread = NULL;
if( !_cs.IsInitialized() ) { TrkLog(( TRKDBG_WARNING, TEXT("Active thread list critsec not initialized!!!") )); return S_OK; }
_cs.Enter();
if( _cActiveThreads < _cMaxThreads ) { // Add the thread ID at the end of the list.
_prgdwThreadIDs[ _cActiveThreads++ ] = GetCurrentThreadId(); } else { // Alloc a larger buffer for the list, then add the thread ID.
hr = Grow(); if( SUCCEEDED(hr) ) { _prgdwThreadIDs[ _cActiveThreads++ ] = GetCurrentThreadId(); } }
#if DBG
if( SUCCEEDED(hr) ) TrkLog(( TRKDBG_WORKMAN, TEXT("Added thread 0x%x to the active thread list (%d)"), GetCurrentThreadId(), _cActiveThreads )); #endif
_cs.Leave();
return( hr ); }
//+----------------------------------------------------------------------------
//
// CActiveThreadList::RemoveCurrent
//
// Remove the current thread ID from the list which is maintained by this
// class.
//
//+----------------------------------------------------------------------------
HRESULT CActiveThreadList::RemoveCurrent( ) { HRESULT hr = S_OK; BOOL fFound = FALSE; DWORD dwThreadID = GetCurrentThreadId();
if( !_cs.IsInitialized() ) return S_OK;
_cs.Enter();
// Search for the current thread's ID in the list.
for( ULONG i = 0; i < _cActiveThreads; i++ ) { if( _prgdwThreadIDs[ i ] == dwThreadID ) { // We found this thread. Remove it from the list by copying down
// all the IDs behind it.
memcpy( &_prgdwThreadIDs[i], &_prgdwThreadIDs[i+1], (--_cActiveThreads - i) * sizeof(_prgdwThreadIDs[0]) ); _prgdwThreadIDs[ _cActiveThreads ] = 0;
TrkLog(( TRKDBG_WORKMAN, TEXT("Removed thread 0x%x from the active thread list (%d)"), dwThreadID, _cActiveThreads ));
fFound = TRUE; break; } }
if( !fFound ) { hr = E_FAIL; TrkLog(( TRKDBG_WORKMAN, TEXT("CActiveThreadList couldn't remove thread 0x%x, not found"), dwThreadID )); }
_cs.Leave();
return( hr ); }
//+----------------------------------------------------------------------------
//
// CActiveThreadList::CancelAllRpc
//
// Call RpcCancelThread on each of the threads in the list.
//
//+----------------------------------------------------------------------------
void CActiveThreadList::CancelAllRpc() { if( !_cs.IsInitialized() ) return;
_cs.Enter();
TrkLog(( TRKDBG_WKS|TRKDBG_SVR, TEXT("Canceling all out-going RPCs") ));
// Loop through the list of threads.
for( ULONG i = 0; i < _cActiveThreads; i++ ) { TrkAssert( 0 != _prgdwThreadIDs[i] );
// Get a thread handle for this thread ID.
HANDLE hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, _prgdwThreadIDs[i] ); if( NULL == hThread ) { // Nothing we can do about it. Move on to the next thread.
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open thread 0x%x to cancel RPC (%lu)"), _prgdwThreadIDs[i], GetLastError() )); continue; }
// Cancel any out-going RPC on this thread.
RPC_STATUS rpcstatus = RpcCancelThread( hThread ); if( RPC_S_OK != rpcstatus ) { TrkLog(( TRKDBG_ERROR, TEXT("Failed RpcCancelThread on %p/0x%x (%lu)"), hThread, _prgdwThreadIDs[i], rpcstatus )); } else { TrkLog(( TRKDBG_WORKMAN, TEXT("Canceled RPC on %p/0x%x"), hThread, _prgdwThreadIDs[i] )); }
// Close the thread handle and move on.
CloseHandle( hThread ); }
_cs.Leave(); }
//+----------------------------------------------------------------------------
//
// CActiveThreadList::Grow
//
// Private member function to grow the buffer used to hold the thread IDs.
//
//+----------------------------------------------------------------------------
HRESULT CActiveThreadList::Grow() { // This is a private method, the critsec has already been entered.
HRESULT hr = S_OK; DWORD *prgNew = NULL; ULONG cMaxThreads = _cMaxThreads + INCREMENT_ACTIVE_THREAD_LIST;
prgNew = new DWORD[ cMaxThreads ]; if( NULL == prgNew ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't grow active thread list") )); return( E_OUTOFMEMORY ); }
TrkLog(( TRKDBG_WORKMAN, TEXT("Growing active thread list from %d to %d"), _cMaxThreads, cMaxThreads ));
memcpy( prgNew, _prgdwThreadIDs, _cMaxThreads*sizeof(_prgdwThreadIDs[0]) ); _cMaxThreads = cMaxThreads; delete [] _prgdwThreadIDs; _prgdwThreadIDs = prgNew;
return( hr ); }
|