|
|
//+----------------------------------------------------------------------------
//
// Copyright (C) 2000, Microsoft Corporation
//
// File: DfsFolder.cxx
//
// Contents: implements the base DFS Folder class
//
// Classes: DfsFolder.
//
// History: Dec. 8 2000, Author: udayh
//
//-----------------------------------------------------------------------------
#include "DfsFolder.hxx"
#include "DfsFolderReferralData.hxx"
#include "DfsInit.hxx"
//
// logging specific includes
//
#include "DfsFolder.tmh"
//+-------------------------------------------------------------------------
//
// Function: GetReferralData - get the referral data
//
// Arguments: ppReferralData - the referral data for this folder
// pCacheHit - did we find it already loaded?
//
// Returns: Status
// ERROR_SUCCESS if we could get the referral data
// error status otherwise.
//
//
// Description: This routine returns a reference DfsFolderReferralDAta
// for the folder. If one does not already exist in this
// folder, we create a new one. If someone is in the process
// of loading the referral, we wait on the event in
// the referral data which gets signalled when the thread
// responsible for loading is done with the load.
//
//--------------------------------------------------------------------------
DFSSTATUS DfsFolder::GetReferralData( OUT DfsFolderReferralData **ppReferralData, OUT BOOLEAN *pCacheHit, IN BOOLEAN AddToLoadedList ) { DfsFolderReferralData *pRefData; DFSSTATUS Status = STATUS_SUCCESS; *pCacheHit = FALSE;
Status = AcquireWriteLock(); if ( Status != STATUS_SUCCESS ) { return Status; }
// First see if we may need to do a reload.
if (_LoadState == DfsFolderLoadFailed && IsTimeToRetry()) { ASSERT(_pReferralData == NULL); _LoadState = DfsFolderNotLoaded; DFS_TRACE_HIGH(REFERRAL_SERVER, "Retrying failed folder load for %wZ (%wZ)\n", GetFolderMetadataName(), GetFolderLogicalName()); } //
// WE take difference action depending on the load state.
//
switch ( _LoadState ) { case DfsFolderLoaded:
DFS_TRACE_NORM(REFERRAL_SERVER, " Get Referral Data: Cache hit\n"); //
// we are dealing with a loaded folder. Just acquire a reference
// and return the loaded referral data.
//
ASSERT (_pReferralData != NULL);
pRefData = _pReferralData;
pRefData->Timeout = _Timeout; pRefData->AcquireReference(); ReleaseLock(); *pCacheHit = TRUE; *ppReferralData = pRefData;
break;
case DfsFolderNotLoaded:
//
// The folder is not loaded. Make sure that the referral data is
// indeed empty. Create a new instance of the referral data
// and set the state to load in progress.
// The create reference of the folder referral data is inherited
// by the folder. (we are holding a reference to the referral
// data in _pReferralData). This reference is released when
// we RemoveReferralData at a later point.
//
ASSERT(_pReferralData == NULL); DFS_TRACE_NORM(REFERRAL_SERVER, " Get Referral Data: not loaded\n");
_pReferralData = new DfsFolderReferralData( &Status, this ); if ( _pReferralData != NULL ) { if(Status == ERROR_SUCCESS) { _LoadState = DfsFolderLoadInProgress;
if (IsFolderRoot() == TRUE) { _pReferralData->SetRootReferral(); }
if (IsFolderInSiteReferrals() || ((_pParent != NULL) && (_pParent->IsFolderInSiteReferrals()))) { _pReferralData->SetInSite(); }
//
// Site costing is inherited from the parent.
//
if (IsFolderSiteCostingEnabled() || ((_pParent != NULL) && (_pParent->IsFolderSiteCostingEnabled()))) { _pReferralData->SetSiteCosting(); } if (IsFolderOutOfDomain()) { _pReferralData->SetOutOfDomain(); }
_pReferralData->Timeout = _Timeout;
pRefData = _pReferralData;
} else { _pReferralData->ReleaseReference(); _pReferralData = NULL; } } else { Status = ERROR_NOT_ENOUGH_MEMORY; }
//
// We no longer need the lock. We have allocate the referral
// data and marked the state accordingly. No other thread can
// interfere with our load now.
//
ReleaseLock();
//
// Now we load the referral data, and save the status of the
// load in both our load status as well as the load status
// in the referral data.
// If the load was successful, we add this to the loaded list
// of referral data that can be scavenged later. We set the load
// state to loaded, and signal the event so that all waiting
// threads can now be woken up.
//
if ( Status == ERROR_SUCCESS ) { DFS_TRACE_NORM(REFERRAL_SERVER, " Load called on link %wZ (%wZ)\n", GetFolderMetadataName(), GetFolderLogicalName());
//
// We depend on _pReferralData being non-null although
// we aren't holding the folder lock still.
//
_pReferralData->FolderOffLine = IsFolderOffline(); Status = LoadReferralData( _pReferralData);
if(IsFolderOffline()) { DFS_TRACE_NORM(REFERRAL_SERVER, "Link %wZ (%wZ) is OFFLINE\n", GetFolderMetadataName(), GetFolderLogicalName()); }
_LoadStatus = Status; _pReferralData->LoadStatus = Status;
if ( Status == ERROR_SUCCESS ) { //
// Acquire a reference on the new referral data, since we
// have to return a referenced referral data to the caller.
// Get the reference here before we add it to the loaded list,
// otherwise we could end up with the referral data
// being freed up.
//
pRefData->AcquireReference();
_LoadState = DfsFolderLoaded; _RetryFailedLoadTimeout = 0; if (AddToLoadedList == TRUE) { DfsAddReferralDataToLoadedList( _pReferralData ); } *ppReferralData = pRefData; pRefData->Signal(); } else { DFSSTATUS RemoveStatus;
_LoadState = DfsFolderLoadFailed;
// We'll try reloading this at some other time
_RetryFailedLoadTimeout = GetTickCount(); pRefData->Signal(); RemoveStatus = RemoveReferralData(pRefData, NULL);
DFS_TRACE_ERROR_HIGH(_LoadStatus, REFERRAL_SERVER, "Replica load failed for %wZ (%wZ), load status %x\n", GetFolderMetadataName(), GetFolderLogicalName(), _LoadStatus); } }
break;
case DfsFolderLoadInProgress:
//
// The load is in progress. We acquire a reference on the
// referral data being loaded and wait for the event in the
// referral data to be signalled. The return status of the wait
// indicates if we can return the referral data or we fail
// this request with an error.
//
DFS_TRACE_NORM(REFERRAL_SERVER, " Get Referral Data: load in progress\n"); ASSERT(_pReferralData != NULL); pRefData = _pReferralData; pRefData->AcquireReference();
ReleaseLock();
DFSLOG("Thread: Waiting fod ..r referral load\n");
Status = pRefData->Wait();
if ( Status == ERROR_SUCCESS ) { *ppReferralData = pRefData; } else { pRefData->ReleaseReference(); } DFS_TRACE_NORM(REFERRAL_SERVER, " Get Referral Data: load in progress done\n"); break;
case DfsFolderLoadFailed: //
// The Load failed. REturn error. We've already setup a time
// after which we'll reattempt the load.
//
ASSERT(_pReferralData == NULL); Status = _LoadStatus; ReleaseLock(); *ppReferralData = NULL; break;
default: //
// We should never get here. Its an invalid state.
//
ASSERT(TRUE); Status = ERROR_INVALID_STATE; ReleaseLock();
break; }
ASSERT((Status != ERROR_SUCCESS) || (*ppReferralData != NULL));
return Status; }
//+-------------------------------------------------------------------------
//
// Function: RemoveReferralData - remove the referral data from folder
//
// Arguments: NONE
//
// Returns: Status
// ERROR_SUCCESS if we could remove the referral data
// error status otherwise.
//
//
// Description: This routine removes the cached reference to the loaded
// referral data in the folder, and releases its reference
// on it.
// This causes all future GetREferralDAta to be loaded
// back from the store.
//
//--------------------------------------------------------------------------
DFSSTATUS DfsFolder::RemoveReferralData( DfsFolderReferralData *pRemoveReferralData, PBOOLEAN pRemoved ) { DFSSTATUS Status = ERROR_SUCCESS; DfsFolderReferralData *pRefData = NULL; //
// Get tnhe exclusive lock on the folder
//
if (pRemoved != NULL) { *pRemoved = FALSE; }
AcquireWriteLock(); if ( (pRemoveReferralData == NULL) || (pRemoveReferralData == _pReferralData) ) { //
// BUG 773319 : There's a race between the worker thread
// trying to sync and purge referral data in UpdateLinkFolder
// and referrals trying to load referral data in GetReferralData.
// GetReferralData drops the folder-lock with the understanding that
// others will honor the DfsFolderLoadInProgress flag.
//
if (_LoadState == DfsFolderLoadInProgress) { pRefData = _pReferralData; ASSERT(pRefData != NULL); pRefData->AcquireReference(); ReleaseLock();
DFS_TRACE_NORM(REFERRAL_SERVER, "ReplicaData load in progress for Link %wZ (%wZ). Waiting\n", GetFolderMetadataName(), GetFolderLogicalName()); //
// We wait here because we don't want to lose our race to the thread
// that did LoadReferralData. The sync-thread would've already created
// folders based on what was in cache at one point. What's about to
// get loaded may be inconsistent with respect to the linkfolder data.
//
(VOID)pRefData->Wait(); // Release the reference we took above.
pRefData->ReleaseReference(); pRefData = NULL; AcquireWriteLock(); }; //
// There are only two states that LoadInProgress transition to: Loaded and LoadFailed.
// If we are in any other state then we are done because some other thread must have
// beaten us to it.
//
// Also, don't change the LoadFailed status here. There's a separate timer ticking on
// on that transition.
//
if (_LoadState == DfsFolderLoaded || _LoadState == DfsFolderLoadFailed) { // Don't take another reference here. We want to deref its original reference
// to get rid of it (see below).
pRefData = _pReferralData; _pReferralData = NULL; _LoadState = (_LoadState == DfsFolderLoaded) ? DfsFolderNotLoaded : DfsFolderLoadFailed; } }
ReleaseLock(); //
// Release reference on the referral data. This is the reference
// we had taken when we had cached the referral data in this folder.
//
if (pRefData != NULL) { if (pRefData->GetOwningFolder() != NULL) { DFS_TRACE_LOW(REFERRAL, "Purged cached referral for Logical Name %ws, Link %ws\n", pRefData->GetOwningFolder()->GetFolderLogicalNameString(), pRefData->GetOwningFolder()->GetFolderMetadataNameString()); } pRefData->ReleaseReference(); if (pRemoved != NULL) { *pRemoved = TRUE; } }
return Status; }
|