You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1423 lines
38 KiB
1423 lines
38 KiB
/*++
|
|
|
|
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 <tsunami.hxx>
|
|
#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 127
|
|
#define METACACHE_ENTRY_SIGN ((DWORD)'ECEM')
|
|
#define METACACHE_ENTRY_FREE ((DWORD)'ECEf')
|
|
|
|
|
|
// time in minutes to keep entry in metachace after it was used last time
|
|
#define TIME_TO_KEEP_METACACHE_ENTRY (5)
|
|
|
|
//
|
|
// Structure of a metacache table entry.
|
|
//
|
|
|
|
typedef struct _METACACHE_ENTRY {
|
|
|
|
DWORD Signature;
|
|
struct _METACACHE_ENTRY *pNext;
|
|
DWORD dwDataSetNumber;
|
|
DWORD dwServiceID;
|
|
PVOID pMetaData;
|
|
DWORD dwRefCount;
|
|
DWORD dwLastUsedTime;
|
|
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;
|
|
DWORD g_dwMetaCacheScavengeCnt = 1;
|
|
/************************************************************
|
|
* 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);
|
|
pCurrentEntry->dwLastUsedTime = g_dwMetaCacheScavengeCnt;
|
|
|
|
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;
|
|
pNewEntry->dwLastUsedTime = g_dwMetaCacheScavengeCnt;
|
|
|
|
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 - %8p, bValid = %s\n"
|
|
"\t dwServiceID = %8d pMetaData = %8p\n"
|
|
"\t dwRefCount = %8d pFreeRoutine = %8p\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
|
|
WINAPI
|
|
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 &&
|
|
(pEntry->dwLastUsedTime + TIME_TO_KEEP_METACACHE_ENTRY < g_dwMetaCacheScavengeCnt))
|
|
{
|
|
|
|
// 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);
|
|
}
|
|
InterlockedIncrement ((long *)&g_dwMetaCacheScavengeCnt);
|
|
}
|
|
|
|
|
|
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;
|
|
UINT j;
|
|
BOOL fRet;
|
|
|
|
for (i = 0; i < METACACHE_TABLE_SIZE; i++)
|
|
{
|
|
fRet = INITIALIZE_CRITICAL_SECTION(&MetaCacheTable[i].csCritSec);
|
|
if ( !fRet )
|
|
{
|
|
//
|
|
// Cleanup all inited critical sections
|
|
//
|
|
|
|
if ( i > 0 )
|
|
{
|
|
for ( j = 0; j < i - 1; j++ )
|
|
{
|
|
DeleteCriticalSection(&MetaCacheTable[i].csCritSec);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
MetaCacheTable[i].pEntry = NULL;
|
|
}
|
|
|
|
MetaCacheTimerCookie = ScheduleWorkItem(
|
|
MetaCacheScavenger,
|
|
NULL,
|
|
60 * 1000, // 1 minute
|
|
TRUE ); // Periodic
|
|
|
|
if (!MetaCacheTimerCookie)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
MetaCache_Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminate our metacache code.
|
|
|
|
Arguments
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
{
|
|
UINT i;
|
|
|
|
if (MetaCacheTimerCookie != 0)
|
|
{
|
|
RemoveWorkItem(MetaCacheTimerCookie);
|
|
MetaCacheTimerCookie = 0;
|
|
}
|
|
|
|
TsFlushMetaCache(0, TRUE);
|
|
|
|
for (i = 0; i < METACACHE_TABLE_SIZE; i++)
|
|
{
|
|
DeleteCriticalSection(&MetaCacheTable[i].csCritSec);
|
|
}
|
|
|
|
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 ),
|
|
m_fDoCache ( TRUE )
|
|
{
|
|
//
|
|
// 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 cause us trouble us
|
|
//
|
|
|
|
} // COMMON_METADATA::COMMON_METADATA()
|
|
|
|
|
|
COMMON_METADATA::~COMMON_METADATA(VOID)
|
|
{
|
|
CheckSignature();
|
|
|
|
if ( m_IpDnsAccessCheckTag )
|
|
{
|
|
FreeMdTag( m_IpDnsAccessCheckTag );
|
|
m_IpDnsAccessCheckTag = 0;
|
|
}
|
|
else if ( m_IpDnsAccessCheckPtr != NULL )
|
|
{
|
|
LocalFree( m_IpDnsAccessCheckPtr );
|
|
m_IpDnsAccessCheckPtr = NULL;
|
|
}
|
|
|
|
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
|
|
)
|
|
{
|
|
METADATA_GETALL_INTERNAL_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 = (METADATA_GETALL_INTERNAL_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 = (CHAR)ch;
|
|
// VR_PATH was defined at this level !
|
|
|
|
break;
|
|
}
|
|
*pszInVr = (CHAR)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 = DIFF(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() +
|
|
pMDRecord->dwMDDataOffset);
|
|
|
|
switch ( pMDRecord->dwMDIdentifier ) {
|
|
|
|
case MD_IP_SEC:
|
|
RMD_ASSERT( pMDRecord->dwMDDataType == BINARY_METADATA );
|
|
|
|
if ( pMDRecord->dwMDDataLen )
|
|
{
|
|
if ( !SetIpDnsAccessCheck( pMDRecord->dwMDDataTag ?
|
|
pMDRecord->pbMDData :
|
|
pDataPointer,
|
|
pMDRecord->dwMDDataLen,
|
|
pMDRecord->dwMDDataTag ) )
|
|
{
|
|
goto FreeRefs;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MD_ACCESS_PERM:
|
|
DBG_ASSERT( pMDRecord->dwMDDataTag == NULL );
|
|
RMD_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
|
// 64bit alignment fix
|
|
SetAccessPerms(*(UNALIGNED DWORD*) pDataPointer);
|
|
break;
|
|
|
|
case MD_SSL_ACCESS_PERM:
|
|
DBG_ASSERT( pMDRecord->dwMDDataTag == NULL );
|
|
RMD_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
|
// 64bit alignment fix
|
|
SetSslAccessPerms( *((UNALIGNED DWORD *) pDataPointer) );
|
|
break;
|
|
|
|
case MD_DONT_LOG:
|
|
DBG_ASSERT( pMDRecord->dwMDDataTag == NULL );
|
|
DBG_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
|
// 64bit alignment fix
|
|
SetDontLogFlag( *((UNALIGNED 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 );
|
|
// 64bit alignment fix
|
|
SetVrPassThrough( !!*((UNALIGNED 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;
|
|
|
|
case MD_VR_NO_CACHE:
|
|
RMD_ASSERT( pMDRecord->dwMDDataType == DWORD_METADATA );
|
|
// 64bit alignment fix
|
|
SetDoCache( !*(UNALIGNED DWORD *) pDataPointer );
|
|
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() &&
|
|
!(QueryAccessPerms() & VROOT_MASK_NO_PHYSICAL_DIR) )
|
|
{
|
|
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(
|
|
MB * pmb,
|
|
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:
|
|
pmb - pointer to MB object (Metabase pointer)
|
|
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.
|
|
--*/
|
|
{
|
|
BOOL fRet;
|
|
INT cOffSet = 0;
|
|
|
|
CheckSignature();
|
|
DBG_ASSERT(NULL != pmb && NULL != pstrApplPhysicalPath);
|
|
|
|
// m_strAppPath is in the format of /LM/W3SVC/X/ROOT/AppRoot.....
|
|
// Now, the next code segment split this format into
|
|
// strInstanceMDPath = /LM/W3SVC/X/ROOT/
|
|
// strVrPath = /AppRoot....
|
|
STR strInstanceMDPath = STR(m_strAppPath);
|
|
LPSTR pszVrPath = strInstanceMDPath.QueryStr();
|
|
INT VrLevel = 0;
|
|
|
|
while(*pszVrPath != NULL)
|
|
{
|
|
if ('/' == *pszVrPath)
|
|
{
|
|
VrLevel++;
|
|
if (5 == VrLevel)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
pszVrPath++;
|
|
}
|
|
|
|
STR strVrPath = STR(pszVrPath);
|
|
// Case: /LM/W3SVC/1/ROOT
|
|
if (4 == VrLevel)
|
|
{
|
|
strVrPath.Append('/');
|
|
// OffSet needs to minus 1 because strInstanceMDPath does not have
|
|
// ending '/'
|
|
cOffSet = strInstanceMDPath.QueryCCH()-1;
|
|
}
|
|
else if ( 5 == VrLevel)
|
|
{
|
|
// Make a copy of VRPath
|
|
// Caculate the cOffSet of /LM/W3SVC/X/Root/
|
|
// Set the strInstanceMDPath
|
|
cOffSet = DIFF(pszVrPath-strInstanceMDPath.QueryStr());
|
|
strInstanceMDPath.SetLen(cOffSet);
|
|
}
|
|
else
|
|
{
|
|
// Can not resolve Application Physical Path.
|
|
DBG_ASSERT(FALSE);
|
|
pstrApplPhysicalPath->SetLen(0);
|
|
return FALSE;
|
|
}
|
|
|
|
// Open the metabase key from /LM/W3SVC/X/ROOT/
|
|
fRet = pmb->Open(strInstanceMDPath.QueryStr());
|
|
if (TRUE == fRet)
|
|
{
|
|
// Get VR Path first. (Note, this is not the final VRpath).
|
|
fRet = pmb->GetStr(strVrPath.QueryStr(),
|
|
MD_VR_PATH,
|
|
IIS_MD_UT_FILE,
|
|
pstrApplPhysicalPath,
|
|
METADATA_INHERIT,
|
|
NULL);
|
|
|
|
if (TRUE == fRet)
|
|
{
|
|
BOOL fFound = FALSE;
|
|
DWORD dwBufferSizeNeeded;
|
|
LPSTR pszTempMDPath = strVrPath.QueryStr();
|
|
LPSTR pszTemp = pszTempMDPath + strVrPath.QueryCCH() - 1;
|
|
BOOL fNoMoreVR = FALSE; // used to break out infinite loop if there is no VR_PATH
|
|
// defined at /LM/W3SVC/X/ROOT
|
|
|
|
// Find where we get the VR path
|
|
do
|
|
{
|
|
dwBufferSizeNeeded = 0;
|
|
if ( !pmb->GetString(strVrPath.QueryStr(),
|
|
MD_VR_PATH,
|
|
IIS_MD_UT_FILE,
|
|
NULL,
|
|
&dwBufferSizeNeeded,
|
|
0
|
|
)
|
|
&& GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
fFound = TRUE;
|
|
// cOffSet is the strlen(closest Metabase path that defined
|
|
// a MDVrPath property).
|
|
// For Example:
|
|
// /LM/W3SVC/1/ROOT/VR1/VR2/VR3
|
|
// VR2 defines MDVrPath. cOffSet is strlen(/VR2)
|
|
// so, based on cOffSet, we can find VR3 later.
|
|
cOffSet += strVrPath.QueryCCH();
|
|
}
|
|
else
|
|
{
|
|
// '/' is not a DBCS trailing byte.
|
|
// going backwords in /VR1/VR2/VR3/.../VRn and search '/'
|
|
// and shrink one VR Level
|
|
pszTemp = strrchr(pszTempMDPath, '/');
|
|
strVrPath.SetLen(DIFF(pszTemp-pszTempMDPath));
|
|
// if this is TRUE, then, strVrPath is empty now, and there is no VR_PATH
|
|
// defined.
|
|
if (fNoMoreVR)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (0 == strVrPath.QueryCCH())
|
|
{
|
|
fNoMoreVR = TRUE;
|
|
}
|
|
}
|
|
} while (!fFound);
|
|
|
|
pmb->Close();
|
|
if (!fFound && 0 == strVrPath.QueryCCH())
|
|
{
|
|
DBG_ASSERT(FALSE);
|
|
pstrApplPhysicalPath->SetLen(0);
|
|
return FALSE;
|
|
}
|
|
pstrApplPhysicalPath->Append(m_strAppPath.QueryStr()+cOffSet);
|
|
if (pstrApplPhysicalPath->QueryCCH())
|
|
{
|
|
CHAR ch;
|
|
|
|
ch = *CharPrev(pstrApplPhysicalPath->QueryStr(),
|
|
pstrApplPhysicalPath->QueryStr()+pstrApplPhysicalPath->QueryCCH());
|
|
|
|
if (ch != '\\' && ch != '/')
|
|
{
|
|
pstrApplPhysicalPath->Append("\\");
|
|
}
|
|
}
|
|
|
|
FlipSlashes(pstrApplPhysicalPath->QueryStr());
|
|
}
|
|
else
|
|
{
|
|
pmb->Close();
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
} // COMMON_METADATA::BuildApplPhysicalPath()
|
|
|
|
|
|
|
|
BOOL
|
|
COMMON_METADATA::BuildPhysicalPath(
|
|
LPSTR pszURL,
|
|
STR * pstrPhysicalPath
|
|
)
|
|
{
|
|
return BuildPhysicalPathWithAltRoot( pszURL, pstrPhysicalPath, NULL );
|
|
}
|
|
|
|
|
|
BOOL
|
|
COMMON_METADATA::BuildPhysicalPathWithAltRoot(
|
|
LPSTR pszURL,
|
|
STR * pstrPhysicalPath,
|
|
PCSTR pstrAltRoot
|
|
)
|
|
/*++
|
|
Description:
|
|
|
|
Construct a physical path of the following components:
|
|
- the virtual root mapping
|
|
- an alternate root (if provided)
|
|
- the significant part of the URI
|
|
|
|
Arguments:
|
|
pszURL - The URL to be converted
|
|
pstrPhysicalPath - the resulting physical path
|
|
pstrAltRoot - the alternat root
|
|
|
|
Returns:
|
|
TRUE if success, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
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 the alternate root
|
|
//
|
|
|
|
if ( pstrAltRoot )
|
|
{
|
|
if ( pstrPhysicalPath->QueryCCH() )
|
|
{
|
|
//
|
|
// ensure there is one and only one separator
|
|
//
|
|
|
|
ch = *CharPrev(pstrPhysicalPath->QueryStr(), pstrPhysicalPath->QueryStr() +
|
|
pstrPhysicalPath->QueryCCH());
|
|
|
|
if ( (ch != '/') && (ch != '\\') &&
|
|
(*pstrAltRoot != '/') && (*pstrAltRoot != '\\') )
|
|
{
|
|
if ( !pstrPhysicalPath->Append( "\\" ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
} else if ( (ch == '/' || ch == '\\') &&
|
|
(*pstrAltRoot == '/' || *pstrAltRoot == '\\') )
|
|
{
|
|
++pstrAltRoot;
|
|
}
|
|
}
|
|
|
|
if ( !pstrPhysicalPath->Append( pstrAltRoot ) )
|
|
{
|
|
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;
|
|
}
|
|
|
|
|