1262 lines
31 KiB
C++
1262 lines
31 KiB
C++
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name :
|
||
|
||
metacach.cxx
|
||
|
||
Abstract:
|
||
This module contains the tsunami caching routines for metadata.
|
||
|
||
Author:
|
||
Henry Sanders ( henrysa ) 15-Oct-1996
|
||
|
||
--*/
|
||
|
||
#include "TsunamiP.Hxx"
|
||
#pragma hdrstop
|
||
#include <dbgutil.h>
|
||
#include <issched.hxx>
|
||
#include <metacach.hxx>
|
||
|
||
extern TCHAR * FlipSlashes( TCHAR * pszPath );
|
||
|
||
//
|
||
// The number of buckets in our hash table.
|
||
//
|
||
|
||
#define METACACHE_TABLE_SIZE 32
|
||
#define METACACHE_ENTRY_SIGN ((DWORD)'ECEM')
|
||
#define METACACHE_ENTRY_FREE ((DWORD)'ECEf')
|
||
|
||
//
|
||
// Structure of a metacache table entry.
|
||
//
|
||
|
||
typedef struct _METACACHE_ENTRY {
|
||
|
||
DWORD Signature;
|
||
struct _METACACHE_ENTRY *pNext;
|
||
DWORD dwDataSetNumber;
|
||
DWORD dwServiceID;
|
||
PVOID pMetaData;
|
||
DWORD dwRefCount;
|
||
PMDFREERTN pFreeRoutine;
|
||
BOOL bValid;
|
||
|
||
} METACACHE_ENTRY, *PMETACACHE_ENTRY;
|
||
|
||
//
|
||
// Structure of a hash table bucket.
|
||
//
|
||
|
||
typedef struct _METACACHE_BUCKET {
|
||
|
||
PMETACACHE_ENTRY pEntry;
|
||
CRITICAL_SECTION csCritSec;
|
||
|
||
} METACACHE_BUCKET;
|
||
|
||
METACACHE_BUCKET MetaCacheTable[METACACHE_TABLE_SIZE];
|
||
|
||
DWORD MetaCacheTimerCookie = 0;
|
||
|
||
|
||
/************************************************************
|
||
* Functions
|
||
************************************************************/
|
||
dllexp
|
||
PVOID
|
||
TsFindMetaData(
|
||
|
||
IN DWORD dwDataSetNumber,
|
||
IN DWORD dwServiceID
|
||
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function takes a data set number and service ID, and tries to find
|
||
a formatted chunk of metadata in the cache. If it does so, it returns a
|
||
pointer to it, otherwise it returns NULL.
|
||
|
||
Arguments
|
||
|
||
dwDataSetNumber - The data set number to be found.
|
||
dwServiceID - ID of calling service.
|
||
--*/
|
||
{
|
||
DWORD dwIndex;
|
||
PMETACACHE_ENTRY pCurrentEntry;
|
||
|
||
dwIndex = dwDataSetNumber % METACACHE_TABLE_SIZE;
|
||
|
||
//
|
||
// This needes to be protected, we use a critical section per bucket.
|
||
//
|
||
EnterCriticalSection(&MetaCacheTable[dwIndex].csCritSec);
|
||
|
||
pCurrentEntry = MetaCacheTable[dwIndex].pEntry;
|
||
|
||
// Walk the chain on the bucket. If we find a match, return it.
|
||
//
|
||
while (pCurrentEntry != NULL )
|
||
{
|
||
PCOMMON_METADATA pCMD;
|
||
|
||
pCMD = (PCOMMON_METADATA)pCurrentEntry->pMetaData;
|
||
|
||
pCMD->CheckSignature();
|
||
ASSERT(pCMD->QueryCacheInfo() == pCurrentEntry);
|
||
|
||
|
||
if (pCurrentEntry->dwDataSetNumber == dwDataSetNumber &&
|
||
pCurrentEntry->dwServiceID == dwServiceID &&
|
||
pCurrentEntry->bValid)
|
||
{
|
||
|
||
ASSERT( pCurrentEntry->Signature == METACACHE_ENTRY_SIGN );
|
||
|
||
// Found a match. Increment the refcount and return a pointer
|
||
// to the metadata.
|
||
InterlockedIncrement((LONG *)&pCurrentEntry->dwRefCount);
|
||
LeaveCriticalSection(&MetaCacheTable[dwIndex].csCritSec);
|
||
|
||
return pCurrentEntry->pMetaData;
|
||
}
|
||
|
||
// Otherwise try the next one.
|
||
pCurrentEntry = pCurrentEntry->pNext;
|
||
}
|
||
|
||
|
||
// Didn't find a match, so we'll return NULL.
|
||
|
||
LeaveCriticalSection(&MetaCacheTable[dwIndex].csCritSec);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
dllexp
|
||
PVOID
|
||
TsAddMetaData(
|
||
|
||
IN PCOMMON_METADATA pMetaData,
|
||
IN PMDFREERTN pFreeRoutine,
|
||
IN DWORD dwDataSetNumber,
|
||
IN DWORD dwServiceID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Add a chunk of formatted metadata to our cache.
|
||
|
||
Arguments
|
||
|
||
pMetaData - MetaData to be added.
|
||
dwDataSetNumber - The data set number to be found.
|
||
dwServiceID - ID of calling service.
|
||
|
||
Returns
|
||
Pointer to metacache 'handle' to be used when freeing information.
|
||
|
||
--*/
|
||
{
|
||
PMETACACHE_ENTRY pNewEntry;
|
||
DWORD dwIndex;
|
||
|
||
pMetaData->CheckSignature();
|
||
|
||
dwIndex = dwDataSetNumber % METACACHE_TABLE_SIZE;
|
||
|
||
|
||
pNewEntry = (PMETACACHE_ENTRY)ALLOC(sizeof(METACACHE_ENTRY));
|
||
|
||
if (pNewEntry == NULL)
|
||
{
|
||
// Couldn't add the entry. No big deal, just return.
|
||
return NULL;
|
||
}
|
||
|
||
pNewEntry->Signature = METACACHE_ENTRY_SIGN;
|
||
pNewEntry->dwDataSetNumber = dwDataSetNumber;
|
||
pNewEntry->dwServiceID = dwServiceID;
|
||
pNewEntry->pMetaData = pMetaData;
|
||
pNewEntry->pFreeRoutine = pFreeRoutine;
|
||
pNewEntry->dwRefCount = 1;
|
||
pNewEntry->bValid = TRUE;
|
||
|
||
pMetaData->SetCacheInfo(pNewEntry);
|
||
|
||
EnterCriticalSection(&MetaCacheTable[dwIndex].csCritSec);
|
||
|
||
pNewEntry->pNext = MetaCacheTable[dwIndex].pEntry;
|
||
|
||
MetaCacheTable[dwIndex].pEntry = pNewEntry;
|
||
|
||
LeaveCriticalSection(&MetaCacheTable[dwIndex].csCritSec);
|
||
|
||
return pNewEntry;
|
||
|
||
}
|
||
|
||
dllexp
|
||
VOID
|
||
TsFreeMetaData(
|
||
|
||
IN PVOID pCacheEntry
|
||
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free a chunk of formatted metadata to the cache. What we really do here
|
||
is decrement the ref count. If it goes to 0 and the cache element is
|
||
marked deleted, we'll free it here.
|
||
|
||
Arguments
|
||
|
||
pMetaData - MetaData to be freed.
|
||
--*/
|
||
{
|
||
PMETACACHE_ENTRY pEntry = (PMETACACHE_ENTRY)pCacheEntry;
|
||
PCOMMON_METADATA pCMD;
|
||
|
||
|
||
ASSERT( pEntry->Signature == METACACHE_ENTRY_SIGN );
|
||
|
||
pCMD = (PCOMMON_METADATA)pEntry->pMetaData;
|
||
|
||
pCMD->CheckSignature();
|
||
|
||
ASSERT(pCMD->QueryCacheInfo() == pEntry);
|
||
|
||
InterlockedDecrement((LONG *)&pEntry->dwRefCount);
|
||
|
||
}
|
||
|
||
dllexp
|
||
VOID
|
||
TsAddRefMetaData(
|
||
|
||
IN PVOID pCacheEntry
|
||
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Increment reference count to chunk of formatted metadata
|
||
|
||
Arguments
|
||
|
||
pMetaData - MetaData to be AddRef'ed
|
||
--*/
|
||
{
|
||
PMETACACHE_ENTRY pEntry = (PMETACACHE_ENTRY)pCacheEntry;
|
||
|
||
ASSERT( pEntry->Signature == METACACHE_ENTRY_SIGN );
|
||
|
||
InterlockedIncrement((LONG *)&pEntry->dwRefCount);
|
||
}
|
||
|
||
dllexp
|
||
VOID
|
||
TsFlushMetaCache(
|
||
DWORD dwService,
|
||
BOOL bTerminating
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called when we need to flush all of our cached metainformation. We walk
|
||
the table, and for each entry we check to see if it's in use. If it's not
|
||
we'll free it, otherwise we'll mark it as deleted.
|
||
|
||
If the passed in dwService ID is non-zero, then we'll only
|
||
flush those entries that match the service. Also, if we're terminating,
|
||
we'll do some additional checking, and also cancle any callbacks if we need
|
||
to.
|
||
|
||
Arguments
|
||
|
||
dwService - Service ID of entries to be flushed, 0 for all
|
||
services.
|
||
bTerminating - TRUE if the caller is terminating.
|
||
|
||
|
||
--*/
|
||
{
|
||
UINT i;
|
||
PMETACACHE_ENTRY pEntry;
|
||
PMETACACHE_ENTRY pTrailer;
|
||
PCOMMON_METADATA pCMD;
|
||
|
||
for (i = 0; i < METACACHE_TABLE_SIZE; i++)
|
||
{
|
||
EnterCriticalSection(&MetaCacheTable[i].csCritSec);
|
||
|
||
pTrailer = CONTAINING_RECORD(&MetaCacheTable[i].pEntry,
|
||
METACACHE_ENTRY, pNext);
|
||
|
||
// Walk the chain on the bucket. For every entry, if it's not in
|
||
// use, free it.
|
||
//
|
||
while (pTrailer->pNext != NULL )
|
||
{
|
||
pEntry = pTrailer->pNext;
|
||
|
||
ASSERT( pEntry->Signature == METACACHE_ENTRY_SIGN );
|
||
|
||
pCMD = (PCOMMON_METADATA)pEntry->pMetaData;
|
||
|
||
pCMD->CheckSignature();
|
||
ASSERT(pCMD->QueryCacheInfo() == pEntry);
|
||
|
||
if (dwService == 0 || dwService == pEntry->dwServiceID)
|
||
{
|
||
if (pEntry->dwRefCount == 0)
|
||
{
|
||
// This entry is not in use.
|
||
|
||
// If whoever added it gave us a free routine, call it now.
|
||
if (pEntry->pFreeRoutine != NULL)
|
||
{
|
||
(*(pEntry->pFreeRoutine))(pEntry->pMetaData);
|
||
}
|
||
|
||
// Look at the next one.
|
||
pTrailer->pNext = pEntry->pNext;
|
||
|
||
pEntry->Signature = METACACHE_ENTRY_FREE;
|
||
FREE(pEntry);
|
||
}
|
||
else
|
||
{
|
||
// In a debug build we'll assert here if we're terminating,
|
||
// since that shouldn't happen. In a free build we won't
|
||
// assert for that but we will NULL out the free routine to
|
||
// keep it from getting called, since presumably the owner is
|
||
// going away.
|
||
|
||
if (bTerminating)
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"\n=========================================\n"
|
||
"Leftover item in metacache - %8x, bValid = %s\n"
|
||
"\t dwServiceID = %8d pMetaData = %8x\n"
|
||
"\t dwRefCount = %8d pFreeRoutine = %8x\n"
|
||
,
|
||
pEntry,
|
||
(pEntry->bValid ? "TRUE" : "FALSE"),
|
||
pEntry->dwServiceID,
|
||
pEntry->pMetaData,
|
||
pEntry->dwRefCount,
|
||
pEntry->pFreeRoutine ));
|
||
|
||
pEntry->pFreeRoutine = NULL;
|
||
}
|
||
|
||
|
||
pEntry->bValid = FALSE;
|
||
pTrailer = pEntry;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pTrailer = pEntry;
|
||
}
|
||
}
|
||
|
||
|
||
LeaveCriticalSection(&MetaCacheTable[i].csCritSec);
|
||
}
|
||
}
|
||
|
||
dllexp
|
||
VOID
|
||
TsReferenceMetaData(
|
||
IN PVOID pEntry
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called when we need to reference a metadata cache entry. The caller
|
||
must have already referenced it once.
|
||
|
||
Arguments
|
||
|
||
pEntry - Entry to be referenced.
|
||
|
||
|
||
--*/
|
||
{
|
||
PMETACACHE_ENTRY pCacheEntry = (PMETACACHE_ENTRY)pEntry;
|
||
PCOMMON_METADATA pCMD;
|
||
|
||
|
||
ASSERT( pCacheEntry->Signature == METACACHE_ENTRY_SIGN );
|
||
|
||
pCMD = (PCOMMON_METADATA)pCacheEntry->pMetaData;
|
||
|
||
pCMD->CheckSignature();
|
||
|
||
ASSERT(pCMD->QueryCacheInfo() == pCacheEntry);
|
||
|
||
InterlockedIncrement((LONG *)&pCacheEntry->dwRefCount);
|
||
|
||
}
|
||
|
||
VOID
|
||
MetaCacheScavenger(
|
||
PVOID pContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called periodically to time out metacache information. We scan the table;
|
||
if we find an object that's not in use we free it.
|
||
|
||
Arguments
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
{
|
||
UINT i;
|
||
PMETACACHE_ENTRY pEntry;
|
||
PMETACACHE_ENTRY pTrailer;
|
||
PCOMMON_METADATA pCMD;
|
||
|
||
for (i = 0; i < METACACHE_TABLE_SIZE; i++)
|
||
{
|
||
if (MetaCacheTable[i].pEntry == NULL)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
EnterCriticalSection(&MetaCacheTable[i].csCritSec);
|
||
|
||
pTrailer = CONTAINING_RECORD(&MetaCacheTable[i].pEntry,
|
||
METACACHE_ENTRY, pNext);
|
||
|
||
|
||
// Walk the chain on the bucket. For every entry, if it's not in
|
||
// use, free it.
|
||
//
|
||
while (pTrailer->pNext != NULL )
|
||
{
|
||
pEntry = pTrailer->pNext;
|
||
|
||
ASSERT( pEntry->Signature == METACACHE_ENTRY_SIGN );
|
||
pCMD = (PCOMMON_METADATA)pEntry->pMetaData;
|
||
|
||
pCMD->CheckSignature();
|
||
ASSERT(pCMD->QueryCacheInfo() == pEntry);
|
||
|
||
|
||
if (pEntry->dwRefCount == 0)
|
||
{
|
||
|
||
// This entry is not in use.
|
||
|
||
// If whoever added it gave us a free routine, call it now.
|
||
if (pEntry->pFreeRoutine != NULL)
|
||
{
|
||
(*(pEntry->pFreeRoutine))(pEntry->pMetaData);
|
||
}
|
||
|
||
// Free the entry and look at the next one.
|
||
pTrailer->pNext = pEntry->pNext;
|
||
|
||
pEntry->Signature = METACACHE_ENTRY_FREE;
|
||
|
||
FREE(pEntry);
|
||
}
|
||
else
|
||
{
|
||
pTrailer = pEntry;
|
||
}
|
||
}
|
||
|
||
LeaveCriticalSection(&MetaCacheTable[i].csCritSec);
|
||
}
|
||
}
|
||
|
||
|
||
dllexp
|
||
VOID
|
||
_TsValidateMetaCache(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
|
||
--*/
|
||
{
|
||
UINT i;
|
||
PMETACACHE_ENTRY pEntry;
|
||
PCOMMON_METADATA pCMD;
|
||
|
||
for (i = 0; i < METACACHE_TABLE_SIZE; i++)
|
||
{
|
||
if (MetaCacheTable[i].pEntry == NULL)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
EnterCriticalSection(&MetaCacheTable[i].csCritSec);
|
||
|
||
pEntry = MetaCacheTable[i].pEntry;
|
||
|
||
while (pEntry != NULL )
|
||
{
|
||
|
||
ASSERT( pEntry->Signature == METACACHE_ENTRY_SIGN );
|
||
pCMD = (PCOMMON_METADATA)pEntry->pMetaData;
|
||
|
||
pCMD->CheckSignature();
|
||
ASSERT(pCMD->QueryCacheInfo() == pEntry);
|
||
|
||
pEntry = pEntry->pNext;
|
||
}
|
||
|
||
LeaveCriticalSection(&MetaCacheTable[i].csCritSec);
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
MetaCache_Initialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize our metacache code.
|
||
|
||
Arguments
|
||
|
||
Nothing.
|
||
|
||
--*/
|
||
{
|
||
UINT i;
|
||
|
||
for (i = 0; i < METACACHE_TABLE_SIZE; i++)
|
||
{
|
||
InitializeCriticalSection(&MetaCacheTable[i].csCritSec);
|
||
MetaCacheTable[i].pEntry = NULL;
|
||
|
||
SET_CRITICAL_SECTION_SPIN_COUNT( &MetaCacheTable[i].csCritSec,
|
||
IIS_DEFAULT_CS_SPIN_COUNT);
|
||
}
|
||
|
||
MetaCacheTimerCookie = ScheduleWorkItem(
|
||
(PFN_SCHED_CALLBACK) MetaCacheScavenger,
|
||
NULL,
|
||
60000, // BUGBUG
|
||
TRUE ); // Periodic
|
||
|
||
if (!MetaCacheTimerCookie)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
MetaCache_Terminate(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Terminate our metacache code.
|
||
|
||
Arguments
|
||
|
||
Nothing.
|
||
|
||
--*/
|
||
{
|
||
if (MetaCacheTimerCookie != 0)
|
||
{
|
||
RemoveWorkItem(MetaCacheTimerCookie);
|
||
MetaCacheTimerCookie = 0;
|
||
}
|
||
|
||
TsFlushMetaCache(0, TRUE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
|
||
COMMON_METADATA::COMMON_METADATA(VOID)
|
||
: m_IpDnsAccessCheckSize( 0 ),
|
||
m_IpDnsAccessCheckPtr ( NULL ),
|
||
m_IpDnsAccessCheckTag ( 0 ),
|
||
m_fDontLog ( FALSE ),
|
||
m_dwAccessPerm ( MD_ACCESS_READ ),
|
||
m_dwSslAccessPerm ( 0 ),
|
||
m_pAcl ( NULL ),
|
||
m_dwAclTag ( 0 ),
|
||
m_dwVrLevel ( 0 ),
|
||
m_dwVrLen ( 0 ),
|
||
m_hVrToken ( NULL ),
|
||
m_fVrPassThrough ( FALSE ),
|
||
m_dwVrError ( 0 ),
|
||
m_Signature ( CMD_SIG )
|
||
{
|
||
//
|
||
// Hmmm, since most of these values aren't getting initialized, if
|
||
// somebody went and deleted all the metadata items from the tree, then
|
||
// bad things could happen. We should initialize with defaults things
|
||
// that might mess us
|
||
//
|
||
|
||
} // COMMON_METADATA::COMMON_METADATA()
|
||
|
||
|
||
COMMON_METADATA::~COMMON_METADATA(VOID)
|
||
{
|
||
CheckSignature();
|
||
|
||
if ( m_IpDnsAccessCheckTag )
|
||
{
|
||
FreeMdTag( m_IpDnsAccessCheckTag );
|
||
m_IpDnsAccessCheckTag = 0;
|
||
}
|
||
if ( m_dwAclTag )
|
||
{
|
||
FreeMdTag( m_dwAclTag );
|
||
m_dwAclTag = 0;
|
||
}
|
||
|
||
if ( m_hVrToken )
|
||
{
|
||
TsDeleteUserToken( m_hVrToken );
|
||
m_hVrToken = NULL;
|
||
}
|
||
|
||
} // COMMON_METADATA::~COMMON_METADATA()
|
||
|
||
|
||
VOID
|
||
COMMON_METADATA::FreeMdTag(
|
||
DWORD dwTag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free a metadata object accessed by reference
|
||
|
||
Arguments:
|
||
|
||
dwTag - tag of metadata object reference
|
||
|
||
Returns:
|
||
|
||
Nothing
|
||
|
||
--*/
|
||
{
|
||
MB mb( (IMDCOM*) m_pInstance->m_Service->QueryMDObject() );
|
||
|
||
CheckSignature();
|
||
mb.ReleaseReferenceData( dwTag );
|
||
}
|
||
|
||
|
||
//
|
||
// Private constants.
|
||
//
|
||
|
||
#define DEFAULT_MD_RECORDS 40
|
||
#define DEFAULT_RECORD_SIZE 50
|
||
|
||
# define DEF_MD_REC_SIZE ((1 + DEFAULT_MD_RECORDS) * \
|
||
(sizeof(METADATA_RECORD) + DEFAULT_RECORD_SIZE))
|
||
|
||
#define RMD_ASSERT(x) if (!(x)) {DBG_ASSERT(FALSE); return FALSE; }
|
||
|
||
|
||
BOOL
|
||
COMMON_METADATA::ReadMetaData(
|
||
PIIS_SERVER_INSTANCE pInstance,
|
||
MB * pmb,
|
||
LPSTR pszURL,
|
||
PMETADATA_ERROR_INFO pMDError
|
||
)
|
||
{
|
||
PMETADATA_RECORD pMDRecord;
|
||
DWORD dwNumMDRecords;
|
||
BYTE tmpBuffer[ DEF_MD_REC_SIZE];
|
||
BUFFER TempBuff( tmpBuffer, DEF_MD_REC_SIZE);
|
||
DWORD i;
|
||
DWORD dwDataSetNumber;
|
||
INT ch;
|
||
LPSTR pszInVr;
|
||
LPSTR pszMinInVr;
|
||
DWORD dwNeed;
|
||
DWORD dwL;
|
||
DWORD dwVRLen;
|
||
LPSTR pszVrUserName;
|
||
LPSTR pszVrPassword;
|
||
BYTE tmpPrivateBuffer[ 20 ];
|
||
BUFFER PrivateBuffer( tmpPrivateBuffer, 20 );
|
||
DWORD dwPrivateBufferUsed;
|
||
|
||
|
||
CheckSignature();
|
||
TsValidateMetaCache();
|
||
|
||
m_pInstance = pInstance;
|
||
|
||
DBG_ASSERT( TempBuff.QuerySize() >=
|
||
(DEFAULT_MD_RECORDS *
|
||
(sizeof(METADATA_RECORD) + DEFAULT_RECORD_SIZE))
|
||
);
|
||
|
||
if ( !pmb->Open( pInstance->QueryMDVRPath() ))
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
if ( !pmb->GetAll( pszURL,
|
||
METADATA_INHERIT | METADATA_PARTIAL_PATH | METADATA_REFERENCE,
|
||
IIS_MD_UT_FILE,
|
||
&TempBuff,
|
||
&dwNumMDRecords,
|
||
&dwDataSetNumber ))
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
pMDRecord = (PMETADATA_RECORD)TempBuff.QueryPtr();
|
||
i = 0;
|
||
|
||
//
|
||
// Check from where we got VR_PATH
|
||
//
|
||
|
||
pszMinInVr = pszURL ;
|
||
if ( *pszURL )
|
||
{
|
||
for ( pszInVr = pszMinInVr + strlen(pszMinInVr) ;; )
|
||
{
|
||
ch = *pszInVr;
|
||
*pszInVr = '\0';
|
||
dwNeed = 0;
|
||
if ( !pmb->GetString( pszURL, MD_VR_PATH, IIS_MD_UT_FILE, NULL, &dwNeed, 0 ) &&
|
||
GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
||
{
|
||
*pszInVr = ch;
|
||
// VR_PATH was defined at this level !
|
||
|
||
break;
|
||
}
|
||
*pszInVr = ch;
|
||
|
||
if ( ch )
|
||
{
|
||
if ( pszInVr > pszMinInVr )
|
||
{
|
||
pszInVr = CharPrev( pszMinInVr, pszInVr );
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// VR_PATH was defined above Instance vroot
|
||
// or not at all. If defined above, then the reference
|
||
// path is empty, so we can claim we found it.
|
||
// if not defined, then this will be catch later.
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
// scan for previous delimiter
|
||
|
||
while ( *pszInVr != '/' && *pszInVr != '\\' )
|
||
{
|
||
if ( pszInVr > pszMinInVr )
|
||
{
|
||
pszInVr = CharPrev( pszMinInVr, pszInVr );
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// VR_PATH was defined above Instance vroot
|
||
// or not at all. If defined above, then the reference
|
||
// path is empty, so we can claim we found it.
|
||
// if not defined, then this will be catch later.
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
dwVRLen = pszInVr - pszMinInVr;
|
||
}
|
||
else
|
||
{
|
||
dwVRLen = 0;
|
||
pszInVr = pszMinInVr;
|
||
}
|
||
|
||
// Close this now to minimize lock contention.
|
||
DBG_REQUIRE(pmb->Close());
|
||
|
||
for ( dwL = 0 ; pszMinInVr < pszInVr - 1 ; pszMinInVr = CharNext(pszMinInVr) )
|
||
{
|
||
if ( *pszMinInVr == '/' || *pszMinInVr == '\\' )
|
||
{
|
||
++dwL;
|
||
}
|
||
}
|
||
|
||
// Now walk through the array of returned metadata objects and format
|
||
// each one into our predigested form.
|
||
|
||
SetVrLevelAndLen( dwL, dwVRLen );
|
||
pszVrPassword = NULL;
|
||
pszVrUserName = NULL;
|
||
dwPrivateBufferUsed = 0;
|
||
pMDError->IsValid = FALSE;
|
||
|
||
for ( ; i < dwNumMDRecords; i++, pMDRecord++ ) {
|
||
|
||
PVOID pDataPointer;
|
||
CHAR *pszMimePtr;
|
||
CHAR *pszTemp;
|
||
DWORD dwTemp;
|
||
|
||
|
||
pDataPointer = (PVOID) ((PCHAR)TempBuff.QueryPtr() +
|
||
(UINT)pMDRecord->pbMDData);
|
||
|
||
switch ( pMDRecord->dwMDIdentifier ) {
|
||
|
||
case MD_IP_SEC:
|
||
DBG_ASSERT( pMDRecord->dwMDDataTag );
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == BINARY_METADATA &&
|
||
pMDRecord->dwMDAttributes & METADATA_REFERENCE );
|
||
if ( pMDRecord->dwMDDataTag )
|
||
{
|
||
if ( !SetIpDnsAccessCheck( pMDRecord->pbMDData,
|
||
pMDRecord->dwMDDataLen,
|
||
pMDRecord->dwMDDataTag ) )
|
||
{
|
||
goto FreeRefs;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case MD_ACCESS_PERM:
|
||
DBG_ASSERT( pMDRecord->dwMDDataTag == NULL );
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
||
SetAccessPerms( *((DWORD *) pDataPointer) );
|
||
break;
|
||
|
||
case MD_SSL_ACCESS_PERM:
|
||
DBG_ASSERT( pMDRecord->dwMDDataTag == NULL );
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
||
SetSslAccessPerms( *((DWORD *) pDataPointer) );
|
||
break;
|
||
|
||
case MD_DONT_LOG:
|
||
DBG_ASSERT( pMDRecord->dwMDDataTag == NULL );
|
||
DBG_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
||
SetDontLogFlag( *((DWORD *) pDataPointer ));
|
||
break;
|
||
|
||
case MD_VR_PATH:
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == STRING_METADATA );
|
||
if (!QueryVrPath()->Copy((const CHAR *)pDataPointer))
|
||
{
|
||
goto FreeRefs;
|
||
}
|
||
break;
|
||
|
||
case MD_APP_ROOT:
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == STRING_METADATA );
|
||
if (!QueryAppPath()->Copy((const CHAR *)pDataPointer))
|
||
{
|
||
goto FreeRefs;
|
||
}
|
||
break;
|
||
|
||
case MD_VR_USERNAME:
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == STRING_METADATA );
|
||
pszVrUserName = (LPSTR)pDataPointer;
|
||
break;
|
||
|
||
case MD_VR_PASSWORD:
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == STRING_METADATA );
|
||
pszVrPassword = (LPSTR)pDataPointer;
|
||
break;
|
||
|
||
case MD_VR_PASSTHROUGH:
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
||
SetVrPassThrough( !!*((DWORD *) pDataPointer) );
|
||
break;
|
||
|
||
case MD_VR_ACL:
|
||
DBG_ASSERT( pMDRecord->dwMDDataTag );
|
||
RMD_ASSERT( pMDRecord->dwMDDataType == BINARY_METADATA );
|
||
if ( pMDRecord->dwMDDataTag )
|
||
{
|
||
SetAcl( pMDRecord->pbMDData,
|
||
pMDRecord->dwMDDataLen,
|
||
pMDRecord->dwMDDataTag );
|
||
}
|
||
break;
|
||
|
||
default:
|
||
if ( !HandlePrivateProperty( pszURL, pInstance, pMDRecord, pDataPointer, &PrivateBuffer, &dwPrivateBufferUsed, pMDError ) )
|
||
{
|
||
goto FreeRefs;
|
||
}
|
||
CheckSignature();
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!FinishPrivateProperties(&PrivateBuffer, dwPrivateBufferUsed, TRUE))
|
||
{
|
||
goto FreeRefs;
|
||
}
|
||
|
||
if ( QueryVrPath()->IsEmpty() )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[ReadMetaData] Virtual Dir Path mapping not found\n" ));
|
||
SetLastError( ERROR_FILE_NOT_FOUND );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If this is an UNC share, logon using associated credentials
|
||
// keep a reference to this access token in the cache
|
||
//
|
||
|
||
if ( QueryVrPath()->QueryStr()[0] == '\\' &&
|
||
QueryVrPath()->QueryStr()[1] == '\\' )
|
||
{
|
||
if ( pszVrUserName != NULL && pszVrPassword != NULL &&
|
||
pszVrUserName[0] )
|
||
{
|
||
|
||
if ( !SetVrUserNameAndPassword( pInstance, pszVrUserName, pszVrPassword ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
CheckSignature();
|
||
TsValidateMetaCache();
|
||
return TRUE;
|
||
|
||
FreeRefs:
|
||
|
||
FinishPrivateProperties(&PrivateBuffer, dwPrivateBufferUsed, FALSE);
|
||
CheckSignature();
|
||
TsValidateMetaCache();
|
||
|
||
for ( ; i < dwNumMDRecords; i++, pMDRecord++ )
|
||
{
|
||
if ( pMDRecord->dwMDDataTag )
|
||
{
|
||
pmb->ReleaseReferenceData( pMDRecord->dwMDDataTag );
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
COMMON_METADATA::SetVrUserNameAndPassword(
|
||
PIIS_SERVER_INSTANCE pInstance,
|
||
LPSTR pszUserName,
|
||
LPSTR pszPassword
|
||
)
|
||
/*++
|
||
Description:
|
||
|
||
Set the account used to access the virtual root
|
||
associated with this metadata
|
||
|
||
Arguments:
|
||
pInstance - current instance
|
||
pszUserName - User name
|
||
pszPassword - password
|
||
|
||
Returns:
|
||
TRUE if success, otherwise FALSE
|
||
|
||
--*/
|
||
{
|
||
LARGE_INTEGER liPwdExpiry;
|
||
BOOL fHaveExp;
|
||
BOOL fAsGuest;
|
||
BOOL fAsAnonymous;
|
||
TCP_AUTHENT_INFO TAI;
|
||
|
||
CheckSignature();
|
||
TAI.fDontUseAnonSubAuth = TRUE;
|
||
|
||
m_hVrToken = TsLogonUser( pszUserName,
|
||
pszPassword,
|
||
&fAsGuest,
|
||
&fAsAnonymous,
|
||
pInstance,
|
||
&TAI,
|
||
NULL,
|
||
&liPwdExpiry,
|
||
&fHaveExp );
|
||
|
||
//
|
||
// If fail to logo, we remember the error and return SUCCESS
|
||
// Caller will have to check metadata after creating it to check
|
||
// the error code. Necessary because metadata needs to be set in HTTP_REQUEST
|
||
// to send back proper auth status even if virtual root init failed.
|
||
//
|
||
|
||
if ( !m_hVrToken )
|
||
{
|
||
m_dwVrError = GetLastError();
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
COMMON_METADATA::BuildApplPhysicalPath(
|
||
STR * pstrApplPhysicalPath
|
||
) const
|
||
/*++
|
||
Description:
|
||
This function builds the physical path for the ApplPath of the current
|
||
METADATA object. The ApplPath is a metbase path of the form
|
||
/LM/W3Svc/<instance>/app-root-path
|
||
This function uses the VR_PATH & portion of the APPL_PATH to
|
||
construct the appropriate physical path.
|
||
|
||
Arguments:
|
||
pstrApplPhysicalPath - pointer to STR object that will contain
|
||
the physical path on return.
|
||
|
||
Returns:
|
||
TRUE on success and FALSE if there are errors.
|
||
Use GetLastError() to get the appropriate error code.
|
||
--*/
|
||
{
|
||
|
||
LPCSTR pszInVr;
|
||
DWORD dwL;
|
||
INT ch;
|
||
|
||
CheckSignature();
|
||
DBG_ASSERT( pstrApplPhysicalPath);
|
||
|
||
|
||
//
|
||
// At the minimum copy the current Virtual path
|
||
// (1A) NYI: What happens with the default instance for which
|
||
// the metabase path is: /LM/W3Svc ??
|
||
//
|
||
|
||
if ( !pstrApplPhysicalPath->Copy( m_strVrPath ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// Scan for the portion of the URL that is part of the VROOT path.
|
||
// Do this by scanning forward for all the appropriate # of slashes.
|
||
// This works only if the AppRoot is contained within the Vroot
|
||
//
|
||
// AppPath is of the form: /LM/W3Svc/<instance>/ROOT/app-path
|
||
// the VRLevel counts slashes after the <instance> id
|
||
// So we scan for the appropriate # of VrLevel "/" + 4
|
||
//
|
||
// Since the App Path is internal Metabase Path, it should only have
|
||
// the '/' and not the weird '\\' :-(
|
||
//
|
||
|
||
pszInVr = m_strAppPath.QueryStr();
|
||
DBG_ASSERT( *pszInVr == '/');
|
||
dwL = QueryVrLevel() + 4;
|
||
while ( dwL-- )
|
||
{
|
||
if ( *pszInVr )
|
||
{
|
||
DBG_ASSERT( *pszInVr == '/' );
|
||
DBG_ASSERT( *pszInVr != '\\' );
|
||
|
||
LPCSTR pszNext = strchr( pszInVr + 1, '/');
|
||
if ( pszNext == NULL) {
|
||
//
|
||
// Invalid Metabase Path :( Ignore the error!
|
||
// See (1A) above
|
||
// SetLastError( ERROR_PATH_NOT_FOUND);
|
||
pszInVr = NULL;
|
||
dwL = (DWORD ) -1;
|
||
break;
|
||
}
|
||
|
||
// set the scanning pointer to next slash.
|
||
pszInVr = pszNext;
|
||
}
|
||
}
|
||
|
||
DBG_ASSERT( dwL == (DWORD)-1 );
|
||
|
||
|
||
//
|
||
// Add a path delimiter char between virtual root mount point
|
||
// & significant part of URI
|
||
//
|
||
DWORD cchAppPath = pstrApplPhysicalPath->QueryCCH();
|
||
if ( cchAppPath > 0 )
|
||
{
|
||
ch = *CharPrev(pstrApplPhysicalPath->QueryStr(), pstrApplPhysicalPath->QueryStr() + cchAppPath);
|
||
if ( ch == '/' || ch == '\\')
|
||
{
|
||
if ( ( pszInVr != NULL ) && *pszInVr )
|
||
{
|
||
DBG_ASSERT( *pszInVr == '/');
|
||
++pszInVr;
|
||
}
|
||
} else {
|
||
if ( pszInVr == NULL)
|
||
{
|
||
// happens for the special case (1A)
|
||
// Append a "\\" now.
|
||
if ( !pstrApplPhysicalPath->Append( "\\")) {
|
||
return (FALSE);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( (pszInVr != NULL) && !pstrApplPhysicalPath->Append( pszInVr ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// insure physical path last char uses standard directory delimiter
|
||
//
|
||
|
||
FlipSlashes( pstrApplPhysicalPath->QueryStr() );
|
||
|
||
return TRUE;
|
||
|
||
} // COMMON_METADATA::BuildApplPhysicalPath()
|
||
|
||
|
||
|
||
|
||
|
||
|
||
BOOL
|
||
COMMON_METADATA::BuildPhysicalPath(
|
||
LPSTR pszURL,
|
||
STR * pstrPhysicalPath
|
||
)
|
||
{
|
||
LPSTR pszInVr;
|
||
DWORD dwL;
|
||
INT ch;
|
||
|
||
|
||
CheckSignature();
|
||
TsValidateMetaCache();
|
||
|
||
//
|
||
// Build physical path from VR_PATH & portion of URI not used to define VR_PATH
|
||
//
|
||
|
||
|
||
//
|
||
// skip the URI components used to locate the virtual root
|
||
//
|
||
|
||
pszInVr = pszURL ;
|
||
dwL = QueryVrLevel();
|
||
while ( dwL-- )
|
||
{
|
||
if ( *pszInVr )
|
||
{
|
||
DBG_ASSERT( *pszInVr == '/' || *pszInVr == '\\' );
|
||
|
||
++pszInVr;
|
||
|
||
while ( (ch = *pszInVr) && ch != '/' && ch !='\\' )
|
||
{
|
||
pszInVr = CharNext( pszInVr );
|
||
}
|
||
}
|
||
}
|
||
|
||
DBG_ASSERT( dwL == (DWORD)-1 );
|
||
|
||
if ( !pstrPhysicalPath->Copy( m_strVrPath ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Add a path delimiter char between virtual root mount point & significant part of URI
|
||
//
|
||
|
||
if ( pstrPhysicalPath->QueryCCH() )
|
||
{
|
||
ch = *CharPrev(pstrPhysicalPath->QueryStr(), pstrPhysicalPath->QueryStr() +
|
||
pstrPhysicalPath->QueryCCH());
|
||
if ( (ch == '/' || ch == '\\') && *pszInVr )
|
||
{
|
||
++pszInVr;
|
||
}
|
||
}
|
||
|
||
if ( !pstrPhysicalPath->Append( pszInVr ) )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// insure physical path last char uses standard directory delimiter
|
||
//
|
||
|
||
FlipSlashes( pstrPhysicalPath->QueryStr() );
|
||
|
||
CheckSignature();
|
||
TsValidateMetaCache();
|
||
return TRUE;
|
||
}
|
||
|
||
|