|
|
// Copyright (c) 1996-1999 Microsoft Corporation
//+============================================================================
//
// vol.cxx
//
// This file implements the CVolume class. This class maintains all activities
// for a volume, such as the the log, log file, and deletions manager classes.
//
//+============================================================================
#include <pch.cxx>
#pragma hdrstop
#include "trkwks.hxx"
#include <dbt.h>
#define THIS_FILE_NUMBER VOL_CXX_FILE_NO
//+----------------------------------------------------------------------------
//
// CVolume::AddRef
//
//+----------------------------------------------------------------------------
ULONG CVolume::AddRef() { long cNew; cNew = InterlockedIncrement( &_lRef ); //TrkLog(( TRKDBG_VOLUME, TEXT("+++ Vol %c: refs => %d"), VolChar(_iVol), cNew ));
return( cNew ); }
//+----------------------------------------------------------------------------
//
// CVolume::Release
//
//+----------------------------------------------------------------------------
ULONG CVolume::Release() { long cNew; cNew = InterlockedDecrement( &_lRef ); //TrkLog(( TRKDBG_VOLUME, TEXT("--- Vol %c: refs => %d"), VolChar(_iVol), cNew ));
if( 0 == cNew ) delete this;
return( cNew >= 0 ? cNew : 0 ); }
//+----------------------------------------------------------------------------
//
// CVolume::Initialize
//
// Initialize CVolume and open a handle to the volume itself, but nothing
// more (e.g. don't open the log or verify volume IDs). The remainder of
// the initialization will occur later the first time ReopenVolumeHandles
// is called. This gets that heavy IO work out of the service initialization
// path.
//
//+----------------------------------------------------------------------------
BOOL CVolume::Initialize( TCHAR *ptszVolumeName, const CTrkWksConfiguration * pTrkWksConfiguration, CVolumeManager *pVolMgr, PLogCallback * pLogCallback, PObjIdIndexChangedCallback * pObjIdIndexChangedCallback, SERVICE_STATUS_HANDLE ssh #if DBG
, CTestSync * pTunnelTest #endif
) { HANDLE hFile = NULL; // const CVolumeId volNULL;
// CVolumeId volidVolume;
NTSTATUS status; BOOL fSuccess = FALSE;
_iVol = -1; memset( &_volinfo, 0, sizeof(_volinfo) );
// Save the volume name, without the trailing whack
// Volume names are in the form \\?\Volume{guid}\
_tcscpy( _tszVolumeDeviceName, ptszVolumeName ); TrkAssert( TEXT('\\') == _tszVolumeDeviceName[ _tcslen(_tszVolumeDeviceName)-1 ] ); _tszVolumeDeviceName[ _tcslen(_tszVolumeDeviceName)-1 ] = TEXT('\0');
// Save the inputs
_pTrkWksConfiguration = pTrkWksConfiguration; _pVolMgr = pVolMgr; _pLogCallback = pLogCallback; _ssh = ssh; _hdnVolumeLock = NULL; _fVolumeDismounted = _fVolumeLocked = FALSE;
IFDBG( _pTunnelTest = pTunnelTest; )
__try // __except
{
// Create critical sections
_csVolume.Initialize(); _csHandles.Initialize(); _fInitialized = TRUE;
Lock();
__try // __finally
{ _VolQuotaReached.Initialize();
// Open the volume (not a directory in the volume, but the volume itself).
// We'll use this to do relative-opens by object ID
status = OpenVolume( _tszVolumeDeviceName, &_hVolume ); if (!NT_SUCCESS(status)) TrkRaiseNtStatus(status);
// Initialize, but don't start, the objid index change notifier. When started,
// this will watch for adds/deletes/tunnels/etc. in the index.
_ObjIdIndexChangeNotifier.Initialize( _tszVolumeDeviceName, pObjIdIndexChangedCallback, this );
fSuccess = TRUE; } __finally { Unlock(); }
} __except ( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Failed initializaion of volume %s (%08x)"), ptszVolumeName, GetExceptionCode() ));
if( TRK_E_VOLUME_NOT_DRIVE != GetExceptionCode() ) { TrkReportInternalError( THIS_FILE_NUMBER, __LINE__, GetExceptionCode(), TRKREPORT_LAST_PARAM ); } }
return fSuccess; }
//+----------------------------------------------------------------------------
//
// CVolume::SetLocallyGeneratedVolId
//
// Generate a volume ID and set it on the volume. If we're in a domain,
// this will later get replaced with a volume ID from trksvr.
//
//+----------------------------------------------------------------------------
void CVolume::SetLocallyGeneratedVolId() { NTSTATUS status = STATUS_SUCCESS;
// Ensure the volume is writeable
RaiseIfWriteProtectedVolume();
_fDirty = TRUE;
// Create the ID
// Call _volinfo.volid.UuidCreate()
RPC_STATUS rpc_status = GenerateVolumeIdInVolInfo(); if( RPC_S_OK != rpc_status ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't create a local volid for new volume") )); TrkRaiseWin32Error(rpc_status); }
// Set the ID on the volume.
status = SetVolIdOnVolume( _volinfo.volid ); g_ptrkwks->_entropy.Put();
if( NT_SUCCESS(status) ) TrkLog(( TRKDBG_VOLUME, TEXT("Locally generated a new volid for %c:, %s"), VolChar(_iVol), (const TCHAR*)CDebugString(_volinfo.volid) )); else { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't set new volid on %c: (%08x)"), VolChar(_iVol), status )); TrkRaiseNtStatus(status); }
// Get rid of the log and existing object IDs since we have a new volid.
DeleteAndReinitializeLog(); MarkForMakeAllOidsReborn();
}
//+----------------------------------------------------------------------------
//
// CVolume::VolumeSanityCheck
//
// This routine is called when the volume handles are opened,
// The caller is responsible for calling CLogFile::Initialize and
// CLog::Initialize. The caller must ensure that _volinfo is
// properly loaded prior to the call.
//
//+----------------------------------------------------------------------------
const GUID s_guidInvalidVolId = { /* {d2a2ac27-b89a-11d2-9335-00805ffe11b8} */ 0xd2a2ac27, 0xb89a, 0x11d2, {0x93, 0x35, 0x00, 0x80, 0x5f, 0xfe, 0x11, 0xb8} };
void CVolume::VolumeSanityCheck( BOOL fVolIndexSetAlready ) { NTSTATUS status; CVolumeId volidVolume; const CVolumeId volNULL; const CMachineId mcidLocal( MCID_LOCAL ); TCHAR tszVolumeName[ CCH_MAX_VOLUME_NAME + 1 ];
// Get the volume name that the mount manager has associated with this
// volume.
LONG iVolOld = _iVol;
if( !fVolIndexSetAlready ) { _iVol = MapVolumeDeviceNameToIndex( _tszVolumeDeviceName ); if( -1 == _iVol ) { TrkLog(( TRKDBG_VOLUME, TEXT("Volume %s does not appear any longer to have a drive letter (was %c:)"), _tszVolumeDeviceName, VolChar( iVolOld ) )); MarkSelfForDelete(); TrkRaiseException( TRK_E_VOLUME_NOT_DRIVE ); } else if( iVolOld != _iVol ) { TrkLog(( TRKDBG_VOLUME, TEXT("Volume %c: is now %c:"), VolChar(iVolOld), VolChar(_iVol) )); } }
// Get the filesystem-maintained volume ID
TCHAR tszRoot[ MAX_PATH ]; _tcscpy( tszRoot, _tszVolumeDeviceName ); _tcscat( tszRoot, TEXT("\\") ); status = QueryVolumeId(tszRoot, &volidVolume); g_ptrkwks->_entropy.Put();
if( !NT_SUCCESS(status) && STATUS_OBJECT_NAME_NOT_FOUND != status ) { // For some reason we couldn't read the NTFS volid
// (e.g. it's been dismounted).
TrkLog(( TRKDBG_VOLUME, TEXT("Couldn't get filesys volid for %c:"), VolChar(_iVol) )); TrkRaiseNtStatus(status); }
TrkLog(( TRKDBG_VOLUME, TEXT("VolId (from NTFS) for %c: is %s"), VolChar(_iVol), (const TCHAR*)CDebugString(volidVolume) ));
// Compare the volume IDs from the filesystem (volume) and from
// the VolInfo structure we keep in the log file. If one is
// set but not the other, then we'll adopt the one that's set.
// If they're both set, but to different values, then we'll
// take the one from NTFS.
if( volNULL == volidVolume && volNULL != _volinfo.volid ) { // Assume the volid in the VolInfo structure is correct.
// This scenario occurs after a volume is formatted while the service
// is running. In that case, we have the volume info in memory and think
// it's not dirty, but in fact the log file is gone. So, just to be safe,
// we'll go dirty, and the flush at the end will put the latest state out
// to the file.
RaiseIfWriteProtectedVolume(); _fDirty = TRUE;
TrkLog(( TRKDBG_ERROR, TEXT("Duping the volid from the logfile to the volume for %c:"), VolChar(_iVol) )); status = SetVolIdOnVolume(_volinfo.volid); g_ptrkwks->_entropy.Put(); if(!NT_SUCCESS(status)) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't set a volume ID on %c:"), VolChar(_iVol) )); TrkRaiseNtStatus(status); }
SetState( VOL_STATE_NOTOWNED );
} else if( volidVolume != _volinfo.volid || volNULL == volidVolume) { // Either the two volids don't match, or they're both NULL.
if( volNULL != volidVolume && s_guidInvalidVolId != volidVolume ) { // Assume the volid on the volume (NTFS) is correct.
// If the log has a different volid, it may have invalid move entries.
// So we delete it.
if( volNULL != _volinfo.volid ) DeleteAndReinitializeLog();
_volinfo.Initialize();
// Set _volinfo.volid = volidVolume
SetVolIdInVolInfo( volidVolume );
_volinfo.machine = mcidLocal;
SetState( VOL_STATE_NOTOWNED ); } else { // Both the volume and the _volinfo (the log) are null. We're going to
// go into the not-created state, but first put on a volid so that we
// never have a volume with no ID.
_volinfo.Initialize(); _volinfo.machine = mcidLocal;
// Create a new ID and put it on the volume.
SetLocallyGeneratedVolId(); // Updates _volinfo.volid
// Put ourselves in the not-created state.
TrkAssert( VOL_STATE_OWNED == GetState() ); SetState( VOL_STATE_NOTCREATED ); } }
// If the machine ID in the log isn't the current machine, then go into
// the not-created state so that we'll re-claim the volume.
if( mcidLocal != _volinfo.machine ) SetState( VOL_STATE_NOTOWNED );
// See if this volume duplicates any other on this system.
CVolume *pvolDuplicate = _pVolMgr->IsDuplicateVolId( this, GetVolumeId() ); if( NULL != pvolDuplicate ) { // This should never happen; there should never be two
// volumes on the same machine with the same ID.
// When this happens on different machines it gets caught during
// CheckSequenceNumbers, but on the same machine this doesn't work,
// because TrkSvr accepts the Claim of both machines.
TrkLog(( TRKDBG_WARNING, TEXT("Volume %c: and %c: have duplicate volume IDs. Resetting %c:"), VolChar(GetIndex()), VolChar(pvolDuplicate->GetIndex()), VolChar(GetIndex()) )); TrkReportEvent( EVENT_TRK_SERVICE_DUPLICATE_VOLIDS, EVENTLOG_INFORMATION_TYPE, static_cast<const TCHAR*>(CStringize( VolChar(GetIndex()))), static_cast<const TCHAR*>(CStringize( VolChar(pvolDuplicate->GetIndex()) )), TRKREPORT_LAST_PARAM );
SetLocallyGeneratedVolId(); // Updates _volinfo.volid
SetState( CVolume::VOL_STATE_NOTCREATED );
pvolDuplicate->SetState( CVolume::VOL_STATE_NOTOWNED ); pvolDuplicate->Release(); }
// If anything's dirty, flush it now. In the normal initialization path,
// this will have no effect.
Flush(); }
//+----------------------------------------------------------------------------
//
// CVolume::Refresh
//
//+----------------------------------------------------------------------------
void CVolume::Refresh() { HANDLE hVolume = NULL; NTSTATUS status = STATUS_SUCCESS;
Lock(); __try { status = OpenVolume( _tszVolumeDeviceName, &hVolume );
if( NT_SUCCESS(status) ) _iVol = MapVolumeDeviceNameToIndex( _tszVolumeDeviceName ); else if( !IsErrorDueToLockedVolume(status) ) _iVol = -1;
if( -1 == _iVol ) { TrkLog(( TRKDBG_VOLUME, TEXT("Drive not found in CVolume::Refresh") )); MarkSelfForDelete(); } } __except( BreakOnDebuggableException() ) { }
// Unlock before calling NtClose, since during stress that
// could raise.
Unlock(); if( NULL != hVolume ) NtClose( hVolume );
} // CVolume::Refresh
//+----------------------------------------------------------------------------
//
// CVolume::MarkSelfForDelete
//
// Mark this CVolume to be deleted (not the volume, but the class). The
// delete will actually occur when this object is completely released
// and unlocked. We do, however, as part of this method remove ourself
// from the volume manager's list.
//
//+----------------------------------------------------------------------------
void CVolume::MarkSelfForDelete() { AssertLocked();
if( !_fDeleteSelfPending ) { TrkLog(( TRKDBG_VOLUME, TEXT("Marking %c: for delete"), VolChar(_iVol) ));
// Show that we need to be deleted. We can't actually delete now, because there
// may be threads active in the object.
_fDeleteSelfPending = TRUE;
// On the final UnLock, Release will be called to counter this AddRef and
// cause the actual delete.
AddRef();
// Take this object out of the Volume Manager's linked list (which will do
// a Release, thus the need for the above AddRef);
_pVolMgr->RemoveVolumeFromLinkedList( this ); } else TrkLog(( TRKDBG_VOLUME, TEXT("%c: is already marked for delete"), VolChar(_iVol) )); }
//+----------------------------------------------------------------------------
//
// CVolume::RegisterPnpVolumeNotification
//
// Register to receive PNP notifications for this volume. If already
// registered, register again (since the volume handle against which
// we'd previously registered may no longer exist).
//
//+----------------------------------------------------------------------------
void CVolume::RegisterPnPVolumeNotification() { DEV_BROADCAST_HANDLE dbchFilter; HDEVNOTIFY hdnVolumeLock = _hdnVolumeLock;
dbchFilter.dbch_size = sizeof(DEV_BROADCAST_HANDLE); dbchFilter.dbch_devicetype = DBT_DEVTYP_HANDLE; dbchFilter.dbch_handle = _ObjIdIndexChangeNotifier._hDir; // _hVolume;
__try { // Register against the oid index handle (as a representative of
// the volume).
hdnVolumeLock = RegisterDeviceNotification((HANDLE)_ssh, &dbchFilter, DEVICE_NOTIFY_SERVICE_HANDLE); if(hdnVolumeLock == NULL) { TrkLog((TRKDBG_VOLUME, TEXT("Can't register for volume notifications, %08x"), GetLastError())); TrkRaiseLastError(); }
// Get rid of our old registration, if we had one.
UnregisterPnPVolumeNotification();
// Keep the new registration.
_hdnVolumeLock = hdnVolumeLock; TrkLog(( TRKDBG_VOLUME, TEXT("Registered for volume lock/unlock notification on %c: (%p)"), VolChar(_iVol), _hdnVolumeLock )); } __except(BreakOnDebuggableException()) { TrkLog((TRKDBG_VOLUME, TEXT("Can't register for volume notification, %08x"), GetExceptionCode())); } }
//+----------------------------------------------------------------------------
//
// CVolume::UnregisterPnpVolumeNotification
//
// Unregister the device notification handle for this volume (if we have
// one).
//
//+----------------------------------------------------------------------------
void CVolume::UnregisterPnPVolumeNotification() { if(_hdnVolumeLock) { if( !UnregisterDeviceNotification(_hdnVolumeLock)) { TrkLog(( TRKDBG_ERROR, TEXT("UnregisterDeviceNotification failed: %lu"), GetLastError() )); }
TrkLog(( TRKDBG_VOLUME, TEXT("Unregistered for volume lock/unlock notification on %c: (%p)"), VolChar(_iVol), _hdnVolumeLock ));
_hdnVolumeLock = NULL; } }
//+----------------------------------------------------------------------------
//
// CVolume::DeleteAndReinitializeLog
//
// Delete the volume log and reinitialize it.
//
//+----------------------------------------------------------------------------
void CVolume::DeleteAndReinitializeLog() { // Delete and reinitialize the log
__try { RaiseIfWriteProtectedVolume();
TrkLog(( TRKDBG_VOLUME, TEXT("DeleteAndReinitializeLog (%s)"), _tszVolumeDeviceName ));
if( IsHandsOffVolumeMode() ) // Volume is locked
TrkRaiseNtStatus( STATUS_ACCESS_DENIED );
// Delete the log
_cLogFile.Delete();
// Reinitialize the log file, then the log itself.
_cLogFile.Initialize( NULL, NULL, NULL, VolChar(_iVol) ); _cLog.Initialize( _pLogCallback, _pTrkWksConfiguration, &_cLogFile ); } __except( IsErrorDueToLockedVolume( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // If the volume is locked, start the reopen timer and abort.
CloseVolumeHandles(); // Never raises
g_ptrkwks->SetReopenVolumeHandlesTimer(); TrkRaiseException( GetExceptionCode() ); } }
//+----------------------------------------------------------------------------
//
// CVolume::DeleteLogAndReInitializeVolume
//
// Delete and reinit the log, then reinitialize the rest of the volume.
//
//+----------------------------------------------------------------------------
void CVolume::DeleteLogAndReInitializeVolume() { AssertLocked();
// There's the remote possibility that the VolumeSanityCheck
// call below will call this routine. Just to be paranoid, we
// add protection against an infinite recursion.
if( _fDeleteLogAndReInitializeVolume ) TrkRaiseWin32Error( ERROR_OPEN_FAILED ); _fDeleteLogAndReInitializeVolume = TRUE;
__try { TrkLog(( TRKDBG_VOLUME, TEXT("Re-initializing volume %c:"), VolChar(_iVol) ));
// Re-initialize the CLogFile.
DeleteAndReinitializeLog();
// Recreate the volinfo in the new log's header
_fDirty = TRUE; // Force a flush
VolumeSanityCheck(); } __finally { _fDeleteLogAndReInitializeVolume = FALSE; }
}
//+----------------------------------------------------------------------------
//
// CVolume::UnInitialize
//
// Unregister our PNP handle, free critical sections, etc.
//
//+----------------------------------------------------------------------------
void CVolume::UnInitialize() { if( _fInitialized ) { IFDBG( _cLocks++; )
UnregisterPnPVolumeNotification(); _ssh = NULL;
if (_hVolume != NULL) NtClose(_hVolume);
__try { _cLogFile.UnInitialize(); } __except( EXCEPTION_EXECUTE_HANDLER ) // BreakOnDebuggableException() )
{ TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CVolume::UnInitialize after _cLogFile.UnInitialize for %c: %08x"), VolChar(_iVol), GetExceptionCode() )); }
_fInitialized = FALSE; _csHandles.UnInitialize(); _csVolume.UnInitialize();
IFDBG( _cLocks--; )
TrkAssert( 0 == _cLocks ); } _ObjIdIndexChangeNotifier.UnInitialize(); }
//+----------------------------------------------------------------------------
//
// CVolume::Flush
//
// Flush the volinfo structure, the log, and the logfile. In the process,
// mark the logfile header to show a proper shutdown. If we're in the middle
// of a service shutdown, and there's a problem with the log, don't run
// the recovery code.
//
//+----------------------------------------------------------------------------
void CVolume::Flush(BOOL fServiceShutdown) { Lock(); __try { if( _fDirty ) SaveVolInfo();
__try { _cLog.Flush( ); // Flushes to CLogFile
_cLogFile.SetShutdown( TRUE ); // Causes a flush to disk if necessary
} __except( !fServiceShutdown && 0 == _cHandleLocks && IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // Note that we don't handle this exception if the _cHandleLocks is non-zero.
// In this case we're in fast-path and must complete quickly, and the following
// calls could be too time consuming. We must complete quickly because
// CloseVolumeHandles uses that lock, and that call might be called on the
// SCM thread (for e.g. a volume lock event). The SCM thread is shared by
// all services in the process, so we must fast-path anything on it.
if( IsErrorDueToLockedVolume( GetExceptionCode() )) { CloseAndReopenVolumeHandles(); // Reopen might fail
_cLog.Flush(); _cLogFile.SetShutdown( TRUE ); } else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL );
DeleteLogAndReInitializeVolume(); } } } __finally { Unlock(); } }
//+----------------------------------------------------------------------------
//
// CVolume::OpenFile
//
// Open a file on this volume, given the file's object ID.
//
//+----------------------------------------------------------------------------
BOOL CVolume::OpenFile( const CObjId &objid, ACCESS_MASK AccessMask, ULONG ShareAccess, HANDLE *ph) { NTSTATUS status;
Lock(); __try { status = OpenFileById( _tszVolumeDeviceName, objid, AccessMask, ShareAccess, 0, ph );
if( NT_SUCCESS(status) ) return TRUE; else if( STATUS_OBJECT_NAME_NOT_FOUND == status ) return FALSE; else TrkRaiseNtStatus( status ); } __finally { Unlock(); }
return( FALSE );
} // CVolume::OpenFile()
//+----------------------------------------------------------------------------
//
// CVolume::LoadSyncVolume
//
// Load the TRKSVR_SYNC_VOLUME message request for this volume, if necessary.
// The call of this message to trksvr is actually sent by the caller. On
// return of that request, UnloadSyncVolume method will be called.
//
//+----------------------------------------------------------------------------
BOOL CVolume::LoadSyncVolume( TRKSVR_SYNC_VOLUME *pSyncVolume, EAggressiveness eAggressiveness, BOOL* pfSyncNeeded ) { CVOL_STATE state = GetState(); BOOL fSuccess = FALSE;
Lock(); __try { if( !_fVolInfoInitialized ) TrkRaiseException( E_UNEXPECTED );
memset( pSyncVolume, 0, sizeof(*pSyncVolume) ); if(pfSyncNeeded) { *pfSyncNeeded = FALSE; }
// See if it's time to transition from not-owned to not-created.
if( NotOwnedExpired() ) SetState( state = VOL_STATE_NOTCREATED );
// Load the message request, if necessary, based on our current state.
if(state == VOL_STATE_NOTCREATED) { // Ordinarily, if we were unable to create this volume due to volume
// quota, we won't try again. But if we're told to be aggressive,
// we'll try anyway.
if( PASSIVE == eAggressiveness && _VolQuotaReached.IsSet() ) { TrkLog(( TRKDBG_VOLUME, TEXT("Not attempting to create new volume ID on %c:; quota reached"), VolChar(_iVol) )); } else { // Generate a new secret for authentication of this volume.
g_ptrkwks->_entropy.Put();
if( !g_ptrkwks->_entropy.InitializeSecret( & _tempSecret ) ) { // This should never happen - even if there hasn't been enough
// entropy yet, more will be generated.
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't generate secret for volume %c:"), VolChar(_iVol) )); goto Exit; }
TrkLog((TRKDBG_VOLUME, TEXT("Generated secret %s for volume %c"), (const TCHAR*)CDebugString(_tempSecret), VolChar( _iVol )));
// Put the secret in the request, and set the request type to "create"
pSyncVolume->secret = _tempSecret; pSyncVolume->SyncType = CREATE_VOLUME;
// Show that we put data into the request that should be sent
// to trksvr.
if (pfSyncNeeded != NULL) *pfSyncNeeded = TRUE; }
} // case CREATE_VOLUME
else if(state == VOL_STATE_NOTOWNED) { // Attempt to claim this volume.
pSyncVolume->volume = _volinfo.volid; pSyncVolume->secretOld = _volinfo.secret; pSyncVolume->SyncType = CLAIM_VOLUME;
// Generate a new secret.
if( !g_ptrkwks->_entropy.InitializeSecret( &_tempSecret ) ) { TrkLog(( TRKDBG_ERROR, TEXT("Couldn't generate secret for volume %c:"), VolChar(_iVol) )); goto Exit; }
TrkLog((TRKDBG_VOLUME, TEXT("Generated secret %s for volume %c"), (const TCHAR*)CDebugString(_tempSecret), VolChar( _iVol ))); pSyncVolume->secret = _tempSecret;
// Show that the request should be sent.
if (pfSyncNeeded != NULL) *pfSyncNeeded = TRUE;
} // case CLAIM_VOLUME
} __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in LoadSyncVolume for %c: %08x"), VolChar(_iVol), GetExceptionCode() )); goto Exit; }
fSuccess = TRUE;
Exit:
Unlock(); return( fSuccess );
}
//+----------------------------------------------------------------------------
//
// CVolume::OnRestore
//
// Not currently implemented.
//
//+----------------------------------------------------------------------------
HRESULT CVolume::OnRestore() { return( E_NOTIMPL );
#if 0
HRESULT hr = E_FAIL; CVolumeId volidVolume; const CMachineId mcidLocal( MCID_LOCAL ); NTSTATUS status;
memset( &_volinfo, 0, sizeof(_volinfo) ); hr = S_OK;
Lock(); __try // __finally
{ __try {
// Get volume id from two different places: in the log file, and on
// the volume. If the two
// disagree, use the object id in the log file, overwrite the other
// one. Put the volume into NOTOWNED state.
TrkLog(( TRKDBG_VOLUME, TEXT("Checking for recorded id's on volume %c:"), VolChar(_iVol) ));
LoadVolInfo();
TrkLog(( TRKDBG_VOLUME, TEXT("volume id in log file ---- (%s) %c:"), CDebugString(_volinfo.volid)._tsz, VolChar(_iVol) ));
status = QueryVolumeId(_tszVolumeDeviceName, &volidVolume); if(!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) { TrkLog((TRKDBG_ERROR, TEXT("Can't get id from volume %c"), VolChar(_iVol))); SetState(VOL_STATE_NOTCREATED); } // if no id is set on the volume, adopt from the log file
if(volidVolume == CVolumeId() && _volinfo.volid != CVolumeId()) { status = SetVolIdOnVolume(_volinfo.volid); g_ptrkwks->_entropy.Put(); if(!NT_SUCCESS(status)) { TrkRaiseNtStatus(status); } SetState( VOL_STATE_NOTOWNED ); } else if(volidVolume != _volinfo.volid) // The log file could have been copied or moved before the restore
// happened, in order to be safe we trash the volume.
{ SetState(VOL_STATE_NOTCREATED); }
hr = S_OK; } __except (BreakOnDebuggableException()) { TrkLog((TRKDBG_ERROR, TEXT("OnRestore failed"))); hr = GetExceptionCode(); }
// If an un-recoverable log error was raised, re-initialize everything
if( TRK_E_CORRUPT_LOG == hr ) { TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL ); __try { DeleteLogAndReInitializeVolume(); } __except( BreakOnDebuggableException()) { hr = GetExceptionCode(); } } else if( IsErrorDueToLockedVolume(hr) ) { CloseAndReopenVolumeHandles(); // Reopen might fail
TrkRaiseException( hr ); } } __finally { Unlock(); }
return hr;
#endif // #if 0
}
//+----------------------------------------------------------------------------
//
// CVolume::LoadQueryVolume
//
// Load a TRKSVR_SYNC_VOLUME request for this volume, if necessary. If we
// load it, the caller will send it to trksvr. On return of that request,
// UnloadQueryVolume method will be called.
//
//+----------------------------------------------------------------------------
BOOL CVolume::LoadQueryVolume( TRKSVR_SYNC_VOLUME *pQueryVolume ) { BOOL fSuccess = FALSE;
Lock(); __try { // Don't do anything we're not even in trksvr.
if(GetState() == VOL_STATE_NOTCREATED) { goto Exit; }
// Put our volid & log sequence number into the request.
memset( pQueryVolume, 0, sizeof(*pQueryVolume) );
pQueryVolume->SyncType = QUERY_VOLUME; pQueryVolume->volume = _volinfo.volid; pQueryVolume->seq = _cLog.GetNextSeqNumber(); // Never raises
} __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in LoadQueryVolume for %c: %08x"), VolChar(_iVol), GetExceptionCode() )); goto Exit; }
fSuccess = TRUE;
Exit:
Unlock(); return( fSuccess ); }
// Originally, trkwks bundle up volume requests (sync, claim, create) and call the DC. After the
// DC returns, this function is called to put necessary information back on the volume. Now the
// DC callback mechanism is added. When there are create volume requests, the DC will callback and
// this function is called by the DC callback function. The DC needs to know if each create volume
// request is successfully finished by the trkwks, so this function has to put an HRESULT to
// indicate that in the hr field of the TRKSVR_SYNC_VOLUME structure.
//+----------------------------------------------------------------------------
//
// CVolume::UnloadSyncVolume
//
// A sync-volume request was loaded in LoadSyncVolume, sent to trksvr, and
// we now need to interpret the result. If we successfully completed
// a create or claim, we'll go into the owned state.
//
//+----------------------------------------------------------------------------
BOOL CVolume::UnloadSyncVolume( TRKSVR_SYNC_VOLUME *pSyncVolume ) { BOOL fSuccess = FALSE; BOOL fWrite = FALSE; CMachineId mcidLocal( MCID_LOCAL );
Lock(); __try { if( !_fVolInfoInitialized ) TrkRaiseException( E_UNEXPECTED );
if(pSyncVolume->hr == S_OK) { // Clear the bit that indicates we've reported a vol quota event.
// That way, the next time we get a volume quota error, we'll report
// to the event log.
_VolQuotaReached.Clear();
switch( pSyncVolume->SyncType ) { case CREATE_VOLUME: { NTSTATUS status = STATUS_SUCCESS;
// Write the volume ID to the volume meta-data.
status = SetVolIdOnVolume( pSyncVolume->volume ); g_ptrkwks->_entropy.Put(); if( !NT_SUCCESS(status) ) __leave;
TrkLog(( TRKDBG_VOLUME, TEXT("Newly-created vol id = %s, %c:"), (const TCHAR*)CDebugString(pSyncVolume->volume), VolChar(_iVol) ));
// Create a fresh log
DeleteAndReinitializeLog();
// Update _volinfo
_fDirty = TRUE; _volinfo.cftLastRefresh = pSyncVolume->ftLastRefresh;
// Set _volinfo.volid = pSyncVolume->volume
SetVolIdInVolInfo( pSyncVolume->volume );
_volinfo.machine = mcidLocal; _volinfo.secret = _tempSecret;
// And update our state.
SetState( VOL_STATE_OWNED ); // Flushes _volinfo
TrkAssert( VOL_STATE_OWNED == GetState() );
TrkReportEvent( EVENT_TRK_SERVICE_VOLUME_CREATE, EVENTLOG_INFORMATION_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), static_cast<const TCHAR*>( CStringize( _volinfo.volid )), NULL ); } // case CREATE_VOLUME
break;
case CLAIM_VOLUME: { RaiseIfWriteProtectedVolume(); _fDirty = TRUE;
_volinfo.machine = mcidLocal; _volinfo.cftLastRefresh = pSyncVolume->ftLastRefresh; _volinfo.secret = _tempSecret;
SetState( VOL_STATE_OWNED ); // Flushes _volinfo
TrkAssert( VOL_STATE_OWNED == GetState() ); Seek( pSyncVolume->seq );
TrkReportEvent( EVENT_TRK_SERVICE_VOLUME_CLAIM, EVENTLOG_INFORMATION_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), static_cast<const TCHAR*>( CStringize( _volinfo.volid )), TRKREPORT_LAST_PARAM );
} // case CLAIM_VOLUME
break;
default:
TrkAssert( FALSE && TEXT("Invalid SyncType given to CVolume::Serialize") ); break;
} // switch
fSuccess = TRUE;
} // if(pSyncVolume->hr == S_OK)
else { // If this is a quota error, log it (but only log it once
// per machine per transition).
if( TRK_E_VOLUME_QUOTA_EXCEEDED == pSyncVolume->hr ) { TrkLog(( TRKDBG_ERROR, TEXT("Vol quota reached") )); if( !_VolQuotaReached.IsSet() ) { _VolQuotaReached.Set(); TrkReportEvent( EVENT_TRK_SERVICE_VOL_QUOTA_EXCEEDED, EVENTLOG_WARNING_TYPE, TRKREPORT_LAST_PARAM ); }
// We'll call this success so that we don't retry. We'll try again
// later when the infrequent timer goes off.
fSuccess = TRUE; }
SetState(VOL_STATE_NOTOWNED); __leave; }
} __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in UnloadSyncVolume for %c: %08x"), VolChar(_iVol), GetExceptionCode() )); }
if( !fSuccess && pSyncVolume->SyncType == CREATE_VOLUME ) { __try { CVolumeSecret ToReturn = _tempSecret; _tempSecret = CVolumeSecret(); g_ptrkwks->_entropy.ReturnUnusedSecret( &ToReturn );
if( SUCCEEDED(pSyncVolume->hr) ) pSyncVolume->hr = E_FAIL; } __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception (2) in UnloadSyncVolume for %c: %08x"), VolChar(_iVol), GetExceptionCode() )); } }
Unlock(); return( fSuccess );
} // CVolume::UnloadSyncVolume
//+----------------------------------------------------------------------------
//
// CVolume::UnloadQueryVolume
//
// The volume manager called LoadQueryVolume, sent the request to trskvr,
// and is now giving us the result.
//
//+----------------------------------------------------------------------------
BOOL CVolume::UnloadQueryVolume( const TRKSVR_SYNC_VOLUME *pQueryVolume ) { BOOL fSuccess = FALSE;
Lock(); __try { // Was the request successful?
if(pQueryVolume->hr == S_OK) { // Go into the owned state, if we're not there
// already.
SetState( VOL_STATE_OWNED );
// Seek the log to match what trksvr expects. If this causes the
// seek pointer to be backed up, it will set the timer to trigger
// a new move-notification to trksvr.
Seek( pQueryVolume->seq ); } else // DC didn't return VOLUME_OK
{ TrkLog((TRKDBG_VOLUME, TEXT("DC returned %s for QueryVolume of volume %s (%c:) -> VOL_STATE_NOTOWNED"), GetErrorString(pQueryVolume->hr), (const TCHAR*)CDebugString(pQueryVolume->volume), VolChar(_iVol) ));
// If there was a problem, go into the not-owned state.
SetState(VOL_STATE_NOTOWNED); } } __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in UnloadQueryVolume for %c: %08x"), VolChar(_iVol), GetExceptionCode() )); goto Exit; }
fSuccess = TRUE;
Exit:
Unlock(); return( fSuccess );
}
//+----------------------------------------------------------------------------
//
// CVolume::Append
//
// Append a move notification to the end of this volume's log.
//
//+----------------------------------------------------------------------------
void CVolume::Append( const CDomainRelativeObjId &droidCurrent, const CDomainRelativeObjId &droidNew, const CMachineId &mcidNew, const CDomainRelativeObjId &droidBirth) { //TrkLog((TRKDBG_VOL_REFCNT, TEXT("CVolume(%08x)::Append refcnt=%d (should be 2, sometimes >2)"), this, _lRef));
Lock(); __try // __finally
{ // Validate the IDs
const CVolumeId volidZero; const CObjId objidZero;
if( volidZero == droidCurrent.GetVolumeId() || objidZero == droidCurrent.GetObjId() || volidZero == droidNew.GetVolumeId() || objidZero == droidNew.GetObjId() || volidZero == droidBirth.GetVolumeId() || objidZero == droidBirth.GetObjId() ) { // In the append path, we only raise NTSTATUS errors, not HRESULTs
TrkRaiseException( STATUS_OBJECT_NAME_INVALID ); }
__try // __except
{ _cLog.Append( droidCurrent.GetVolumeId(), droidCurrent.GetObjId(), droidNew, mcidNew, droidBirth ); } __except( IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // We had a potentially recoverable exception. Try to handle it
// and retry the append.
if( IsErrorDueToLockedVolume( GetExceptionCode() ) ) { CloseAndReopenVolumeHandles(); // Reopen might fail
} else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL );
// The log is corrupted. Re-initialize, then attempt the Append again.
// If this raises, it's an unrecoverable exception, so we just pass
// it up.
DeleteLogAndReInitializeVolume(); }
// Retry the Append, which could raise again, but this time we won't catch it.
_cLog.Append( droidCurrent.GetVolumeId(), droidCurrent.GetObjId(), droidNew, mcidNew, droidBirth );
} } __finally { Unlock(); } }
//+----------------------------------------------------------------------------
//
// CVolume::Read
//
// Read one or more entries from the log, from the current seek position.
//
//+----------------------------------------------------------------------------
void CVolume::Read(CObjId *pobjidCurrent, CDomainRelativeObjId *pdroidBirth, CDomainRelativeObjId *pdroidNew, SequenceNumber *pseqFirst, ULONG *pcRead) {
Lock(); __try // __finally
{ __try { _cLog.Read( pobjidCurrent, pdroidBirth, pdroidNew, pseqFirst, pcRead ); } __except( IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // Try to recover from this error and if possible retry
// the read.
if( IsErrorDueToLockedVolume( GetExceptionCode() )) { CloseAndReopenVolumeHandles(); // Reopen might fail
// Retry the read, which could raise again, but this time we won't
// catch it.
TrkLog(( TRKDBG_VOLUME, TEXT("Retrying CLog::Read") )); _cLog.Read( pobjidCurrent, pdroidBirth, pdroidNew, pseqFirst, pcRead ); } else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL );
// The log is corrupted. Re-initialize, then pass up the error.
DeleteLogAndReInitializeVolume(); TrkRaiseException( GetExceptionCode() ); } } } __finally { Unlock(); } }
//+----------------------------------------------------------------------------
//
// CVolume::Search
//
// Search the log for a move-notification (from droidCurrent).
//
//+----------------------------------------------------------------------------
BOOL CVolume::Search( const CDomainRelativeObjId & droidCurrent, CDomainRelativeObjId * pdroidNew, CMachineId *pmcidNew, CDomainRelativeObjId * pdroidBirth ) { BOOL fFound = FALSE;
Lock(); __try // __finally
{ __try { // Perfbug: Don't hold the log locked during the whole search such that
// it locks out Appends.
fFound = _cLog.Search( droidCurrent.GetObjId(), pdroidNew, pmcidNew, pdroidBirth ); } __except( IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // Try to recover from this error and retry the search.
if( IsErrorDueToLockedVolume( GetExceptionCode() )) { CloseAndReopenVolumeHandles(); // Reopen might fail
// Retry the search, which could raise again, but this time we won't catch it.
fFound = _cLog.Search( droidCurrent.GetObjId(), pdroidNew, pmcidNew, pdroidBirth ); } else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL );
// The log is corrupted. Re-initialize, and pass up the error.
DeleteLogAndReInitializeVolume(); TrkRaiseException( GetExceptionCode() ); } } } __finally { Unlock(); }
return( fFound ); }
//+----------------------------------------------------------------------------
//
// CVolume::Seek
//
// Seek the log to a particular sequence number.
//
//+----------------------------------------------------------------------------
BOOL CVolume::Seek( SequenceNumber seq ) { BOOL fSuccess = FALSE;
Lock(); __try { __try { fSuccess = _cLog.Seek( seq ); } __except( IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // Try to recover from this error and retry the seek.
if( IsErrorDueToLockedVolume( GetExceptionCode() )) { CloseAndReopenVolumeHandles(); // Reopen might fail
// Retry the Seek, which could raise again, but this time we won't catch it.
fSuccess = _cLog.Seek( seq ); } else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL );
// The log is corrupted. Re-initialize and pass up the error.
DeleteLogAndReInitializeVolume(); TrkRaiseException( GetExceptionCode() ); } } } __finally { Unlock(); }
if( fSuccess ) TrkLog(( TRKDBG_VOLUME, TEXT("Log on %c: sought to seq %d"), VolChar(_iVol), seq )); else TrkLog(( TRKDBG_VOLUME, TEXT("Log on %c: couldn't be sought to seq %d"), VolChar(_iVol), seq ));
return( fSuccess ); }
//+----------------------------------------------------------------------------
//
// CVolume::Seek
//
// Seek to a relative (e.g. back up 2) or absolute (e.g. first) position.
//
//+----------------------------------------------------------------------------
void CVolume::Seek( int origin, int iSeek ) { Lock(); __try // __finally
{ __try { _cLog.Seek( origin, iSeek ); } __except( IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // Attempt to recover from this error and retry the seek.
if( IsErrorDueToLockedVolume( GetExceptionCode() )) { CloseAndReopenVolumeHandles(); // Reopen might fail
// Retry the Seek, which could raise again, but this time we won't catch it.
_cLog.Seek( origin, iSeek ); } else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL );
// The log is corrupted. Re-initialize and pass up the error.
DeleteLogAndReInitializeVolume(); TrkRaiseException( GetExceptionCode() ); } } } __finally { Unlock(); }
}
//+----------------------------------------------------------------------------
//
// CVolume::GetVolumeId
//
// Get the volume ID without taking the lock. This was done
// so that CVolumeManager::IsDuplicateID can check the volid
// of other volumes without deadlocking. Otherwise we run the
// risk of one volume holding its locks and trying to get
// another volume's lock (using GetVolumeId on that volume)
// while another thread is in that volume doing the same for
// this volume.
//
//+----------------------------------------------------------------------------
const CVolumeId CVolume::GetVolumeId() { CVolumeId volid; ULONG cAttempts = 0;
// Spin until we get a good volid.
while( TRUE ) { // Get the update counter before and after reading
// from the volid. (This assumes that
// reading the long is atomic.)
LONG lVolidUpdatesBefore = _lVolidUpdates;
volid = _volinfo.volid;
LONG lVolidUpdatesAfter = _lVolidUpdates;
// When the _volinfo is updated, the _lVolidUpdates is
// incremented before and after the update. So if there
// was an update in progress when we started, it will
// be an odd number.
//
// Ensure there was no update in progress when we read
// the volid, and there was no update started while we
// were reading the volid.
if( (lVolidUpdatesBefore & 1) || lVolidUpdatesBefore != lVolidUpdatesAfter ) { // Check for timeout (30 seconds)
if( 3000 < ++cAttempts ) { TrkLog(( TRKDBG_ERROR, TEXT("Failed spin in GetVolumeId") )); TrkRaiseWin32Error( WAIT_TIMEOUT ); }
// Wait for the update to complete then try again.
Sleep( 10 ); continue; } else break; }
return( volid ); }
//+----------------------------------------------------------------------------
//
// CVolume::GetState
//
// Get the current state of this volume (owned, not-owned, or not-created).
//
//+----------------------------------------------------------------------------
CVolume::CVOL_STATE CVolume::GetState() { CVolumeId volNULL; CVOL_STATE state = VOL_STATE_UNKNOWN;
Lock(); __try {
if( _volinfo.fNotCreated ) { state = VOL_STATE_NOTCREATED; }
// If the time we entered the not-owned state is non-zero, then
// we're certainly in the not-owned state. Also, if the machine ID
// in the _volinfo header doesn't match the local machine, we're
// not owned.
else if(_volinfo.cftEnterNotOwned != 0 || _volinfo.machine != CMachineId(MCID_LOCAL) ) { // Is this the first time that we realized we're not owned?
if( !IsWriteProtectedVolume() && 0 == _volinfo.cftEnterNotOwned ) { _volinfo.cftEnterNotOwned.SetToUTC(); _fDirty = TRUE; Flush(); }
state = VOL_STATE_NOTOWNED; }
// Otherwise, we must be properly owned.
else { state = VOL_STATE_OWNED; } } __finally { Unlock(); }
return state; }
//+----------------------------------------------------------------------------
//
// CVolume::SetState
//
// Change the current state of the volume. This checks for valid transitions.
// For example, you can't transition from not-created to not-owned (such
// a request is silently ignored). This alleviates the caller from having
// to perform this logic.
//
//+----------------------------------------------------------------------------
void CVolume::SetState(CVOL_STATE volstateTarget) { CVOL_STATE volstateCurrent = GetState(); NTSTATUS status = STATUS_SUCCESS; VolumePersistentInfo volinfoNew = _volinfo; BOOL fDirtyNew = _fDirty;
// Make sure the volume is writeable
RaiseIfWriteProtectedVolume();
Lock(); __try { switch( volstateTarget ) {
case VOL_STATE_NOTOWNED:
// We can only go to not-owned from owned.
if( VOL_STATE_OWNED == volstateCurrent ) { TrkAssert( !volinfoNew.fNotCreated ); TrkLog(( TRKDBG_VOLUME, TEXT("Entering not-owned state on vol %c:"), VolChar(_iVol) ));
RaiseIfWriteProtectedVolume(); fDirtyNew = TRUE; volinfoNew.cftEnterNotOwned.SetToUTC(); } break;
case VOL_STATE_NOTCREATED:
// We can always go to not-created.
if( volstateCurrent != VOL_STATE_NOTCREATED ) { TrkLog(( TRKDBG_VOLUME, TEXT("Entering not-created state on vol %c:"), VolChar(_iVol) ));
RaiseIfWriteProtectedVolume(); fDirtyNew = TRUE; volinfoNew.fNotCreated = TRUE; volinfoNew.cftEnterNotOwned = CFILETIME(0); }
break;
case VOL_STATE_OWNED:
if( VOL_STATE_NOTCREATED == volstateCurrent ) { // We're going from not-created to owned, so we need to make
// all our OIDs reborn.
TrkLog(( TRKDBG_VOLUME, TEXT("Entering owned state (from not-created) on vol %c:"), VolChar(_iVol) ));
RaiseIfWriteProtectedVolume(); fDirtyNew = TRUE; volinfoNew.fNotCreated = FALSE; TrkAssert( CFILETIME(0) == volinfoNew.cftEnterNotOwned );
// Since we now have a new volid, we must give all the existing
// files new object IDs.
//MarkForMakeAllOidsReborn();
volinfoNew.fDoMakeAllOidsReborn = TRUE; } else if( VOL_STATE_NOTOWNED == volstateCurrent ) { // We're going from not-owned to owned.
TrkLog(( TRKDBG_VOLUME, TEXT("Entering owned state (from not-owned) on vol %c:"), VolChar(_iVol) )); TrkAssert( !volinfoNew.fNotCreated );
RaiseIfWriteProtectedVolume(); fDirtyNew = TRUE; volinfoNew.cftEnterNotOwned = CFILETIME(0); }
TrkAssert( CVolumeId() != _volinfo.volid ); break;
default: TrkAssert( !TEXT("Bad target state in CVolume::SetState") );
} // switch( volstateTarget )
// If we modified the volinfo, write it back out.
_volinfo = volinfoNew; _fDirty |= fDirtyNew;
Flush();
} __finally { Unlock(); }
return;
}
//+----------------------------------------------------------------------------
//
// CVolume::NotOwnedExpired
//
// Have we been in the not-owned state for long enough that we should be
// in the not-created state?
//
//+----------------------------------------------------------------------------
BOOL CVolume::NotOwnedExpired() { Lock(); __try { if(_volinfo.cftEnterNotOwned != 0) { CFILETIME cftDiff = CFILETIME() - _volinfo.cftEnterNotOwned; ULONG SecondsDiff = static_cast<ULONG>((LONGLONG)cftDiff/10000000);
if(SecondsDiff > _pTrkWksConfiguration->GetVolNotOwnedExpireLimit()) { return TRUE; } } } __finally { Unlock(); }
return FALSE; }
//+----------------------------------------------------------------------------
//
// CVolume::MakeAllOidsReborn
//
// Reset (zero out) the birth IDs (actually, all 48 extended bytes) of
// all the files on this volume. That makes the file no longer a link source,
// so we won't try to track it. If someone subsequently makes a link
// to it, NTFS will fill in a new birth ID.
//
//+----------------------------------------------------------------------------
BOOL CVolume::MakeAllOidsReborn() { CObjIdEnumerator oie; BOOL fSuccess = FALSE; CObjId objid; CDomainRelativeObjId droidBirth; NTSTATUS status; CVolumeId vidNull; BOOL fLocked = FALSE;
__try { // Give all the files with object IDs a fresh birth ID, as if the
// file had first been linked to on this volume.
TrkLog(( TRKDBG_VOLUME, TEXT("Making OIDs reborn on volume %c:"), VolChar(_iVol) ));
if(oie.Initialize(_tszVolumeDeviceName)) { if(oie.FindFirst(&objid, &droidBirth)) { do { g_ptrkwks->RaiseIfStopped();
// If this has what looks like an invalid birth ID, ignore it.
if( CObjId() == droidBirth.GetObjId() ) continue;
// We only take the lock directly around the make-reborn
// call, since with the sleep below we could be in this routine
// for a while.
Lock(); fLocked = TRUE; TrkAssert( 1 == _cLocks ); MakeObjIdReborn( _tszVolumeDeviceName, objid ); Unlock(); fLocked = FALSE;
Sleep( 100 ); // don't hog the machine
} while(oie.FindNext(&objid, &droidBirth)); } } } __except( BreakOnDebuggableException() ) { __try { if( TRK_E_CORRUPT_LOG == GetExceptionCode() ) { TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL ); BreakIfRequested(); DeleteLogAndReInitializeVolume(); } else if( IsErrorDueToLockedVolume( GetExceptionCode() )) { CloseAndReopenVolumeHandles(); TrkRaiseException( GetExceptionCode() ); }
TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CVolume::MakeAllOidsReborn for %c: %08x"), VolChar(_iVol), GetExceptionCode() )); } __finally { if( fLocked ) Unlock(); }
goto Exit; }
fSuccess = TRUE;
Exit:
oie.UnInitialize();
if(!fSuccess) { TrkLog((TRKDBG_ERROR, TEXT("Can't delete all object ids on volume %c:"), VolChar(_iVol) )); }
return fSuccess; }
//+----------------------------------------------------------------------------
//
// CVolume::OnHandlesMustClose
//
// This routine is called from CLogFile if it discovers that the log file
// needs to be closed (an oplock break). We close all handles on the volume
// and start the reopen timer.
//
//+----------------------------------------------------------------------------
void CVolume::OnHandlesMustClose() { CloseVolumeHandles(); // Doesn't raise
g_ptrkwks->SetReopenVolumeHandlesTimer(); }
//+----------------------------------------------------------------------------
//
// CVolume::FileActionIdNotTunnelled
//
// This method is called as an event notification, indicating that NTFS has
// notified us that a file could not be tunnelled. We do the tunnelling manually
// here.
//
//+----------------------------------------------------------------------------
#define ON_NOT_TUNNELLED_DELAY 500 // .5 seconds
void CVolume::FileActionIdNotTunnelled( FILE_OBJECTID_INFORMATION * poi ) { ULONG ulMillisecondsSleptSoFar = 0; HANDLE hFile = NULL;
// We don't take the volume lock here. So don't attempt to
// do anything other than simple I/O. We don't take the lock because
// we want to ensure that tunnelling is resolved quickly without
// getting blocked.
//
// Open the file being "tunnelled from" by OBJECTID
// Delete the object id
// Close
// Open the file being "tunnelled to" by FileReference
// Set the object id and extra data
// Close
// Test hook
IFDBG( _pTunnelTest->ReleaseAndWait() );
__try { if (_hVolume == NULL) { // Couldn't reopen the volume
__leave; }
NTSTATUS Status;
OBJECT_ATTRIBUTES oa; UNICODE_STRING uId; IO_STATUS_BLOCK ios; CObjId objid( FOI_OBJECTID, *poi ); int i;
// Ignore if this isn't a link tracking (e.g. it's an NTFRS) object ID.
if( CObjId() == CObjId(FOI_BIRTHID, *poi) ) { TrkLog(( TRKDBG_VOLUME, TEXT("Ignoring not-tunneled notification for %s"), (const TCHAR*)CDebugString( objid ) )); __leave; }
uId.Length = sizeof(poi->ObjectId); uId.MaximumLength = sizeof(poi->ObjectId); uId.Buffer = (PWSTR) poi->ObjectId;
InitializeObjectAttributes( &oa, &uId, OBJ_CASE_INSENSITIVE, _hVolume, NULL );
// -----------------
// Open the old file
// -----------------
// Some kind of write access, along with restore privelege, is required
// for set/delete OID calls.
EnableRestorePrivilege(); Status = NtCreateFile( &hFile, SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, &oa, &ios, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_NO_RECALL | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
// ------------------------------
// Delete the OID on the old file
// ------------------------------
if (NT_SUCCESS(Status)) { Status = NtFsControlFile( hFile, NULL, NULL, NULL, &ios, FSCTL_DELETE_OBJECT_ID, NULL, // in buffer
0, // in buffer size
NULL, // Out buffer
0); // Out buffer size
NtClose( hFile ); hFile = NULL;
if (NT_SUCCESS(Status)) { TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %s - deleted from old file"), (const TCHAR*)CDebugString(objid) )); } else { TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %c:%s - couldn't FSCTL_DELETE_OBJECT_ID ntstatus=%08x"), VolChar(_iVol), (const TCHAR*)CDebugString(objid), Status )); } } // if (NT_SUCCESS(Status))
else { // We couldn't open the old file, so we'll ignore it and try to set the
// object ID.
TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %c:%s - couldn't open old file %08x"), VolChar(_iVol), (const TCHAR*)CDebugString(objid), Status)); }
if( Status == STATUS_INVALID_DEVICE_REQUEST || IsErrorDueToLockedVolume( Status ) ) { // If we get STATUS_INVALID_DEVICE_REQUEST, then _hVolume is
// broken.
CloseVolumeHandles(); g_ptrkwks->SetReopenVolumeHandlesTimer(); __leave; }
// -----------------
// Open the new file
// -----------------
uId.Length = sizeof(poi->FileReference); uId.MaximumLength = sizeof(poi->FileReference); uId.Buffer = (PWSTR) &poi->FileReference;
Status = NtCreateFile( &hFile, SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, &oa, &ios, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_BY_FILE_ID | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_NO_RECALL | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
// ---------------------------
// Set the OID on the new file
// ---------------------------
if (NT_SUCCESS(Status)) { Status = NtFsControlFile( hFile, NULL, NULL, NULL, &ios, FSCTL_SET_OBJECT_ID, poi->ObjectId, sizeof(FILE_OBJECTID_BUFFER), NULL, // Out buffer
0); // Out buffer size
NtClose(hFile); hFile = NULL;
TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %s: FSCTL_SET_OBJECT_ID %s %08x"), (const TCHAR*)CDebugString(objid), NT_SUCCESS(Status) ? TEXT("succeeded") : TEXT("failed"), Status )); } else { TrkLog((TRKDBG_TUNNEL, TEXT("Tunnelling objid %c:%s - couldn't OpenByFileReference ntstatus=%08x"), VolChar(_iVol), (const TCHAR*)CDebugString(objid), Status )); }
if(Status == STATUS_INVALID_DEVICE_REQUEST || IsErrorDueToLockedVolume( Status ) ) { // If we get STATUS_INVALID_DEVICE_REQUEST, then _hVolume is
// broken.
CloseVolumeHandles(); g_ptrkwks->SetReopenVolumeHandlesTimer(); __leave; }
} __except( BreakOnDebuggableException() ) { TrkLog(( TRKDBG_ERROR, TEXT("Exception %08x in CVolume::FileActionIdNotTunnelled"), GetExceptionCode() )); }
if( NULL != hFile ) NtClose( hFile );
return; }
//+----------------------------------------------------------------------------
//
// CVolume::NotifyAddOrDelete
//
// This method is called as an event notification, indicating that NTFS has
// notified us that the volume ID has been modified. We use this to ensure
// the volume ID doesn't get incorrectly modified.
//
// If you watch the object ID notification queue while someone sets the
// volume ID directly in NTFS, you'll see:
// * a remove of the old ID (setting a new ID shows up as a remove/add),
// * an add of the bogus ID,
// * a remove of the bogus ID (part of the SetVolid that we do in this routine),
// * an add of the correct ID.
//
//+----------------------------------------------------------------------------
void CVolume::NotifyAddOrDelete( ULONG Action, const CObjId & objid ) { // Not supported
return; }
//+----------------------------------------------------------------------------
//
// CVolume::LoadVolInfo
//
// Load the _volinfo member from the log.
//
//+----------------------------------------------------------------------------
void CVolume::LoadVolInfo() { AssertLocked(); TrkAssert( CVOLUME_HEADER_LENGTH == sizeof(_volinfo) );
// Read _volinfo from the extended header portion of the log.
__try { _cLogFile.ReadExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) ); } __except( IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // Attempt to recover from this error and retry the read.
if( IsErrorDueToLockedVolume( GetExceptionCode() )) { CloseAndReopenVolumeHandles(); _cLogFile.ReadExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) ); } else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL ); DeleteLogAndReInitializeVolume(); } } }
//+----------------------------------------------------------------------------
//
// CVolume::SaveVolInfo
//
// Write the _volinfo structure to the extended header portion of the log.
// Clear _fDirty if successful.
//
//+----------------------------------------------------------------------------
void CVolume::SaveVolInfo( ) { AssertLocked();
__try { _cLogFile.WriteExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) ); _fDirty = FALSE; } __except( IsRecoverableDiskError( GetExceptionCode() ) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // Attempt to recover from this error and retry the write.
if( IsErrorDueToLockedVolume( GetExceptionCode() ) ) { CloseAndReopenVolumeHandles(); _cLogFile.WriteExtendedHeader( CVOLUME_HEADER_START, &_volinfo, sizeof(_volinfo) ); _fDirty = FALSE; } else { TrkAssert( TRK_E_CORRUPT_LOG == GetExceptionCode() ); TrkReportEvent( EVENT_TRK_SERVICE_CORRUPT_LOG, EVENTLOG_ERROR_TYPE, static_cast<const TCHAR*>( CStringize( VolChar(_iVol) )), NULL ); DeleteLogAndReInitializeVolume(); } }
}
//+----------------------------------------------------------------------------
//
// CVolume::CloseVolumeHandles
//
// This method close all handle that this object maintains on the volume.
// This will allow e.g. format or chkdsk /f to run successfully.
// We don't take the volume critsec, so we're guaranteed to run quickly
// and not block.
//
//+----------------------------------------------------------------------------
void CVolume::CloseVolumeHandles( HDEVNOTIFY hdnVolume, EHandleChangeReason eHandleChangeReason ) { HANDLE hVolToClose = NULL;
// This routine never raises
// Is this notification intended for everyone, or specifically
// for us?
if( hdnVolume != NULL && hdnVolume != _hdnVolumeLock ) // No, it's just for another volume.
return;
// Are we already in a CloseVolumeHandles somewhere?
// If so, there's no need to continue, and worse yet if we were
// to continue we could deadlock (scenario: one thread is in
// _cLogFile.Close below unregistering the oplock wait, which is
// blocking, and another thread is executing an oplock break).
if( !BeginSingleInstanceTask( &_cCloseVolumeHandlesInProgress ) ) { TrkLog(( TRKDBG_VOLUME, TEXT("Skipping CloseVolumeHandles, another instance already in progress") )); return; }
// We don't want this method to take the normal _csVolume lock, because
// we're called from threads that must not block (such as the service control handler
// thread). So we just take the limited lock that's used in this method
// and in ReopenVolumeHandles.
LockHandles(); __try { BOOL fAlreadyLockedOrDismounted = _fVolumeLocked || _fVolumeDismounted;
TrkLog(( TRKDBG_WARNING, TEXT("Closing volume handles on %c:"), VolChar(_iVol) ));
// If this notification is specifically for us, then remember
// if we're locked/dismounted.
if( hdnVolume != NULL ) { if( VOLUME_LOCK_CHANGE == eHandleChangeReason ) _fVolumeLocked = TRUE; else if( VOLUME_MOUNT_CHANGE == eHandleChangeReason ) _fVolumeDismounted = TRUE; }
// Is this volume already locked or dismounted?
if( fAlreadyLockedOrDismounted ) { TrkAssert( NULL == _hVolume ); __leave; }
// Close the object ID index directory handle.
_ObjIdIndexChangeNotifier.StopListeningAndClose();
// Close the log.
_cLogFile.Close(); // Doesn't raise
// Prepare to close the volume handle.
if (_hVolume != NULL) { hVolToClose = _hVolume; _hVolume = NULL; }
TrkLog((TRKDBG_VOLUME, TEXT("Volume %c: closed"), VolChar(_iVol))); }
__except( EXCEPTION_EXECUTE_HANDLER ) // BreakThenReturn( EXCEPTION_EXECUTE_HANDLER ))
{ TrkLog(( TRKDBG_ERROR, TEXT("Ignoring unexpected exception in CVolume::CloseVolumeHandles (%08x)"), GetExceptionCode() )); }
UnlockHandles(); EndSingleInstanceTask( &_cCloseVolumeHandlesInProgress );
if( NULL != hVolToClose ) { NtClose( hVolToClose ); TrkLog((TRKDBG_VOLUME, TEXT("(Volume %c: fully closed)"), VolChar(_iVol))); }
}
//+----------------------------------------------------------------------------
//
// CVolume::ReopenVolumeHandles
//
// Reopen the handles that we maintain on the volume. This is synchronized
// with CloseVolumeHandles using the handle critsec. This method does
// nothing if the volume is locked (as indicated by _fVolumeLocked),
// or if the volume handles are already opened.
//
//+----------------------------------------------------------------------------
BOOL CVolume::ReopenVolumeHandles() { NTSTATUS status; BOOL fHandlesLocked = FALSE; BOOL fHandlesOpen = FALSE; BOOL fReopenedLog = FALSE; BOOL fStartedListening = FALSE;
// Don't open if the service is stopping.
g_ptrkwks->RaiseIfStopped();
TrkLog(( TRKDBG_WARNING, TEXT("\nReopenVolumeHandles called on %c:"), VolChar(_iVol) ));
// This method must acquire the _csVolumes critical section like every other
// public method (via the Lock call). It must also acquire the _csHandles
// lock, in order to coordinate with the CloseVolumeHandles and SetUnlockVolume
// methods, which have special needs.
Lock(); __try { LockHandles(); fHandlesLocked = TRUE; // Are we supposed to reopen?
if( IsHandsOffVolumeMode() ) { // Don't open yet, wait until we get an UnLock notification.
TrkLog(( TRKDBG_VOLUME, TEXT("Didn't open handles on %c:, it's %s"), VolChar(_iVol), _fVolumeLocked ? ( _fVolumeDismounted ? TEXT("locked & dismounted") : TEXT("locked") ) : TEXT("dismounted") )); __leave; }
// Open the main volume handle.
if( NULL == _hVolume ) { status = OpenVolume(_tszVolumeDeviceName, &_hVolume); if(!NT_SUCCESS(status)) { if( STATUS_OBJECT_NAME_NOT_FOUND == status ) { TrkLog(( TRKDBG_VOLUME, TEXT("Volume not found in ReopenVolumeHandles, deleting CVolume") )); MarkSelfForDelete(); }
TrkRaiseNtStatus(status); } }
// Start listening for objid index change notifications
__try { fStartedListening = _ObjIdIndexChangeNotifier.AsyncListen( ); } __except( BreakOnDebuggableException() ) { // We should never get a path-not-found error, because meta-files always exist
// on a good NTFS5 volume. If we get one, it's probably because an NTFS5
// volume has been reformatted as a FAT volume.
HRESULT hr = GetExceptionCode(); if( HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr ) { TrkLog(( TRKDBG_VOLUME, TEXT("ObjId Index not found in ReopenVolumeHandles, deleting CVolume") )); MarkSelfForDelete(); }
TrkRaiseException( hr );
}
// Register for PNP notifications. We don't hold the handle lock because this
// call can take a while, and we don't want to block the service control
// handler thread from doing a CloseVolumeHandles.
if( fStartedListening ) {
#if DBG
TrkVerify( 0 == UnlockHandles() ); #else
UnlockHandles(); #endif
fHandlesLocked = FALSE; RegisterPnPVolumeNotification(); LockHandles(); fHandlesLocked = TRUE; }
// If CloseVolumeHandles came in after we released the handle lock just now,
// then abort.
if( NULL == _hVolume ) { TrkLog(( TRKDBG_VOLUME, TEXT("Aborting ReopenVolumeHandles") )); TrkRaiseException( E_FAIL ); }
// Open the log
if( !_cLogFile.IsOpen() ) { for( int i = 0; i < 2; i++ ) { __try { _cLogFile.Initialize( _tszVolumeDeviceName, _pTrkWksConfiguration, this, VolChar(_iVol) ); _cLog.Initialize( _pLogCallback, _pTrkWksConfiguration, &_cLogFile ); } __except( (0 == i && TRK_E_CORRUPT_LOG == GetExceptionCode()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { BreakIfRequested();
// Get rid of the corrupt file
_cLogFile.Delete();
// Loop back and try again.
continue; } break; }
fReopenedLog = TRUE; }
// If we've never read in the volinfo, do so now.
if( !_fVolInfoInitialized ) { LoadVolInfo(); _fVolInfoInitialized = TRUE; TrkLog(( TRKDBG_VOLUME, TEXT("VolId (from log file) for %c: is %s"), VolChar(_iVol), (const TCHAR*)CDebugString(_volinfo.volid) )); }
if( fReopenedLog ) { // Reconcile our volinfo with the log.
// BUGBUG (removable media): We haven't re-read the volinfo out of the log, we're still
// using what we already had in memory. This won't work for removeable
// media, so we need to add some extra checking here.
VolumeSanityCheck();
// Start the move notify timer; we may have been trying to send
// notifies when we discovered that the volume handles were bad.
g_ptrkwks->OnEntriesAvailable(); }
TrkLog((TRKDBG_VOLUME, TEXT("Volume %c open"), VolChar(_iVol))); fHandlesOpen = TRUE; } __finally { // __try
{ // We either open everything, or open nothing
if( AbnormalTermination() ) { CloseVolumeHandles(); g_ptrkwks->SetReopenVolumeHandlesTimer(); // Try again later
} } // __finally
{ if( fHandlesLocked ) UnlockHandles();
Unlock(); } }
return( fHandlesOpen ); }
//+----------------------------------------------------------------------------
//
// CVolume::CloseAndReopenVolumeHandles
//
// One or more of the handles maintained on the volume are bad (for example,
// the handle may have been broken by a dismount). Close all of them, and
// attempt to reopen new ones.
//
//+----------------------------------------------------------------------------
void CVolume::CloseAndReopenVolumeHandles() {
Lock();
__try { // There's the remote possibility that the ReopenVolumeHandles
// call below will call this routine. Just to be paranoid, we
// add protection against an infinite recursion.
if( _fCloseAndReopenVolumeHandles ) TrkRaiseWin32Error( ERROR_OPEN_FAILED ); _fCloseAndReopenVolumeHandles = TRUE;
// Close then reopen the handles
CloseVolumeHandles(); ReopenVolumeHandles(); } __except( BreakOnDebuggableException() ) { TrkLog((TRKDBG_VOLUME, TEXT("Immediate reopen of volume handle failed, set the reopen timer"))); g_ptrkwks->SetReopenVolumeHandlesTimer(); }
_fCloseAndReopenVolumeHandles = FALSE; Unlock(); }
//+----------------------------------------------------------------------------
//
// CVolume::PrepareToReopenVolumeHandles
//
// The handles to the volume were closed at some point, but they may now
// be reopened. Call CVolumeManager::OnVolumeToBeReopened, so that it can
// call us in ReopenVolumeHandles on a worker thread (right now we're on
// the services handler thread, which is shared by all of services.exe).
//
//+----------------------------------------------------------------------------
void CVolume::PrepareToReopenVolumeHandles( HDEVNOTIFY hdnVolume, EHandleChangeReason eHandleChangeReason ) { // It is important that this method does not take any lock.
// This method is called during the volume unlock notification
// that we receive in CTrkWksSvc::ServiceHandler, and we can
// never allow that thread to hang.
// Fortunately, the flags touched in this routine are only
// touched in a single-threaded manner, because they're only
// touched as a result of a message from the service controller,
// for which there's a single thread in the process.
__try { if( _hdnVolumeLock == hdnVolume ) { if( VOLUME_LOCK_CHANGE == eHandleChangeReason ) _fVolumeLocked = FALSE; else if( VOLUME_MOUNT_CHANGE == eHandleChangeReason ) _fVolumeDismounted = FALSE;
if( !_fVolumeLocked && !_fVolumeDismounted ) { TrkLog(( TRKDBG_VOLUME, TEXT("Volume %c: is to be reopened"), VolChar(_iVol) )); _pVolMgr->OnVolumeToBeReopened(); } } } __finally { }
return; }
|