Leaked source code of windows server 2003
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.
 
 
 
 
 
 

6331 lines
171 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name :
comobj.cxx
Abstract:
This module defines DCOM Admin APIs.
Author:
Sophia Chung (sophiac) 23-Nov-1996
--*/
#include "precomp.hxx"
#include <iadm.h>
#include "coiadm.hxx"
#include "admacl.hxx"
//
// Globals
//
DECLARE_DEBUG_PRINTS_OBJECT();
ULONG g_dwRefCount = 0;
COpenHandle g_ohMasterRootHandle;
HANDLE_TABLE g_MasterRoot = {
NULL,
0,
METADATA_MASTER_ROOT_HANDLE,
&g_ohMasterRootHandle
};
DWORD CADMCOMW::sm_dwProcessIdThis = 0;
DWORD CADMCOMW::sm_dwProcessIdRpcSs = 0;
//
// Private prototypes
//
//
// Used by RestoreHelper
//
#define RESTORE_HISTORY 0x1
#define RESTORE_BACKUP 0x2
BOOL
MakeParentPath(
LPWSTR pszPath
);
//------------------------------
CADMCOMW::CADMCOMW():
m_ImpIConnectionPointContainer(),
m_ImpExpHelp(),
m_pMdObject(NULL),
m_pMdObject3(NULL),
m_dwRefCount(1),
m_dwHandleValue(1),
m_pEventSink(NULL),
m_pConnPoint(NULL),
m_piuFTM(NULL),
m_bTerminated(FALSE),
m_bIsTerminateRoutineComplete(FALSE),
m_dwProcessIdCaller(0),
m_hProcessCaller(NULL),
m_hWaitCaller(NULL),
m_dwThreadIdDisconnect(0)
{
HRESULT hRes;
memset((PVOID)m_hashtab, 0, sizeof(m_hashtab) );
InitializeListHead( &m_ObjectListEntry );
hRes = CoCreateInstance(CLSID_MDCOM, NULL, CLSCTX_INPROC_SERVER, IID_IMDCOM2, (void**) &m_pMdObject);
if (FAILED(hRes))
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::CADMCOMW] CoCreateInstance(MDCOM) failed, error %lx\n",
hRes ));
}
else
{
hRes = m_pMdObject->ComMDInitialize();
if (FAILED(hRes))
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::CADMCOMW] ComMDInitialize(MDCOM) failed, error %lx\n",
hRes ));
m_pMdObject->Release();
m_pMdObject = NULL;
}
}
if (SUCCEEDED(hRes))
{
// pickup IMDCOM3 if available
// ignore the return value by design
// IIS5.1 metadata.dll will fail this call. IIS6.0+ metadata will succeed this call
HRESULT hrTemp = m_pMdObject->QueryInterface(IID_IMDCOM3, (void**)&m_pMdObject3);
DBG_ASSERT(SUCCEEDED(hrTemp) || NULL == m_pMdObject3);
if ( FAILED( hrTemp ) )
{
m_pMdObject3 = NULL;
}
m_pEventSink = new CImpIMDCOMSINKW((IMSAdminBaseW*)this);
if( m_pEventSink == NULL )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::CADMCOMW] CImpIMDCOMSINKW failed, error %lx\n",
ERROR_NOT_ENOUGH_MEMORY ));
hRes = RETURNCODETOHRESULT(ERROR_NOT_ENOUGH_MEMORY);
}
else
{
m_pEventSink->AddRef();
// ImpExpHelp
m_ImpExpHelp.Init(this);
m_ImpIConnectionPointContainer.Init(this);
// Rig this COPaper COM object to be connectable. Assign the connection
// point array. This object's connection points are determined at
// compile time--it currently has only one connection point:
// the CONNPOINT_PAPERSINK connection point. Create a connection
// point object for this and assign it into the array. This array could
// easily grow to support additional connection points in the future.
// First try initializing the connection point object. Pass 'this' as the
// pHostObj pointer used by the connection point to pass its AddRef and
// Release calls back to the host connectable object.
// The initialization will create
// its initial dynamic connection array.
hRes = m_ConnectionPoint.Init(this, IID_IMSAdminBaseSink_W);
if (SUCCEEDED(hRes))
{
//
// Admin's sink
//
IConnectionPointContainer* pConnPointContainer = NULL;
// First query the object for its Connection Point Container. This
// essentially asks the object in the server if it is connectable.
hRes = m_pMdObject->QueryInterface( IID_IConnectionPointContainer,
(PVOID *)&pConnPointContainer);
if SUCCEEDED(hRes)
{
// Find the requested Connection Point. This AddRef's the
// returned pointer.
hRes = pConnPointContainer->FindConnectionPoint(IID_IMDCOMSINK_W, &m_pConnPoint);
if (SUCCEEDED(hRes))
{
hRes = m_pConnPoint->Advise((IUnknown *)m_pEventSink, &m_dwCookie);
}
pConnPointContainer->Release();
pConnPointContainer = NULL;
if (SUCCEEDED(hRes))
{
hRes = CoCreateFreeThreadedMarshaler((IUnknown *)this, &m_piuFTM);
}
}
}
else
{
m_ConnectionPoint.Terminate();
}
}
}
if ( SUCCEEDED( hRes ) )
{
// Initialize watching the caller process
hRes = InitializeCallerWatch();
if ( FAILED( hRes ) )
{
DBGPRINTF(( DBG_CONTEXT,
"InitializeCallerWatch() failed in CADMCOMW::~CADMCOMW hr=0x%08x.\n",
hRes ));
}
}
SetStatus(hRes);
//
// Insert our object into the global list only if it is valid.
//
if( SUCCEEDED(hRes) )
{
AddObjectToList();
}
else
{
StopNotifications( TRUE );
}
}
CADMCOMW::~CADMCOMW()
{
Terminate();
}
HRESULT
CADMCOMW::StopNotifications(
BOOL fRemoveAllPending)
{
HRESULT hr = S_OK;
if ( m_pEventSink != NULL )
{
m_pEventSink->DetachAdminObject();
}
if ( ( m_dwCookie != 0 ) && ( m_pConnPoint != NULL ) )
{
m_pConnPoint->Unadvise(m_dwCookie);
m_dwCookie = 0;
}
m_ConnectionPoint.Disable();
if ( fRemoveAllPending )
{
RemoveAllPendingNotifications( TRUE );
}
return hr;
}
HRESULT
CADMCOMW::RemoveAllPendingNotifications(
BOOL fWaitForCurrent)
{
HRESULT hr = S_OK;
NOTIFY_CONTEXT::RemoveWorkFor( this, fWaitForCurrent );
return hr;
}
VOID
CADMCOMW::Terminate()
{
HANDLE_TABLE *node;
HANDLE_TABLE *nextnode;
DWORD i;
BOOL bTerminated;
//
// Terminate must only be called from two locations. And they
// should synchronize correctly.
//
// 1. From ~CADMCOMW. Obviously this should only be called once.
//
// 2. From ForceTerminate. That routine should only be called in
// shutdown. With a reference held on this object. So the final
// release should call the dtor and this routine should noop.
//
bTerminated = (BOOL)InterlockedCompareExchange( (LONG*)&m_bTerminated, TRUE, FALSE );
if( !bTerminated )
{
ShutdownCallerWatch();
//
// Tell ADMWPROX.DLL to release this object's associated security
// context.
//
ReleaseObjectSecurityContextW( ( IUnknown* )this );
//
// Do final release of the connection point objects.
// If this isn't the final release, then the client has an outstanding
// unbalanced reference to a connection point and a memory leak may
// likely result because the host COPaper object is now going away yet
// a connection point for this host object will not end up deleting
// itself (and its connections array).
//
m_ConnectionPoint.Terminate();
if (SUCCEEDED(GetStatus()))
{
m_LockHandleResource.WriteLock();
//
// Close all opened handles
//
for( i = 0; i < HASHSIZE; i++ )
{
for( node = nextnode = m_hashtab[i]; node != NULL; node = nextnode )
{
if ( node->hAdminHandle != INVALID_ADMINHANDLE_VALUE )
{
AdminAclNotifyClose( (LPVOID)this, node->hAdminHandle );
//
// call metadata com api
//
m_pMdObject->ComMDCloseMetaObject( node->hActualHandle );
}
nextnode = node->next;
delete node->pohHandle;
LocalFree(node);
}
m_hashtab[i] = NULL;
}
//
// Issue TaylorW 3/20/2001
// QFE tree contains this call:
//
// AdminAclNotifyClose( (LPVOID)this, METADATA_MASTER_ROOT_HANDLE );
//
// I have no idea when this may have entered their tree or been lost
// from ours. I don't see any record in source depot of it being
// removed or added. Need to investigate why it would be needed.
//
m_LockHandleResource.WriteUnlock();
}
if ( m_pEventSink != NULL )
{
m_pEventSink->Release();
m_pEventSink = NULL;
}
if ( m_pConnPoint != NULL )
{
m_pConnPoint->Release();
m_pConnPoint = NULL;
}
if ( m_pMdObject != NULL )
{
m_pMdObject->ComMDTerminate(TRUE);
m_pMdObject->Release();
m_pMdObject = NULL;
if ( m_pMdObject3 != NULL )
{
m_pMdObject3->Release();
m_pMdObject3 = NULL;
}
}
if ( m_piuFTM != NULL )
{
m_piuFTM->Release();
m_piuFTM = NULL;
}
m_bIsTerminateRoutineComplete = TRUE;
}
while ( m_bIsTerminateRoutineComplete != TRUE )
{
Sleep( 100 );
}
}
VOID
CADMCOMW::ForceTerminate()
{
const INT MAX_WAIT_TRIES = 5;
INT WaitTries;
DBG_ASSERT( !m_bIsTerminateRoutineComplete );
DBG_ASSERT( !m_bTerminated );
//
// Wait on the reference count of this object. But bound
// the wait so a leaked in process object does not prevent
// us from shutting down the service.
//
// Wait on a ref count of 1, because the caller better be
// holding our last reference. This assumes all external
// references are killed through CoDisconnect() and all
// internal references are released because of dependent
// services shutting down.
//
// Issue TaylorW 3/20/2001
//
// In iis 5.1 the web service will shutdown filters after
// it has already reported that it is done shutting down.
// This is bad, but changing the shutdown logic of the
// web service is not worth doing at this time. Hopefully
// the shutdown timeout will be sufficient to allow this
// operation to complete.
//
// Windows Bugs 318006
//
// CoDisconnect the object and the connection point
DisconnectOrphaned();
for( WaitTries = 0;
m_dwRefCount > 1 && WaitTries < MAX_WAIT_TRIES;
WaitTries++ )
{
SleepEx( WaitTries*200, TRUE );
}
//
// If we timed out. Something is wrong. Most likely someone in
// process has leaked this object. These asserts are actually
// overactive unless ref tracing is enabled on this object.
//
//
// Issue TaylorW 4/9/2001
//
// It looks like front page leaks a base object from in process.
// So these assertions need to be turned off.
//
#define DEBUG_BASE_OBJ_LEAK 0x80000000L
IF_DEBUG( BASE_OBJ_LEAK )
{
DBG_ASSERT( m_dwRefCount == 1 );
DBG_ASSERT( WaitTries < MAX_WAIT_TRIES );
}
//
// Go ahead and try to clean up.
//
Terminate();
}
HRESULT
CADMCOMW::QueryInterface(
REFIID riid,
void **ppObject)
{
HRESULT hr;
// If caller watch is not initialized
if ( !IsCallerWatchInitialized() )
{
// Initialize the caller watch
hr = InitializeCallerWatch();
if ( FAILED( hr ) )
{
return hr;
}
}
if (riid==IID_IUnknown || riid==IID_IMSAdminBase_W)
{
*ppObject = (IMSAdminBase *) this;
AddRef();
}
else if (IID_IMSAdminBase2_W == riid)
{
*ppObject = (IMSAdminBase2 *) this;
AddRef();
}
else if (IID_IMSAdminBase3_W == riid &&
NULL != m_pMdObject3)
{
*ppObject = (IMSAdminBase3 *) this;
AddRef();
}
else if (IID_IMSImpExpHelp_W == riid)
{
*ppObject = &m_ImpExpHelp;
AddRef();
}
else if (IID_IConnectionPointContainer == riid)
{
METADATA_HANDLE hActualHandle;
hr = LookupAndAccessCheck( METADATA_MASTER_ROOT_HANDLE,
&hActualHandle,
NULL,
0,
METADATA_PERMISSION_READ);
if (FAILED(hr))
{
return hr;
}
*ppObject = &m_ImpIConnectionPointContainer;
AddRef();
}
else if (IID_IMarshal == riid)
{
return m_piuFTM->QueryInterface(riid, ppObject);
}
else
{
return E_NOINTERFACE;
}
return S_OK;
}
ULONG
CADMCOMW::AddRef( )
{
DWORD dwRefCount;
dwRefCount = InterlockedIncrement((long *)&m_dwRefCount);
if( sm_pDbgRefTraceLog )
{
WriteRefTraceLog( sm_pDbgRefTraceLog, dwRefCount, this );
}
return dwRefCount;
}
ULONG
CADMCOMW::Release( )
{
DWORD dwRefCount;
dwRefCount = InterlockedDecrement((long *)&m_dwRefCount);
if( sm_pDbgRefTraceLog )
{
WriteRefTraceLog( sm_pDbgRefTraceLog, -(LONG)dwRefCount, this );
}
if( dwRefCount == 1 )
{
//
// We keep a list of objects around so that we can clean up and
// shutdown successfully. The list holds a reference to this object
// when we hit a reference of 1, we know it is time to remove
// ourselves from the list. If we are in shutdown we may already
// have been removed from the list. But normally, this call to
// RemoveObjectFromList removes our last reference and thus sends
// us back through Release and ultimately to our destructor.
//
RemoveObjectFromList();
}
else if( dwRefCount == 0 )
{
delete this;
}
return dwRefCount;
}
HRESULT
CADMCOMW::AddKey(
IN METADATA_HANDLE hMDHandle,
IN LPCWSTR pszMDPath)
/*++
Routine Description:
Add meta object and adds it to the list of child objects for the object
specified by Path.
Arguments:
hMDHandle - open handle
pszMDPath - path of the object to be added
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
hresReturn = AddKeyHelper(hMDHandle, pszMDPath);
return hresReturn;
}
HRESULT
CADMCOMW::DeleteKey(
IN METADATA_HANDLE hMDHandle,
IN LPCWSTR pszMDPath)
/*++
Routine Description:
Deletes a meta object and all of its data.
Arguments:
hMDHandle - open handle
pszMDPath - path of object to be deleted, relative to the path of Handle.
Must not be NULL.
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
if (pszMDPath == NULL)
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
AAC_DELETEKEY,
METADATA_PERMISSION_WRITE);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDDeleteMetaObjectW( hActualHandle,
pszMDPath );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::DeleteChildKeys(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath)
/*++
Routine Description:
Deletes all child meta objects of the specified object, with all of their
data.
Arguments:
hMDHandle - open handle
pszMDPath - path of the parent of the objects to be deleted, relative to
the path of Handle.
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
AAC_DELETEKEY,
METADATA_PERMISSION_WRITE);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDDeleteChildMetaObjectsW( hActualHandle,
pszMDPath );
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::EnumKeys(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
LPWSTR pszMDName,
DWORD dwMDEnumObjectIndex)
/*++
Routine Description:
Enumerate objects in path.
Arguments:
hMDHandle - open handle
pszMDPath - path of parent object, relative to the path of Handle
eg. "Root Object/Child/GrandChild"
pszMDName - buffer where the Name of the object is returned
dwEnumObjectIndex - index of the value to be retrieved
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
if (pszMDName == NULL)
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
AAC_ENUM_KEYS,
METADATA_PERMISSION_READ);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDEnumMetaObjectsW( hActualHandle,
pszMDPath,
pszMDName,
dwMDEnumObjectIndex );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::CopyKey(
METADATA_HANDLE hMDSourceHandle,
LPCWSTR pszMDSourcePath,
METADATA_HANDLE hMDDestHandle,
LPCWSTR pszMDDestPath,
BOOL bMDOverwriteFlag,
BOOL bMDCopyFlag)
/*++
Routine Description:
Copy or move source meta object and its data and descendants to Dest.
Arguments:
hMDSourceHandle - open handle
pszMDSourcePath - path of the object to be copied
hMDDestHandle - handle of the new location for the object
pszMDDestPath - path of the new location for the object, relative
to the path of hMDDestHandle
bMDOverwriteFlag - determine the behavior if a meta object with the same
name as source is already a child of pszMDDestPath.
bMDCopyFlag - determine whether Source is deleted from its original location
Return Value:
Status
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hSActualHandle;
METADATA_HANDLE hDActualHandle;
//
// lookup and access check source
//
if (bMDCopyFlag)
{
hresReturn = LookupAndAccessCheck(hMDSourceHandle,
&hSActualHandle,
pszMDSourcePath,
0,
METADATA_PERMISSION_READ);
}
else
{
//
// Deleting source path, so need delete permission
//
hresReturn = LookupAndAccessCheck(hMDSourceHandle,
&hSActualHandle,
pszMDSourcePath,
AAC_DELETEKEY,
METADATA_PERMISSION_WRITE);
}
if (SUCCEEDED(hresReturn))
{
//
// lookup and access check dest
//
hresReturn = LookupAndAccessCheck(hMDDestHandle,
&hDActualHandle,
pszMDDestPath,
AAC_COPYKEY,
METADATA_PERMISSION_WRITE);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDCopyMetaObjectW( hSActualHandle,
pszMDSourcePath,
hDActualHandle,
pszMDDestPath,
bMDOverwriteFlag,
bMDCopyFlag );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::RenameKey(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
LPCWSTR pszMDNewName)
{
HRESULT hr = S_OK;
BOOL fRet;
DWORD dwError;
METADATA_HANDLE hActualHandle;
COpenHandle *pohParent = NULL;
DWORD cchMDPath;
DWORD i;
STRAU strNewPath;
// Check args
if ( ( hMDHandle == METADATA_MASTER_ROOT_HANDLE ) ||
( pszMDPath == NULL ) ||
( pszMDNewName == NULL ) ||
( *pszMDNewName == L'\0' ) ||
( wcschr( pszMDNewName, MD_PATH_DELIMETERW ) != NULL ) ||
( wcschr( pszMDNewName, MD_ALT_PATH_DELIMETERW ) != NULL ) )
{
hr = E_INVALIDARG;
goto exit;
}
// Get the len of the source path
cchMDPath = (DWORD)wcslen( pszMDPath );
// Check
if ( ( cchMDPath == 0 ) || ( cchMDPath >= METADATA_MAX_NAME_LEN ) )
{
hr = E_INVALIDARG;
goto exit;
}
// Map Admin Handle to Actual Handle
dwError = Lookup( hMDHandle,
&hActualHandle,
&pohParent );
if ( dwError != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// Check access
fRet = AdminAclAccessCheck( m_pMdObject,
this,
hMDHandle,
pszMDPath,
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
pohParent );
if ( !fRet )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// Now we need to do the harder part: check whether the caller
// has access to the new path. To do so we need to construct the
// target path by replacing the last name in the source path with the new name.
// If the source path ends with delimiter
if ( ( pszMDPath[cchMDPath-1] == MD_PATH_DELIMETERW ) ||
( pszMDPath[cchMDPath-1] == MD_ALT_PATH_DELIMETERW ) )
{
// Ignore it
cchMDPath--;
// The resulting path should not be empty or ending with delimiter
if ( ( cchMDPath == 0 ) ||
( pszMDPath[cchMDPath-1] == MD_PATH_DELIMETERW ) ||
( pszMDPath[cchMDPath-1] == MD_ALT_PATH_DELIMETERW ) )
{
hr = E_INVALIDARG;
goto exit;
}
}
// Find the last delimiter
for ( i = cchMDPath; i != 0; i-- )
{
// Delimiter?
if ( ( pszMDPath[i-1] == MD_PATH_DELIMETERW ) ||
( pszMDPath[i-1] == MD_ALT_PATH_DELIMETERW ) )
{
// The part we are interested in is upto excluding the delimiter
// It is valid for cchMDPath to become 0.
cchMDPath = i-1;
break;
}
}
// If there was no delimiter
if ( i == 0 )
{
// This source was just a name, so bellow we shouldn't copy anything
cchMDPath = 0;
}
// Copy the source path w/o the last name
fRet = strNewPath.Copy( (const LPWSTR)pszMDPath, cchMDPath );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
fRet = strNewPath.Append( L"/", 1 );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Append the new name
fRet = strNewPath.Append( (const LPWSTR)pszMDNewName );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Check access on the target path
fRet = AdminAclAccessCheck( m_pMdObject,
this,
hMDHandle,
strNewPath.QueryStrW(),
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
pohParent );
if ( !fRet )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// call metadata com api
hr = m_pMdObject->ComMDRenameMetaObjectW( hActualHandle,
pszMDPath,
pszMDNewName );
if ( FAILED( hr ) )
{
goto exit;
}
exit:
if ( pohParent != NULL )
{
pohParent->Release(this);
pohParent = NULL;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::SetData(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
PMETADATA_RECORD pmdrMDData)
/*++
Routine Description:
Set a data object.
Arguments:
hMDHandle - open handle
pszMDPath - path of the meta object with which this data is associated
pmdrMDData - data to set
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
pmdrMDData->dwMDIdentifier,
METADATA_PERMISSION_WRITE );
if (SUCCEEDED(hresReturn))
{
if ( !AdminAclNotifySetOrDeleteProp(
hMDHandle,
pmdrMDData->dwMDIdentifier ) )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::SetData] AdminAclNotifySetOrDel failed, error %lx\n",
GetLastError() ));
hresReturn = RETURNCODETOHRESULT( GetLastError() );
}
else
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDSetMetaDataW( hActualHandle,
pszMDPath,
pmdrMDData );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetData(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
PMETADATA_RECORD pmdrMDData,
DWORD *pdwMDRequiredDataLen)
/*++
Routine Description:
Get one metadata value
Arguments:
hMDHandle - open handle
pszMDPath - path of the meta object with which this data is associated
pmdrMDData - data structure
pdwMDRequiredDataLen - updated with required length
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
BOOL fEnableSecureAccess;
BOOL fRequestedInheritedStatus;
DWORD dwRetCode;
if ((pmdrMDData == NULL) ||
((pmdrMDData->dwMDDataLen != 0) && (pmdrMDData->pbMDData == NULL)) ||
!CheckGetAttributes(pmdrMDData->dwMDAttributes) ||
(pmdrMDData->dwMDDataType >= INVALID_END_METADATA))
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
pmdrMDData->dwMDIdentifier,
METADATA_PERMISSION_READ,
&fEnableSecureAccess );
if (SUCCEEDED(hresReturn))
{
fRequestedInheritedStatus = pmdrMDData->dwMDAttributes & METADATA_ISINHERITED;
pmdrMDData->dwMDAttributes |= METADATA_ISINHERITED;
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDGetMetaDataW( hActualHandle,
pszMDPath,
pmdrMDData,
pdwMDRequiredDataLen );
//
// if metadata is secure, check if can access this property from
// where it is defined, i.e using the ACL visible at the definition
// point in tree.
//
if ( SUCCEEDED( hresReturn ) &&
(pmdrMDData->dwMDAttributes & METADATA_SECURE) &&
(dwRetCode = IsReadAccessGranted( hMDHandle,
(LPWSTR)pszMDPath,
pmdrMDData ))
!= ERROR_SUCCESS )
{
hresReturn = RETURNCODETOHRESULT( dwRetCode );
}
if ( !fRequestedInheritedStatus )
{
pmdrMDData->dwMDAttributes &= ~METADATA_ISINHERITED;
}
//
// if metadata secure, check access allowed to secure properties
//
if ( SUCCEEDED( hresReturn ) &&
(pmdrMDData->dwMDAttributes & METADATA_SECURE) &&
!fEnableSecureAccess)
{
*pdwMDRequiredDataLen = 0;
pmdrMDData->dwMDDataLen = 0;
hresReturn = RETURNCODETOHRESULT( ERROR_ACCESS_DENIED );
}
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::DeleteData(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD dwMDIdentifier,
DWORD dwMDDataType)
/*++
Routine Description:
Deletes a data object.
Arguments:
hMDHandle - open handle
pszMDPath - path of the meta object with which this data is associated
dwMDIdentifier - identifier of the data to remove
dwMDDataType - optional type of the data to remove
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
if (dwMDDataType >= INVALID_END_METADATA)
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
dwMDIdentifier,
METADATA_PERMISSION_WRITE);
if (SUCCEEDED(hresReturn))
{
if ( !AdminAclNotifySetOrDeleteProp(
hMDHandle,
dwMDIdentifier ) )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::DeleteData] AdminAclNotifySetOrDel failed, error %lx\n",
GetLastError() ));
hresReturn = RETURNCODETOHRESULT( GetLastError() );
}
else
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDDeleteMetaDataW( hActualHandle,
pszMDPath,
dwMDIdentifier,
dwMDDataType );
}
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::EnumData(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
PMETADATA_RECORD pmdrMDData,
DWORD dwMDEnumDataIndex,
DWORD *pdwMDRequiredDataLen)
/*++
Routine Description:
Enumerate properties of object.
Arguments:
hMDHandle - open handle
pszMDPath - path of the meta object with which this data is associated
pmdrMDData - data structure
pdwMDRequiredDataLen - updated with required length
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
BOOL fSecure;
BOOL fRequestedInheritedStatus;
DWORD dwRetCode;
if ((pmdrMDData == NULL) ||
((pmdrMDData->dwMDDataLen != 0) && (pmdrMDData->pbMDData == NULL)) ||
!CheckGetAttributes(pmdrMDData->dwMDAttributes) ||
(pmdrMDData->dwMDDataType >= INVALID_END_METADATA))
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_READ,
&fSecure);
if (SUCCEEDED(hresReturn))
{
fRequestedInheritedStatus = pmdrMDData->dwMDAttributes & METADATA_ISINHERITED;
pmdrMDData->dwMDAttributes |= METADATA_ISINHERITED;
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDEnumMetaDataW( hActualHandle,
pszMDPath,
pmdrMDData,
dwMDEnumDataIndex,
pdwMDRequiredDataLen );
//
// if metadata is secure, check if can access this property from
// where it is defined, i.e using the ACL visible at the definition
// point in tree.
//
if ( SUCCEEDED( hresReturn ) &&
(pmdrMDData->dwMDAttributes & METADATA_SECURE) &&
(dwRetCode = IsReadAccessGranted( hMDHandle,
(LPWSTR)pszMDPath,
pmdrMDData ))
!= ERROR_SUCCESS )
{
hresReturn = RETURNCODETOHRESULT( dwRetCode );
if ( !pmdrMDData->dwMDDataTag )
{
memset( pmdrMDData->pbMDData, 0x0, pmdrMDData->dwMDDataLen );
}
}
if ( !fRequestedInheritedStatus )
{
pmdrMDData->dwMDAttributes &= ~METADATA_ISINHERITED;
}
if ( !fSecure && SUCCEEDED(hresReturn) )
{
if ( pmdrMDData->dwMDAttributes & METADATA_SECURE )
{
hresReturn = RETURNCODETOHRESULT( ERROR_ACCESS_DENIED );
if ( !pmdrMDData->dwMDDataTag )
{
memset( pmdrMDData->pbMDData, 0x0, pmdrMDData->dwMDDataLen );
}
}
}
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetAllData(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD dwMDAttributes,
DWORD dwMDUserType,
DWORD dwMDDataType,
DWORD *pdwMDNumDataEntries,
DWORD *pdwMDDataSetNumber,
DWORD dwMDBufferSize,
unsigned char *pbMDBuffer,
DWORD *pdwMDRequiredBufferSize)
/*++
Routine Description:
Gets all data associated with a Meta Object
Arguments:
hMDHandle - open handle
pszMDPath - path of the meta object with which this data is associated
dwMDAttributes - flags for the data
dwMDUserType - user Type for the data
dwMDDataType - type of the data
pdwMDNumDataEntries - number of entries copied to Buffer
pdwMDDataSetNumber - number associated with this data set
dwMDBufferSize - size in bytes of buffer
pbMDBuffer - buffer to store the data
pdwMDRequiredBufferSize - updated with required length of buffer
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
BOOL fSecure;
BOOL fRequestedInheritedStatus;
if ((pdwMDNumDataEntries == NULL) || ((dwMDBufferSize != 0) && (pbMDBuffer == NULL)) ||
!CheckGetAttributes(dwMDAttributes) ||
(dwMDDataType >= INVALID_END_METADATA))
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
AAC_GETALL,
METADATA_PERMISSION_READ,
&fSecure );
if (SUCCEEDED(hresReturn))
{
fRequestedInheritedStatus = dwMDAttributes & METADATA_ISINHERITED;
dwMDAttributes |= METADATA_ISINHERITED;
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDGetAllMetaDataW( hActualHandle,
pszMDPath,
dwMDAttributes,
dwMDUserType,
dwMDDataType,
pdwMDNumDataEntries,
pdwMDDataSetNumber,
dwMDBufferSize,
pbMDBuffer,
pdwMDRequiredBufferSize );
if ( SUCCEEDED(hresReturn) )
{
PMETADATA_GETALL_RECORD pMDRecord;
DWORD iP;
//
// Scan for secure properties
// For such properties, check if user has access to it using following rules:
// - must have right to access secure properties in ACE
// - must have access to property using ACL visible where property is defined
// if no access to property then remove it from list of returned properties
pMDRecord = (PMETADATA_GETALL_RECORD)pbMDBuffer;
for ( iP = 0 ; iP < *pdwMDNumDataEntries ; )
{
if ( pMDRecord->dwMDAttributes & METADATA_SECURE )
{
if ( !fSecure ||
IsReadAccessGranted( hMDHandle,
(LPWSTR)pszMDPath,
(PMETADATA_RECORD)pMDRecord ) != ERROR_SUCCESS )
{
//
// remove this property from METADATA_RECORD list,
// zero out content
//
memset( pbMDBuffer + pMDRecord->dwMDDataOffset,
0x0,
pMDRecord->dwMDDataLen );
--*pdwMDNumDataEntries;
memmove( pMDRecord,
pMDRecord + 1,
sizeof(METADATA_GETALL_RECORD) * (*pdwMDNumDataEntries-iP) );
continue;
}
}
if ( !fRequestedInheritedStatus )
{
pMDRecord->dwMDAttributes &= ~METADATA_ISINHERITED;
}
++iP;
++pMDRecord;
}
}
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::DeleteAllData(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD dwMDUserType,
DWORD dwMDDataType)
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
if (dwMDDataType >= INVALID_END_METADATA)
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_WRITE);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDDeleteAllMetaDataW( hActualHandle,
pszMDPath,
dwMDUserType,
dwMDDataType );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::CopyData(
METADATA_HANDLE hMDSourceHandle,
LPCWSTR pszMDSourcePath,
METADATA_HANDLE hMDDestHandle,
LPCWSTR pszMDDestPath,
DWORD dwMDAttributes,
DWORD dwMDUserType,
DWORD dwMDDataType,
BOOL bMDCopyFlag)
/*++
Routine Description:
Copies or moves data associated with the source object to the destination
object.
Arguments:
hMDSourceHandle - open handle
pszMDSourcePath - path of the meta object with which then source data is
associated
hMDDestHandle - handle returned by MDOpenKey with write permission
pszMDDestPath - path of the meta object for data to be copied to
dwMDAttributes - flags for the data
dwMDUserType - user Type for the data
dwMDDataType - optional type of the data to copy
bMDCopyFlag - if true, data will be copied; if false, data will be moved.
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hSActualHandle;
METADATA_HANDLE hDActualHandle;
if (((!bMDCopyFlag) && (dwMDAttributes & METADATA_INHERIT)) ||
((dwMDAttributes & METADATA_PARTIAL_PATH) && !(dwMDAttributes & METADATA_INHERIT)))
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check source
//
if (bMDCopyFlag)
{
hresReturn = LookupAndAccessCheck(hMDSourceHandle,
&hSActualHandle,
pszMDSourcePath,
0,
METADATA_PERMISSION_READ);
}
else
{
//
// Deleting source data, so need delete permission
//
hresReturn = LookupAndAccessCheck(hMDSourceHandle,
&hSActualHandle,
pszMDSourcePath,
0,
METADATA_PERMISSION_WRITE);
}
if (SUCCEEDED(hresReturn))
{
//
// lookup and access check dest
//
hresReturn = LookupAndAccessCheck(hMDDestHandle,
&hDActualHandle,
pszMDDestPath,
0,
METADATA_PERMISSION_WRITE);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDCopyMetaDataW(hSActualHandle,
pszMDSourcePath,
hDActualHandle,
pszMDDestPath,
dwMDAttributes,
dwMDUserType,
dwMDDataType,
bMDCopyFlag );
}
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetDataPaths(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD dwMDIdentifier,
DWORD dwMDDataType,
DWORD dwMDBufferSize,
LPWSTR pszMDBuffer,
DWORD *pdwMDRequiredBufferSize)
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
BOOL fSecure;
if (((pszMDBuffer == NULL) && (dwMDBufferSize != 0)) ||
(dwMDDataType >= INVALID_END_METADATA) ||
(pdwMDRequiredBufferSize == NULL))
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_READ,
&fSecure);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDGetMetaDataPathsW( hActualHandle,
pszMDPath,
dwMDIdentifier,
dwMDDataType,
dwMDBufferSize,
pszMDBuffer,
pdwMDRequiredBufferSize );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::OpenKey(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD dwMDAccessRequested,
DWORD dwMDTimeOut,
PMETADATA_HANDLE phMDNewHandle)
/*++
Routine Description:
Opens a meta object for read and/or write access.
Arguments:
hMDHandle - open handle
pszMDPath - path of the object to be opened
dwMDAccessRequested - permissions requested
dwMDTimeOut - time to block waiting for open to succeed, in miliseconds.
phMDNewHandle - handle to be passed to other MD routines
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
// If caller watch is not initialized
if ( !IsCallerWatchInitialized() )
{
// Initialize the caller watch
hresReturn = InitializeCallerWatch();
if ( FAILED( hresReturn ) )
{
return hresReturn;
}
}
hresReturn = OpenKeyHelper(hMDHandle, pszMDPath, dwMDAccessRequested, dwMDTimeOut, phMDNewHandle);
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::CloseKey(
METADATA_HANDLE hMDHandle)
/*++
Routine Description:
Closes a handle to a meta object.
Arguments:
hMDHandle - open handle
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
DWORD dwTemp;
COpenHandle *pohHandle;
if ((hMDHandle == METADATA_MASTER_ROOT_HANDLE))
{
hresReturn = E_HANDLE;
}
else
{
//
// Map Admin Handle to Actual Handle
//
if( (dwTemp = Lookup( hMDHandle,
&hActualHandle,
&pohHandle )) != ERROR_SUCCESS )
{
hresReturn = RETURNCODETOHRESULT(dwTemp);
}
else
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDCloseMetaObject( hActualHandle );
pohHandle->Release(this);
//
// Remove node from handle table
//
if (SUCCEEDED(hresReturn))
{
pohHandle->Release(this);
}
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::ChangePermissions(
METADATA_HANDLE hMDHandle,
DWORD dwMDTimeOut,
DWORD dwMDAccessRequested)
/*++
Routine Description:
Changes permissions on an open meta object handle.
Arguments:
hMDHandle - handle to be modified
dwMDTimeOut - time to block waiting for open to succeed, in miliseconds.
dwMDAccessRequested - requested permissions
Return Value:
Status.
--*/
{
HRESULT hr = S_OK;
METADATA_HANDLE hActualHandle;
if ( hMDHandle == METADATA_MASTER_ROOT_HANDLE )
{
hr = E_HANDLE;
goto exit;
}
if ( ( ( dwMDAccessRequested & ( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) == 0 ) ||
( ( dwMDAccessRequested & ~( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) != 0 ) )
{
hr = E_INVALIDARG;
goto exit;
}
//
// Map Admin Handle to Actual Handle
// and check access
//
hr = LookupAndAccessCheck( hMDHandle,
&hActualHandle,
L"",
0,
dwMDAccessRequested );
if( FAILED( hr ) )
{
goto exit;
}
//
// call metadata com api
//
hr = m_pMdObject->ComMDChangePermissions( hActualHandle,
dwMDTimeOut,
dwMDAccessRequested );
exit:
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::SaveData( )
/*++
Routine Description:
Saves all data changed since the last load or save to permanent storage.
Arguments:
None.
Return Value:
Status.
--*/
{
HRESULT hr = S_OK;
METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE;
METADATA_HANDLE hActualHandle;
//
// lookup and access check
//
hr = LookupAndAccessCheck( METADATA_MASTER_ROOT_HANDLE,
&hActualHandle,
L"",
0,
METADATA_PERMISSION_READ);
if ( FAILED( hr ) )
{
goto exit;
}
//
// First try to lock the tree
//
hr = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE,
NULL,
METADATA_PERMISSION_READ,
DEFAULT_SAVE_TIMEOUT,
&mdhRoot );
if ( FAILED( hr ) )
{
goto exit;
}
//
// call metadata com api
//
hr = m_pMdObject->ComMDSaveData(mdhRoot);
m_pMdObject->ComMDCloseMetaObject(mdhRoot);
exit:
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetHandleInfo(
METADATA_HANDLE hMDHandle,
PMETADATA_HANDLE_INFO pmdhiInfo)
/*++
Routine Description:
Gets the information associated with a handle.
Arguments:
hMDHandle - handle to get information about
pmdhiInfo - structure filled in with the information
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
DWORD dwRetCode = ERROR_SUCCESS;
if (pmdhiInfo == NULL)
{
hresReturn = E_INVALIDARG;
}
else
{
//
// Map Admin Handle to Actual Handle
//
if( (dwRetCode = Lookup( hMDHandle,
&hActualHandle
)) != ERROR_SUCCESS )
{
hresReturn = RETURNCODETOHRESULT(dwRetCode);
}
else
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDGetHandleInfo( hActualHandle,
pmdhiInfo );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetSystemChangeNumber(
DWORD *pdwSystemChangeNumber)
/*++
Routine Description:
Gets the System Change Number.
Arguments:
pdwSystemChangeNumber - system change number
Return Value:
Status.
--*/
{
HRESULT hr = S_OK;
METADATA_HANDLE hActualHandle;
// Check args
if ( pdwSystemChangeNumber == NULL )
{
hr = E_INVALIDARG;
goto exit;
}
//
// lookup and access check
//
hr = LookupAndAccessCheck( METADATA_MASTER_ROOT_HANDLE,
&hActualHandle,
L"",
0,
METADATA_PERMISSION_READ );
if ( FAILED( hr ) )
{
goto exit;
}
//
// call metadata com api
//
hr = m_pMdObject->ComMDGetSystemChangeNumber( pdwSystemChangeNumber );
exit:
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetDataSetNumber(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD *pdwMDDataSetNumber)
/*++
Routine Description:
Gets all the data set number associated with a Meta Object.
Arguments:
hMDHandle - open handle
pszMDPath - path of the meta object with which this data is associated
pdwMDDataSetNumber - number associated with this data set
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
if (pdwMDDataSetNumber == NULL)
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_READ);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDGetDataSetNumberW( hActualHandle,
pszMDPath,
pdwMDDataSetNumber );
}
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::SetLastChangeTime(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
PFILETIME pftMDLastChangeTime,
BOOL bLocalTime)
/*++
Routine Description:
Set the last change time associated with a Meta Object.
Arguments:
hMDHandle - open handle
pszMDPath - path of the affected meta object
pftMDLastChangeTime - new change time for the meta object
Return Value:
Status.
--*/
{
HRESULT hr = S_OK;
BOOL fRet;
DWORD dwError;
METADATA_HANDLE hActualHandle;
FILETIME ftTime;
FILETIME *pftTime = NULL;
// Check arhs
if ( ( pftMDLastChangeTime == NULL ) ||
( hMDHandle == METADATA_MASTER_ROOT_HANDLE ) )
{
hr = E_INVALIDARG;
goto exit;
}
// lookup and access check
hr = LookupAndAccessCheck( hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_WRITE );
if ( FAILED( hr ) )
{
goto exit;
}
if ( bLocalTime )
{
fRet = LocalFileTimeToFileTime( pftMDLastChangeTime, &ftTime );
if ( !fRet )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
pftTime = &ftTime;
}
else
{
pftTime = pftMDLastChangeTime;
}
// call metadata com api
hr = m_pMdObject->ComMDSetLastChangeTimeW( hActualHandle,
pszMDPath,
pftTime );
if ( FAILED( hr ) )
{
goto exit;
}
exit:
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetLastChangeTime(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
PFILETIME pftMDLastChangeTime,
BOOL bLocalTime)
/*++
Routine Description:
Set the last change time associated with a Meta Object.
Arguments:
Handle - open handle
pszMDPath - path of the affected meta object
pftMDLastChangeTime - place to return the change time for the meta object
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
FILETIME ftTime;
if (pftMDLastChangeTime == NULL)
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_READ);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDGetLastChangeTimeW( hActualHandle,
pszMDPath,
&ftTime );
if (bLocalTime)
{
if (!FileTimeToLocalFileTime(&ftTime, pftMDLastChangeTime))
{
hresReturn = E_UNEXPECTED;
}
}
else
{
*pftMDLastChangeTime = ftTime;
}
}
}
return hresReturn;
}
HRESULT
CADMCOMW::BackupHelper(
LPCWSTR pszMDBackupLocation,
DWORD dwMDVersion,
DWORD dwMDFlags,
LPCWSTR pszPasswd)
{
HRESULT hresReturn = S_OK;
HRESULT hresWarning = S_OK;
METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE;
if ( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
&g_ohMasterRootHandle ) )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::Backup] AdminAclAccessCheck failed, error %lx\n",
GetLastError() ));
hresReturn = RETURNCODETOHRESULT( GetLastError() );
}
else
{
if ((dwMDFlags & MD_BACKUP_SAVE_FIRST) != 0)
{
//
// First lock the tree
//
hresReturn = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE,
NULL,
METADATA_PERMISSION_READ,
DEFAULT_SAVE_TIMEOUT,
&mdhRoot);
}
if (FAILED(hresReturn))
{
if ((dwMDFlags & MD_BACKUP_FORCE_BACKUP) != 0)
{
hresWarning = MD_WARNING_SAVE_FAILED;
hresReturn = ERROR_SUCCESS;
dwMDFlags &= ~(MD_BACKUP_FORCE_BACKUP | MD_BACKUP_SAVE_FIRST);
}
}
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
if( !pszPasswd )
{
hresReturn = m_pMdObject->ComMDBackupW(mdhRoot,
pszMDBackupLocation,
dwMDVersion,
dwMDFlags);
}
else
{
hresReturn = m_pMdObject->ComMDBackupWithPasswdW(mdhRoot,
pszMDBackupLocation,
dwMDVersion,
dwMDFlags,
pszPasswd);
}
if ((dwMDFlags & MD_BACKUP_SAVE_FIRST) != 0)
{
m_pMdObject->ComMDCloseMetaObject(mdhRoot);
}
}
if (hresReturn == ERROR_SUCCESS)
{
hresReturn = hresWarning;
}
}
return hresReturn;
}
#define MD_DEFAULT_BACKUP_LOCATION_W L"MDBackUp"
HRESULT
CADMCOMW::RestoreHelper(
LPCWSTR pszMDBackupLocation,
DWORD dwMDVersion,
DWORD dwMDMinorVersion,
LPCWSTR pszPasswd,
DWORD dwMDFlags,
DWORD dwRestoreType) // RESTORE_HISTORY or RESTORE_BACKUP
{
HRESULT hr = S_OK;
DWORD dwError;
BOOL fRet;
BUFFER bufDependentServices;
DWORD cServices = 0;
WCHAR pszEnumLocation[MD_BACKUP_MAX_LEN] = {0};
DWORD dwEnumVersion;
DWORD dwEnumMinorVersion;
FILETIME ftEnumTime;
DWORD i;
DWORD dwEnableHistory = FALSE;
DWORD dwEnableEWR = FALSE;
BOOL fEnableAclCache = FALSE;
// This should be called only internally, so passing wrong restore type is a bug
DBG_ASSERT( dwRestoreType == RESTORE_HISTORY || dwRestoreType == RESTORE_BACKUP );
// Check args
if ( ( dwRestoreType != RESTORE_HISTORY && dwRestoreType != RESTORE_BACKUP ) ||
( dwRestoreType == RESTORE_BACKUP && pszMDBackupLocation == NULL ) ||
( pszMDBackupLocation && wcslen(pszMDBackupLocation) >= MD_BACKUP_MAX_LEN ) )
{
hr = E_INVALIDARG;
goto exit;
}
// Check access
fRet = AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
&g_ohMasterRootHandle );
if ( !fRet )
{
dwError = GetLastError();
DBGPRINTF(( DBG_CONTEXT,
( ( dwRestoreType == RESTORE_HISTORY ) ?
"[CADMCOMW::RestoreHistory] AdminAclAccessCheck failed, error %lx\n" :
"[CADMCOMW::Restore] AdminAclAccessCheck failed, error %lx\n" ),
dwError ));
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// Find the requested backup/history
for ( i = 0; SUCCEEDED( hr ); i++ )
{
if ( dwRestoreType == RESTORE_HISTORY )
{
if( pszMDBackupLocation != NULL )
{
wcscpy( pszEnumLocation, pszMDBackupLocation );
}
else
{
*pszEnumLocation = L'\0';
}
hr = m_pMdObject->ComMDEnumHistoryW( pszEnumLocation,
&dwEnumVersion,
&dwEnumMinorVersion,
&ftEnumTime,
i);
if ( FAILED( hr ) )
{
break;
}
if( dwMDFlags & MD_HISTORY_LATEST )
{
break;
}
else
{
if ( ( dwEnumVersion == dwMDVersion ) &&
( dwEnumMinorVersion == dwMDMinorVersion ) )
{
break;
}
}
}
else
{
if( pszMDBackupLocation != NULL )
{
wcscpy( pszEnumLocation, pszMDBackupLocation );
}
else
{
wcscpy( pszEnumLocation, MD_DEFAULT_BACKUP_LOCATION_W );
}
hr = m_pMdObject->ComMDEnumBackupsW( pszEnumLocation,
&dwEnumVersion,
&ftEnumTime,
i);
if ( FAILED( hr ) )
{
break;
}
if ( ( dwEnumVersion == dwMDVersion ) ||
( dwMDVersion == MD_BACKUP_HIGHEST_VERSION ) )
{
break;
}
}
}
// If we failed to find the requested backup/history
if ( FAILED( hr ) )
{
// If asked for an version that doesn't exist
// adjust the error code
if ( hr == HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) )
{
if( dwRestoreType == RESTORE_HISTORY )
{
if( dwMDFlags & MD_HISTORY_LATEST )
{
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
}
else
{
hr = MD_ERROR_INVALID_VERSION;
}
}
else
{
hr = E_INVALIDARG;
}
}
goto exit;
}
//
// Looks like a valid metabase
//
// Disable History (and EWR) to prevent the dependent services creating
// history and deleting currently valid history files during their stoppping.
// Keep the current state of EnableHistory and EnableEWR, so we can restore
// them if restore fails.
hr = DisableHistory(&dwEnableHistory, &dwEnableEWR);
if ( FAILED( hr ) )
{
goto exit;
}
// Stop all dependent services, keeping their list so later we can start them again
hr = EnumAndStopDependentServices( &cServices, &bufDependentServices );
if ( FAILED( hr ) )
{
goto exit;
}
// Disable the Acl cache, since we are going to re-load the whole metabase
AdminAclDisableAclCache();
// Remember to re-enable it
fEnableAclCache = TRUE;
// Discard everything from the Acl caches
AdminAclFlushCache();
// Finally call the CMDCOM method
if( dwRestoreType == RESTORE_HISTORY )
{
hr = m_pMdObject->ComMDRestoreHistoryW( pszMDBackupLocation,
dwMDVersion,
dwMDMinorVersion,
dwMDFlags );
}
else
{
if( !pszPasswd )
{
hr = m_pMdObject->ComMDRestoreW( pszMDBackupLocation,
dwMDVersion,
dwMDFlags );
}
else
{
hr = m_pMdObject->ComMDRestoreWithPasswdW( pszMDBackupLocation,
dwMDVersion,
dwMDFlags,
pszPasswd );
}
}
if ( FAILED( hr ) )
{
goto exit;
}
//
// Issue TaylorW 4/10/2001
//
// After the restore, notify clients, as data has changed
// and all handles have become invalid
//
// Windows Bug 82423
//
// If restore succeed the values of the EnableHistory and EnableEWR are
// set as from the backup/history file, so we don't restore them.
dwEnableHistory = FALSE;
dwEnableEWR = FALSE;
exit:
// If something went wrong after we got the EnableHistoy and EnableEWR and
// at least one of them was not FALSE set them back
if ( dwEnableHistory || dwEnableEWR )
{
SetHistoryAndEWR( dwEnableHistory, dwEnableEWR );
}
// If we disable the acl cache
if ( fEnableAclCache )
{
// Re-enable it
AdminAclEnableAclCache();
}
// If we stopped any services
if ( cServices )
{
// Enable them back
StartDependentServices( cServices, (ENUM_SERVICE_STATUS*)bufDependentServices.QueryPtr() );
}
return hr;
}
HRESULT
CADMCOMW::EnumAndStopDependentServices(
DWORD *pcServices,
BUFFER *pbufDependentServices)
{
// Locals
HRESULT hr = S_OK;
DWORD dwError;
BOOL fRet;
SC_HANDLE schSCM = NULL;
SC_HANDLE schIISADMIN = NULL;
SC_HANDLE schDependent = NULL;
SERVICE_STATUS ssDependent;
DWORD dwBytesNeeded;
ENUM_SERVICE_STATUS *pessDependentServices = NULL;
DWORD cServices = 0;
DWORD i;
// Check args
DBG_ASSERT( ( pcServices != NULL ) && ( pbufDependentServices != NULL ) );
if ( ( pcServices == NULL ) || ( pbufDependentServices == NULL ) )
{
hr = E_INVALIDARG;
goto exit;
}
// Init
*pcServices = 0;
// Open SCM
schSCM = OpenSCManager( NULL,
NULL,
SC_MANAGER_ALL_ACCESS );
if ( schSCM == NULL )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// Open IISADMIN
schIISADMIN = OpenService( schSCM,
"IISADMIN",
STANDARD_RIGHTS_REQUIRED | SERVICE_ENUMERATE_DEPENDENTS );
if ( schIISADMIN == NULL )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
pessDependentServices = (ENUM_SERVICE_STATUS*)pbufDependentServices->QueryPtr();
DBG_ASSERT( pessDependentServices );
fRet = EnumDependentServices( schIISADMIN,
SERVICE_ACTIVE,
pessDependentServices,
pbufDependentServices->QuerySize(),
&dwBytesNeeded,
&cServices );
// The buffer is too small?
if ( !fRet && ( GetLastError() == ERROR_MORE_DATA ) )
{
// Resize the buffer
fRet = pbufDependentServices->Resize( dwBytesNeeded );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Retry the call
pessDependentServices = (ENUM_SERVICE_STATUS*)pbufDependentServices->QueryPtr();
DBG_ASSERT( pessDependentServices );
fRet = EnumDependentServices( schIISADMIN,
SERVICE_ACTIVE,
pessDependentServices,
dwBytesNeeded,
&dwBytesNeeded,
&cServices );
}
if ( !fRet )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
//
// Open handles and send service control stop command
//
for ( i = 0; i < cServices; i++ )
{
if ( ( pessDependentServices[i].ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING ) ||
( pessDependentServices[i].ServiceStatus.dwCurrentState == SERVICE_STOPPED ) )
{
continue;
}
schDependent = OpenService( schSCM,
pessDependentServices[i].lpServiceName,
SERVICE_ALL_ACCESS );
if ( schDependent == NULL )
{
continue;
}
//Stop Service
ControlService( schDependent, SERVICE_CONTROL_STOP, &ssDependent );
WaitForServiceStatus( schDependent, SERVICE_STOPPED );
CloseServiceHandle( schDependent );
schDependent = NULL;
}
// Return
*pcServices = cServices;
exit:
if ( schSCM != NULL )
{
CloseServiceHandle( schSCM );
schSCM = NULL;
}
if ( schIISADMIN != NULL )
{
CloseServiceHandle( schIISADMIN );
schIISADMIN = NULL;
}
if ( schDependent != NULL )
{
CloseServiceHandle( schDependent );
schDependent = NULL;
}
return hr;
}
HRESULT
CADMCOMW::StartDependentServices(
DWORD cServices,
ENUM_SERVICE_STATUS *pessDependentServices)
{
// Locals
HRESULT hr = S_OK;
DWORD dwError;
DWORD i = 0;
SC_HANDLE schSCM = NULL;
SC_HANDLE schDependent = NULL;
// Check agrs
if ( cServices == 0 )
{
// Nop
goto exit;
}
DBG_ASSERT( pessDependentServices != NULL );
if ( pessDependentServices == NULL )
{
hr = E_INVALIDARG;
goto exit;
}
// Open SCM
schSCM = OpenSCManager( NULL,
NULL,
SC_MANAGER_ALL_ACCESS );
if ( schSCM == NULL )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
//
// Open handles and start services
// Use reverse order, since EnumServices orders
// list by dependencies
//
for ( i = 0; i < cServices; i++ )
{
if ( ( pessDependentServices[cServices-1-i].ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING ) ||
( pessDependentServices[cServices-1-i].ServiceStatus.dwCurrentState == SERVICE_STOPPED ) )
{
continue;
}
schDependent = OpenService( schSCM,
pessDependentServices[cServices-1-i].lpServiceName,
SERVICE_ALL_ACCESS );
if ( schDependent == NULL )
{
continue;
}
//Start Service
StartService( schDependent, 0, NULL );
WaitForServiceStatus( schDependent, SERVICE_RUNNING );
CloseServiceHandle( schDependent );
schDependent = NULL;
}
exit:
if ( schSCM != NULL )
{
CloseServiceHandle( schSCM );
schSCM = NULL;
}
if ( schDependent != NULL )
{
CloseServiceHandle( schDependent );
schDependent = NULL;
}
return hr;
}
HRESULT
CADMCOMW::SetHistoryAndEWR(
DWORD dwEnableHistory,
DWORD dwEnableEWR)
{
// Locals
HRESULT hr = S_OK;
METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE;
METADATA_RECORD mdrHistory = {
MD_ROOT_ENABLE_HISTORY,
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER,
DWORD_METADATA,
sizeof(DWORD),
(BYTE*)&dwEnableHistory,
0
};
METADATA_RECORD mdrEWR = {
MD_ROOT_ENABLE_EDIT_WHILE_RUNNING,
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER,
DWORD_METADATA,
sizeof(DWORD),
(BYTE*)&dwEnableEWR,
0
};
// Open the the root for writting
hr = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE,
L"/LM",
METADATA_PERMISSION_WRITE,
DEFAULT_SAVE_TIMEOUT,
&mdhRoot );
if ( FAILED( hr ) )
{
goto exit;
}
// Set EWR 1st
hr = m_pMdObject->ComMDSetMetaDataW( mdhRoot,
L"",
&mdrEWR );
if ( FAILED( hr ) )
{
goto exit;
}
// Set History after EWR
hr = m_pMdObject->ComMDSetMetaDataW( mdhRoot,
L"",
&mdrHistory );
if ( FAILED( hr ) )
{
goto exit;
}
// Close the write handle to allow saving the metabase
DBG_ASSERT( mdhRoot != METADATA_MASTER_ROOT_HANDLE );
m_pMdObject->ComMDCloseMetaObject( mdhRoot );
mdhRoot = METADATA_MASTER_ROOT_HANDLE;
// Save the metabase
hr = m_pMdObject->ComMDSaveData();
if ( FAILED( hr ) )
{
goto exit;
}
exit:
if ( mdhRoot != METADATA_MASTER_ROOT_HANDLE )
{
m_pMdObject->ComMDCloseMetaObject( mdhRoot );
mdhRoot = METADATA_MASTER_ROOT_HANDLE;
}
return hr;
}
HRESULT
CADMCOMW::DisableHistory(
DWORD *pdwEnableHistoryOld,
DWORD *pdwEnableEWROld)
{
// Locals
HRESULT hr = S_OK;
DWORD dwEnableHistory = FALSE;
DWORD dwEnableEWR = FALSE;
DWORD dwT;
METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE;
METADATA_RECORD mdrHistory = {
MD_ROOT_ENABLE_HISTORY,
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER,
DWORD_METADATA,
sizeof(DWORD),
(BYTE*)&dwEnableHistory,
0
};
METADATA_RECORD mdrEWR = {
MD_ROOT_ENABLE_EDIT_WHILE_RUNNING,
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER,
DWORD_METADATA,
sizeof(DWORD),
(BYTE*)&dwEnableEWR,
0
};
// Check agrs
if ( ( pdwEnableHistoryOld == NULL ) || ( pdwEnableEWROld == NULL ) )
{
hr = E_INVALIDARG;
goto exit;
}
// Intialize
*pdwEnableHistoryOld = FALSE;
*pdwEnableEWROld = FALSE;
hr = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE,
L"/LM",
METADATA_PERMISSION_READ,
DEFAULT_SAVE_TIMEOUT,
&mdhRoot );
if ( hr == HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) )
{
// If /LM is not there don't fail.
// Treat as if EWR and History are not enabled (*pdwEnableHistoryOld and *pdwEnableEWROld are initialized to FALSE)
// This can happen only during sysprep.
hr = S_OK;
goto exit;
}
if ( FAILED( hr ) )
{
goto exit;
}
// Get EWR
hr = m_pMdObject->ComMDGetMetaDataW( mdhRoot,
L"",
&mdrEWR,
&dwT );
if ( hr == MD_ERROR_DATA_NOT_FOUND )
{
// If the property is not there don't fail.
// Treat as if EWR is not enabled (dwEnableEWR is initialized to FALSE)
hr = S_OK;
}
if ( FAILED( hr ) )
{
goto exit;
}
// Get History
hr = m_pMdObject->ComMDGetMetaDataW( mdhRoot,
L"",
&mdrHistory,
&dwT );
if ( hr == MD_ERROR_DATA_NOT_FOUND )
{
// If the property is not there don't fail.
// Treat as if History is not enabled (dwEnableHistory is initialized to FALSE)
hr = S_OK;
}
if ( FAILED( hr ) )
{
goto exit;
}
// If both are already disabled
if ( !dwEnableHistory && !dwEnableEWR )
{
// No need to change anything in the metabase.
// No need to change the out parameters,
// because they are already initialized to FALSE
// Just exit.
goto exit;
}
// Close the read handle to allow writing in SetHistoryAndEWR
DBG_ASSERT( mdhRoot != METADATA_MASTER_ROOT_HANDLE );
m_pMdObject->ComMDCloseMetaObject( mdhRoot );
mdhRoot = METADATA_MASTER_ROOT_HANDLE;
// Disable history and ewr
// They have to be changed in pairs, because EWR turns history on
hr = SetHistoryAndEWR( FALSE, FALSE );
if ( FAILED( hr ) )
{
goto exit;
}
// Return
*pdwEnableHistoryOld = dwEnableHistory;
*pdwEnableEWROld = dwEnableEWR;
exit:
if ( mdhRoot != METADATA_MASTER_ROOT_HANDLE )
{
m_pMdObject->ComMDCloseMetaObject( mdhRoot );
mdhRoot = METADATA_MASTER_ROOT_HANDLE;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::Backup(
LPCWSTR pszMDBackupLocation,
DWORD dwMDVersion,
DWORD dwMDFlags)
{
return BackupHelper( pszMDBackupLocation,
dwMDVersion,
dwMDFlags );
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::BackupWithPasswd(
LPCWSTR pszMDBackupLocation,
DWORD dwMDVersion,
DWORD dwMDFlags,
LPCWSTR pszPasswd)
{
return BackupHelper( pszMDBackupLocation,
dwMDVersion,
dwMDFlags,
pszPasswd );
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::Restore(
LPCWSTR pszMDBackupLocation,
DWORD dwMDVersion,
DWORD dwMDFlags)
{
return RestoreHelper( pszMDBackupLocation,
dwMDVersion,
0,
NULL,
dwMDFlags,
RESTORE_BACKUP );
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::RestoreWithPasswd(
LPCWSTR pszMDBackupLocation,
DWORD dwMDVersion,
DWORD dwMDFlags,
LPCWSTR pszPasswd)
{
return RestoreHelper( pszMDBackupLocation,
dwMDVersion,
0,
pszPasswd,
dwMDFlags,
RESTORE_BACKUP );
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::EnumBackups(
LPWSTR pszMDBackupLocation,
DWORD *pdwMDVersion,
PFILETIME pftMDBackupTime,
DWORD dwMDEnumIndex)
{
HRESULT hresReturn = S_OK;
if ( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
&g_ohMasterRootHandle ) )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::EnumBackups AdminAclAccessCheck failed, error %lx\n",
GetLastError() ));
hresReturn = RETURNCODETOHRESULT( GetLastError() );
}
else
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDEnumBackupsW(pszMDBackupLocation,
pdwMDVersion,
pftMDBackupTime,
dwMDEnumIndex);
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::DeleteBackup(
LPCWSTR pszMDBackupLocation,
DWORD dwMDVersion)
{
HRESULT hresReturn = S_OK;
if ( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
&g_ohMasterRootHandle ) )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::DeleteBackup] AdminAclAccessCheck failed, error %lx\n",
GetLastError() ));
hresReturn = RETURNCODETOHRESULT( GetLastError() );
}
else
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDDeleteBackupW(pszMDBackupLocation,
dwMDVersion);
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::Export(
LPCWSTR i_wszPasswd,
LPCWSTR i_wszFileName,
LPCWSTR i_wszSourcePath,
DWORD i_dwMDFlags)
{
HRESULT hr = S_OK;
DWORD dwError;
METADATA_HANDLE mdh = METADATA_MASTER_ROOT_HANDLE;
METADATA_HANDLE mdhActual = METADATA_MASTER_ROOT_HANDLE;
COpenHandle* pohActual = NULL;
STRAU strFileName;
//
// parameter validation
//
if ( ( i_wszFileName == NULL ) || ( *i_wszFileName == L'\0' ) ||
( i_wszSourcePath == NULL ) )
{
hr = E_INVALIDARG;
goto exit;
}
hr = CoImpersonateClient();
if ( FAILED( hr ) )
{
goto exit;
}
// IVANPASH 598894 (SCR)
// Restrict the access to Export only to administrators
if ( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
&g_ohMasterRootHandle ) )
{
dwError = GetLastError();
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::Export] AdminAclAccessCheck failed, error %lx\n",
dwError ));
hr = RETURNCODETOHRESULT( dwError );
goto exit;
}
hr = OpenKeyHelper( METADATA_MASTER_ROOT_HANDLE,
i_wszSourcePath,
METADATA_PERMISSION_READ,
DEFAULT_SAVE_TIMEOUT,
&mdh);
if ( FAILED( hr ) )
{
goto exit;
}
//
// pohActual refCount = 2 after Lookup.
//
dwError = Lookup(mdh, &mdhActual, &pohActual);
if ( dwError != ERROR_SUCCESS )
{
hr = RETURNCODETOHRESULT( dwError );
DBG_ASSERT( pohActual == NULL );
//
// Yes, an open key does not get closed, but Lookup really should
// not fail if mdh is a valid key.
// Also CloseKey would do exactly the same Lookup call and will fail too.
//
goto exit;
}
//
// Move refCount down to 1.
//
pohActual->Release(this);
if( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
mdh,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
pohActual ) )
{
dwError = GetLastError();
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::Export] AdminAclAccessCheck failed, error %lx\n",
dwError ));
hr = RETURNCODETOHRESULT( dwError );
goto exit;
}
// IVANPASH 598894 (SCR)
// Prepend the file name with \\?\ (or \\?\UNC\) to prevent canonicalization
hr = MakePathCanonicalizationProof( i_wszFileName, FALSE, &strFileName );
if ( FAILED( hr ) )
{
goto exit;
}
// Don't use i_wszFileName any more
i_wszFileName = NULL;
// call metadata com api
hr = m_pMdObject->ComMDExportW( mdhActual,
i_wszPasswd,
strFileName.QueryStrW(),
i_wszSourcePath,
i_dwMDFlags);
if ( FAILED( hr ) )
{
goto exit;
}
exit:
// At this moment mdh can actually contain a valid CMDCOMW handle.
// It is not explicitely closed, because the code bellow actually does
// the same as CloseKey(mdh).
if ( pohActual )
{
// close key
if ( mdhActual != METADATA_MASTER_ROOT_HANDLE )
{
// call metadata com api
m_pMdObject->ComMDCloseMetaObject( mdhActual );
}
// Remove node from handle table
pohActual->Release(this);
pohActual=NULL;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::Import(
LPCWSTR i_wszPasswd,
LPCWSTR i_wszFileName,
LPCWSTR i_wszSourcePath,
LPCWSTR i_wszDestPath,
DWORD i_dwMDFlags)
/*++
Synopsis:
Arguments: [i_wszPasswd] -
[i_wszFileName] -
[i_wszSourcePath] - Absolute metabase path
[i_wszDestPath] - Absolute metabase path
[i_dwMDFlags] -
Return Value:
--*/
{
HRESULT hr = S_OK;
DWORD dwError;
METADATA_HANDLE mdh = 0;
METADATA_HANDLE mdhActual = 0;
COpenHandle* pohActual = NULL;
LPWSTR wszDeepest = NULL;
LONG cchDeepest = 0;
LPWSTR wszEnd = NULL;
LONG idx = 0;
WCHAR wszKeyType[METADATA_MAX_STRING_LEN] = {0};
DWORD dwRequiredSize = 0;
METADATA_RECORD mr =
{
MD_KEY_TYPE,
METADATA_NO_ATTRIBUTES,
IIS_MD_UT_SERVER,
STRING_METADATA,
METADATA_MAX_STRING_LEN*sizeof(WCHAR),
(LPBYTE)wszKeyType,
0
};
STRAU strFileName;
//
// parameter validation
//
if ( ( i_wszFileName == NULL ) || ( *i_wszFileName == L'\0' ) ||
( i_wszSourcePath == NULL ) ||
( i_wszDestPath == NULL ) )
{
hr = E_INVALIDARG;
goto exit;
}
if (i_wszPasswd == NULL)
{
i_wszPasswd = L"";
}
hr = CoImpersonateClient();
if ( FAILED( hr ) )
{
goto exit;
}
// IVANPASH 598894 (SCR)
// Restrict the access to Import only to administrators
if ( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
&g_ohMasterRootHandle ) )
{
dwError = GetLastError();
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::Import] AdminAclAccessCheck failed, error %lx\n",
dwError ));
hr = RETURNCODETOHRESULT( dwError );
goto exit;
}
//
// Copy i_wszDestPath to wszDeepest
// Remove trailing slashes
//
cchDeepest = (LONG)wcslen(i_wszDestPath);
wszDeepest = new WCHAR[1+cchDeepest];
if(!wszDeepest)
{
hr = RETURNCODETOHRESULT(ERROR_NOT_ENOUGH_MEMORY);
goto exit;
}
memcpy(wszDeepest, i_wszDestPath, sizeof(WCHAR)*(cchDeepest+1));
while( cchDeepest > 0 && IS_MD_PATH_DELIM(wszDeepest[cchDeepest-1]) )
{
cchDeepest--;
}
//
// Open the deepest level key possible
//
wszEnd = wszDeepest + cchDeepest;
for(idx = cchDeepest; idx >= 0; idx--)
{
if(idx == 0 || idx == cchDeepest || IS_MD_PATH_DELIM(*wszEnd))
{
*wszEnd = L'\0';
hr = OpenKeyHelper(
METADATA_MASTER_ROOT_HANDLE,
wszDeepest,
METADATA_PERMISSION_WRITE | METADATA_PERMISSION_READ,
DEFAULT_SAVE_TIMEOUT,
&mdh);
if( FAILED(hr) &&
hr != RETURNCODETOHRESULT(ERROR_PATH_NOT_FOUND) )
{
goto exit;
}
else if(SUCCEEDED(hr))
{
break;
}
}
wszEnd--;
}
if(FAILED(hr))
{
goto exit;
}
//
// If we are here, we now have an Open metabase handle
//
dwError = Lookup(mdh, &mdhActual, &pohActual);
hr = RETURNCODETOHRESULT(dwError);
if(FAILED(hr))
{
//
// Yes, an open key does not get closed, but Lookup really should
// not fail if mdh is a valid key.
// Also CloseKey would do exactly the same Lookup call and will fail too.
//
goto exit;
}
pohActual->Release(this); // Decrements refcount from 2 to 1.
if( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
mdh,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE | METADATA_PERMISSION_READ,
pohActual ) )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::Import] AdminAclAccessCheck failed, error %lx\n",
GetLastError() ));
hr = RETURNCODETOHRESULT( GetLastError() );
goto exit;
}
//
// Get the keytype
// If the node does not exist, or node exists but keytype doesn't, we
// will not set wszKeytype and hence ComMDImport will not attempt to match
// the source and dest keytype
//
hr = m_pMdObject->ComMDGetMetaDataW(
mdhActual,
i_wszDestPath+idx,
&mr,
&dwRequiredSize);
if(hr == RETURNCODETOHRESULT(ERROR_PATH_NOT_FOUND))
{
hr = S_OK;
}
else if(hr == MD_ERROR_DATA_NOT_FOUND)
{
hr = S_OK;
}
if(FAILED(hr))
{
DBGPRINTF((DBG_CONTEXT, "Error trying to retrieve keytype for %ws\n", i_wszDestPath+idx));
goto exit;
}
// IVANPASH 598894 (SCR)
// Prepend the file name with \\?\ (or \\?\UNC\) to prevent canonicalization
hr = MakePathCanonicalizationProof( i_wszFileName, TRUE, &strFileName );
if ( FAILED( hr ) )
{
goto exit;
}
// Don't use i_wszFileName any more
i_wszFileName = NULL;
//
// Call Import
//
hr = m_pMdObject->ComMDImportW(
mdhActual,
i_wszDestPath+idx,
wszKeyType,
i_wszPasswd,
strFileName.QueryStrW(),
i_wszSourcePath,
i_dwMDFlags);
if(FAILED(hr))
{
goto exit;
}
exit:
if(pohActual != NULL)
{
//
// Close Key
//
m_pMdObject->ComMDCloseMetaObject( mdhActual );
pohActual->Release(this);
pohActual = NULL;
}
if ( wszDeepest )
{
delete [] wszDeepest;
wszDeepest = NULL;
}
return hr;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::RestoreHistory(
LPCWSTR pszMDHistoryLocation,
DWORD dwMDMajorVersion,
DWORD dwMDMinorVersion,
DWORD dwMDFlags)
{
HRESULT hresReturn = S_OK;
if( (dwMDFlags & ~MD_HISTORY_LATEST) != 0 &&
dwMDFlags != 0 )
{
return HRESULT_FROM_WIN32(ERROR_INVALID_FLAGS);
}
if( (dwMDFlags & MD_HISTORY_LATEST) &&
(dwMDMajorVersion != 0 || dwMDMinorVersion != 0) )
{
return E_INVALIDARG;
}
//
// parameter validation done in here.
//
hresReturn = RestoreHelper(pszMDHistoryLocation,
dwMDMajorVersion,
dwMDMinorVersion,
NULL,
dwMDFlags,
RESTORE_HISTORY);
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::EnumHistory(
LPWSTR io_wszMDHistoryLocation,
DWORD *o_pdwMDMajorVersion,
DWORD *o_pdwMDMinorVersion,
PFILETIME o_pftMDHistoryTime,
DWORD i_dwMDEnumIndex)
{
HRESULT hresReturn = S_OK;
if (io_wszMDHistoryLocation == NULL ||
o_pdwMDMajorVersion == NULL ||
o_pdwMDMinorVersion == NULL ||
o_pftMDHistoryTime == NULL)
{
return E_INVALIDARG;
}
if ( !AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
L"",
MD_ADMIN_ACL,
METADATA_PERMISSION_WRITE,
&g_ohMasterRootHandle ) )
{
DBGPRINTF(( DBG_CONTEXT,
"[CADMCOMW::EnumHistory AdminAclAccessCheck failed, error %lx\n",
GetLastError() ));
hresReturn = RETURNCODETOHRESULT( GetLastError() );
}
else
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDEnumHistoryW(io_wszMDHistoryLocation,
o_pdwMDMajorVersion,
o_pdwMDMinorVersion,
o_pftMDHistoryTime,
i_dwMDEnumIndex);
}
return hresReturn;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetChildPaths(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD cchMDBufferSize,
WCHAR *pszBuffer,
DWORD *pcchMDRequiredBufferSize)
/*++
Routine Description:
Retrieves all child keys of a given path from a given handle
Arguments:
hMDHandle - open handle
pszMDPath - path of the meta object with which this data is associated
cchMDBufferSize - sizeof buffer passed in, in wchars
pszBuffer - buffer, allocated by caller, that result is placed into
pcchMDRequiredBufferSize - required size, filled in only if buffer is insufficient
Return Value:
Status. s_ok on success.
--*/
{
HRESULT hr = S_OK;
METADATA_HANDLE hActualHandle;
BOOL fSecure;
//
// lookup and access check
//
hr = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_READ,
&fSecure);
if (FAILED(hr))
{
goto done;
}
DBG_ASSERT( NULL != m_pMdObject3 );
hr = m_pMdObject3->ComMDGetChildPathsW(hActualHandle,
pszMDPath,
cchMDBufferSize,
pszBuffer,
pcchMDRequiredBufferSize);
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
return hr;
}
HRESULT
CADMCOMW::AddKeyHelper(
IN METADATA_HANDLE hMDHandle,
IN LPCWSTR pszMDPath)
/*++
Routine Description:
Add meta object and adds it to the list of child objects for the object
specified by Path.
Arguments:
hMDHandle - open handle
pszMDPath - path of the object to be added
Return Value:
Status.
--*/
{
HRESULT hresReturn = S_OK;
METADATA_HANDLE hActualHandle;
if ((pszMDPath == NULL) ||
(*pszMDPath == (WCHAR)'\0'))
{
hresReturn = E_INVALIDARG;
}
else
{
//
// lookup and access check
//
hresReturn = LookupAndAccessCheck(hMDHandle,
&hActualHandle,
pszMDPath,
0,
METADATA_PERMISSION_WRITE);
if (SUCCEEDED(hresReturn))
{
//
// call metadata com api
//
hresReturn = m_pMdObject->ComMDAddMetaObjectW( hActualHandle,
pszMDPath );
}
}
return hresReturn;
}
HRESULT
CADMCOMW::OpenKeyHelper(
METADATA_HANDLE hMDHandle,
LPCWSTR pszMDPath,
DWORD dwMDAccessRequested,
DWORD dwMDTimeOut,
PMETADATA_HANDLE phMDNewHandle)
/*++
Routine Description:
Opens a meta object for read and/or write access.
- This is used by Export.
Arguments:
hMDHandle - open handle
pszMDPath - path of the object to be opened
dwMDAccessRequested - permissions requested
dwMDTimeOut - time to block waiting for open to succeed, in miliseconds.
phMDNewHandle - handle to be passed to other MD routines
Return Value:
Status.
--*/
{
HRESULT hr = S_OK;
DWORD dwError;
BOOL fRet;
METADATA_HANDLE hNewHandle = METADATA_MASTER_ROOT_HANDLE;
METADATA_HANDLE hActualHandle = METADATA_MASTER_ROOT_HANDLE;
COpenHandle *pohParent = NULL;
// Check args
if ( ( phMDNewHandle == NULL ) ||
( ( dwMDAccessRequested & ( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) == 0 ) ||
( ( dwMDAccessRequested & ~( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) != 0 ) ||
( ( pszMDPath != NULL ) && ( wcsstr( pszMDPath, L"<nsepm>" ) != NULL ) ) )
{
//
// <nsepm> used to be magic name for the Name Space Extension access
// It was removed for IIS6 but to prevent legacy applications
// to write obsolete and unusable data to metabase deny access to path
// containing <nsepm>
//
hr = E_INVALIDARG;
goto exit;
}
//
// Map Admin Handle to Actual Handle
//
//
// This Addrefs pohParent, which makes sure it doesn't do away
// pohParent is needed by AddNode
//
dwError = Lookup( hMDHandle,
&hActualHandle,
&pohParent );
if( dwError != ERROR_SUCCESS )
{
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// Check access
fRet = AdminAclAccessCheck( m_pMdObject,
this,
hMDHandle,
pszMDPath,
0,
dwMDAccessRequested,
pohParent );
if ( !fRet )
{
dwError = GetLastError();
if ( dwError != ERROR_ACCESS_DENIED )
{
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// If failed with access denied for writing or writing+reading
if ( ( dwMDAccessRequested & METADATA_PERMISSION_WRITE ) != 0 )
{
// Retry checking access for writing MD_ADMIN_ACL
// AdminAclAccessCheck will try 1st to check MD_ACR_RESTRICTED_WRITE and than for MD_ACR_WRITE_DAC
fRet = AdminAclAccessCheck( m_pMdObject,
this,
hMDHandle,
pszMDPath,
MD_ADMIN_ACL,
dwMDAccessRequested,
pohParent );
}
else
{
// If failed with access denied for reading (and the write bit is not set)
if ( dwMDAccessRequested == METADATA_PERMISSION_READ )
{
// Retry checking access for enum only
// AdminAclAccessCheck internally already check for both MD_ACR_UNSECURE_PROPS_READ and MD_ACR_READ
fRet = AdminAclAccessCheck( m_pMdObject,
this,
hMDHandle,
pszMDPath,
AAC_ENUM_KEYS,
dwMDAccessRequested,
pohParent );
}
}
}
if ( !fRet )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
//
// call metadata com api
//
hr = m_pMdObject->ComMDOpenMetaObjectW( hActualHandle,
pszMDPath,
dwMDAccessRequested,
dwMDTimeOut,
&hNewHandle );
if( FAILED( hr ) )
{
goto exit;
}
// Add the opened handle to table
hr = AddNode( hNewHandle,
pohParent,
phMDNewHandle,
pszMDPath );
if( FAILED( hr ) )
{
goto exit;
}
// Don't close the metadata handle
hNewHandle = METADATA_MASTER_ROOT_HANDLE;
exit:
if ( hNewHandle != METADATA_MASTER_ROOT_HANDLE )
{
m_pMdObject->ComMDCloseMetaObject( hNewHandle );
hNewHandle = METADATA_MASTER_ROOT_HANDLE;
}
if ( pohParent != NULL )
{
pohParent->Release(this);
pohParent = NULL;
}
return hr;
}
DWORD
CADMCOMW::Lookup(
IN METADATA_HANDLE hHandle,
OUT METADATA_HANDLE *phActualHandle,
OUT COpenHandle **ppohHandle)
{
HANDLE_TABLE *phtNode;
DWORD dwReturn = ERROR_INVALID_HANDLE;
if( hHandle == METADATA_MASTER_ROOT_HANDLE )
{
*phActualHandle = g_MasterRoot.hActualHandle;
if (ppohHandle != NULL)
{
*ppohHandle = g_MasterRoot.pohHandle;
(*ppohHandle)->AddRef();
}
dwReturn = ERROR_SUCCESS;
}
else
{
m_LockHandleResource.ReadLock();
for( phtNode = m_hashtab[(DWORD)hHandle % HASHSIZE]; phtNode != NULL;
phtNode = phtNode->next )
{
if( phtNode->hAdminHandle == hHandle )
{
*phActualHandle = phtNode->hActualHandle;
if (ppohHandle != NULL)
{
*ppohHandle = phtNode->pohHandle;
(*ppohHandle)->AddRef();
}
dwReturn = ERROR_SUCCESS;
break;
}
}
m_LockHandleResource.ReadUnlock();
}
return dwReturn;
}
VOID
CADMCOMW::DisableAllHandles( )
{
HANDLE_TABLE *phtNode;
DWORD i;
//
// At this point, all metadata handles should be closed because a retore
// just happened. So don't need to close these handles.
//
//
// Can't just delete them, becuase of syncronization problems
// with CloseKey and Lookup. Set the hande to an invalid value
// So Lookup won't use them.
//
m_LockHandleResource.WriteLock();
for( i = 0; i < HASHSIZE; i++ )
{
for( phtNode = m_hashtab[i]; phtNode != NULL; phtNode = phtNode->next )
{
phtNode->hAdminHandle = INVALID_ADMINHANDLE_VALUE;
}
}
m_LockHandleResource.WriteUnlock();
}
HRESULT
CADMCOMW::LookupAndAccessCheck(
IN METADATA_HANDLE hHandle,
OUT METADATA_HANDLE *phActualHandle,
IN LPCWSTR pszPath,
IN DWORD dwId, // check for MD_ADMIN_ACL, must have special right to write them
IN DWORD dwAccess, // METADATA_PERMISSION_*
OUT LPBOOL pfEnableSecureAccess)
{
DWORD dwReturn = ERROR_SUCCESS;
COpenHandle *pohParent;
//
// Map Admin Handle to Actual Handle
//
//
// This Addrefs pohParent, which makes sure it doesn't go away
// until AdminAclAccessCheck is done
//
dwReturn = Lookup( hHandle,
phActualHandle,
&pohParent);
if (dwReturn == ERROR_SUCCESS)
{
if (!AdminAclAccessCheck(m_pMdObject,
(LPVOID)this,
hHandle,
pszPath,
dwId,
dwAccess,
pohParent,
pfEnableSecureAccess))
{
dwReturn = GetLastError();
}
pohParent->Release(this);
}
return RETURNCODETOHRESULT(dwReturn);
}
DWORD
CADMCOMW::LookupActualHandle(
IN METADATA_HANDLE hHandle)
{
HANDLE_TABLE *phtNode;
DWORD i;
DWORD dwReturn = ERROR_INVALID_HANDLE;
m_LockHandleResource.ReadLock();
for( i = 0; (i < HASHSIZE) && (dwReturn != ERROR_SUCCESS); i++ )
{
for( phtNode = m_hashtab[i]; (phtNode != NULL) && (dwReturn != ERROR_SUCCESS); phtNode = phtNode->next )
{
if( phtNode->hActualHandle == hHandle )
{
dwReturn = ERROR_SUCCESS;
}
}
}
m_LockHandleResource.ReadUnlock();
return dwReturn;
}
HRESULT
CADMCOMW::AddNode(
METADATA_HANDLE hActualHandle,
COpenHandle *pohParentHandle,
PMETADATA_HANDLE phAdminHandle,
LPCWSTR pszPath)
{
HRESULT hresReturn = S_OK;
HANDLE_TABLE *phtNode = (HANDLE_TABLE *)LocalAlloc(LMEM_FIXED, sizeof(*phtNode));
DWORD hashVal;
COpenHandle *pohHandle = new COpenHandle;
if ((phtNode == NULL) ||
(pohHandle == NULL))
{
hresReturn = E_OUTOFMEMORY;
if( phtNode )
{
LocalFree(phtNode);
}
if( pohHandle )
{
delete pohHandle;
}
}
else
{
m_LockHandleResource.WriteLock();
hresReturn = pohHandle->Init( m_dwHandleValue,
pszPath,
pohParentHandle->GetPath() );
if (FAILED(hresReturn))
{
LocalFree(phtNode);
delete pohHandle;
}
else
{
phtNode->pohHandle = pohHandle;
phtNode->hAdminHandle = m_dwHandleValue;
*phAdminHandle = m_dwHandleValue++;
phtNode->hActualHandle = hActualHandle;
hashVal = (phtNode->hAdminHandle) % HASHSIZE;
phtNode->next = m_hashtab[hashVal];
m_hashtab[hashVal] = phtNode;
}
m_LockHandleResource.WriteUnlock();
}
return hresReturn;
}
DWORD
CADMCOMW::DeleteNode(
METADATA_HANDLE hHandle)
{
HANDLE_TABLE *phtNode;
HANDLE_TABLE *phtDelNode;
DWORD HashValue = (DWORD)hHandle % HASHSIZE;
if( hHandle == METADATA_MASTER_ROOT_HANDLE )
{
return ERROR_SUCCESS;
}
m_LockHandleResource.WriteLock();
phtNode = m_hashtab[HashValue];
//
// check single node linked list
//
if( phtNode->hAdminHandle == hHandle )
{
m_hashtab[HashValue] = phtNode->next;
delete phtNode->pohHandle;
LocalFree(phtNode);
}
else
{
for( ; phtNode != NULL; phtNode = phtNode->next )
{
phtDelNode = phtNode->next;
if( phtDelNode != NULL )
{
if( phtDelNode->hAdminHandle == hHandle )
{
phtNode->next = phtDelNode->next;
delete phtDelNode->pohHandle;
LocalFree(phtDelNode);
break;
}
}
}
}
m_LockHandleResource.WriteUnlock();
return ERROR_SUCCESS;
}
//---------------
/*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
Method: CADMCOMW::NotifySinks
Summary: Internal utility method of this COM object used to fire event
notification calls to all listening connection sinks in the
client.
Args: PAPER_EVENT PaperEvent
Type of notification event.
SHORT nX
X cordinate. Value is 0 unless event needs it.
SHORT nY
Y cordinate. Value is 0 unless event needs it.
SHORT nInkWidth
Ink Width. Value is 0 unless event needs it.
SHORT crInkColor
COLORREF RGB color value. Value is 0 unless event needs it.
Modifies: ...
Returns: HRESULT
Standard OLE result code. NOERROR for success.
M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
// Initialize class static members
NOTIFY_CONTEXT * NOTIFY_CONTEXT::s_pCurrentlyWorkingOn = NULL;
CReaderWriterLock3 NOTIFY_CONTEXT::s_LockCurrentlyWorkingOn;
LIST_ENTRY NOTIFY_CONTEXT::s_listEntry;
CRITICAL_SECTION NOTIFY_CONTEXT::s_critSec;
BOOL NOTIFY_CONTEXT::s_fInitializedCritSec = FALSE;
HANDLE NOTIFY_CONTEXT::s_hShutdown = NULL;
HANDLE NOTIFY_CONTEXT::s_hDataAvailable = NULL;
HANDLE NOTIFY_CONTEXT::s_hThread = NULL;
DWORD NOTIFY_CONTEXT::s_dwThreadId = 0;
HRESULT
CADMCOMW::NotifySinks(
METADATA_HANDLE hMDHandle,
DWORD dwMDNumElements,
MD_CHANGE_OBJECT_W pcoChangeList[],
BOOL bIsMainNotification)
{
HRESULT hr = S_OK;
// if the object is terminated, or calling sinks is disabled or IISADMIN is shutting
// down ignore the notification and return S_OK to the caller.
if ( m_bTerminated || sm_fShutdownInProgress )
{
goto done;
}
//
// if the meta handle is for this object, return S_OK to
// the caller (admin's sink).
//
if( bIsMainNotification && ( LookupActualHandle( hMDHandle ) == ERROR_SUCCESS ) )
{
goto done;
}
// Any listeners registered for notifications?
hr = m_ConnectionPoint.ListenersPresent();
if ( hr != S_OK )
{
// We are going to ingore this notification, but return S_OK to the caller.
hr = S_OK;
goto done;
}
// Enqueue the notification, which will AddRef this.
hr = NOTIFY_CONTEXT::CreateNewContext( this,
hMDHandle,
dwMDNumElements,
pcoChangeList,
bIsMainNotification );
if (FAILED(hr))
{
goto done;
}
hr = S_OK;
done:
return hr;
}
NOTIFY_CONTEXT::NOTIFY_CONTEXT() :
_dwSignature(NOTIFY_CONTEXT_SIGNATURE),
_pCADMCOMW(NULL),
_dwMDNumElements(0),
_pcoChangeList(NULL),
_bIsMainNotification(FALSE)
{
InitializeListHead(&_listEntry);
}
NOTIFY_CONTEXT::~NOTIFY_CONTEXT()
{
InitializeListHead(&_listEntry);
_dwSignature = NOTIFY_CONTEXT_SIGNATURE_FREE;
s_LockCurrentlyWorkingOn.ReadLock();
if ( this == s_pCurrentlyWorkingOn )
{
s_LockCurrentlyWorkingOn.ConvertSharedToExclusive();
if ( this == s_pCurrentlyWorkingOn )
{
s_pCurrentlyWorkingOn = NULL;
}
s_LockCurrentlyWorkingOn.WriteUnlock();
}
else
{
s_LockCurrentlyWorkingOn.ReadUnlock();
}
if (_pCADMCOMW)
{
_pCADMCOMW->Release();
_pCADMCOMW = NULL;
}
if (_pcoChangeList)
{
for (DWORD i = 0; i < _dwMDNumElements; i++)
{
delete [] _pcoChangeList[i].pszMDPath;
_pcoChangeList[i].pszMDPath = NULL;
_pcoChangeList[i].dwMDChangeType = 0;
_pcoChangeList[i].dwMDNumDataIDs = 0;
delete [] _pcoChangeList[i].pdwMDDataIDs;
_pcoChangeList[i].pdwMDDataIDs = NULL;
}
}
_dwMDNumElements = 0;
delete [] _pcoChangeList;
_pcoChangeList = NULL;
_bIsMainNotification = FALSE;
}
//static
HRESULT
NOTIFY_CONTEXT::CreateNewContext(
CADMCOMW *pCADMCOMW,
METADATA_HANDLE ,
DWORD dwMDNumElements,
MD_CHANGE_OBJECT_W *pcoChangeList,
BOOL bIsMainNotification)
{
HRESULT hr = S_OK;
BOOL fRet = FALSE;
NOTIFY_CONTEXT *pContext = NULL;
DBG_ASSERT( pCADMCOMW != NULL );
if ( pCADMCOMW == NULL )
{
hr = E_INVALIDARG;
goto done;
}
pContext = new NOTIFY_CONTEXT;
if (NULL == pContext)
{
hr = E_OUTOFMEMORY;
goto done;
}
pContext->_pCADMCOMW = pCADMCOMW;
pContext->_pCADMCOMW->AddRef();
pContext->_dwMDNumElements = dwMDNumElements;
pContext->_bIsMainNotification = bIsMainNotification;
pContext->_pcoChangeList = new MD_CHANGE_OBJECT_W[dwMDNumElements];
if (NULL == pContext->_pcoChangeList)
{
hr = E_OUTOFMEMORY;
goto done;
}
ZeroMemory(pContext->_pcoChangeList, dwMDNumElements * sizeof(MD_CHANGE_OBJECT_W));
for (DWORD i = 0; i < dwMDNumElements; i++)
{
DWORD dwLength = (DWORD)wcslen(pcoChangeList[i].pszMDPath);
pContext->_pcoChangeList[i].pszMDPath = new WCHAR[dwLength + 1];
if (NULL == pContext->_pcoChangeList[i].pszMDPath)
{
hr = E_OUTOFMEMORY;
goto done;
}
wcscpy(pContext->_pcoChangeList[i].pszMDPath, pcoChangeList[i].pszMDPath);
pContext->_pcoChangeList[i].dwMDChangeType = pcoChangeList[i].dwMDChangeType;
pContext->_pcoChangeList[i].dwMDNumDataIDs = pcoChangeList[i].dwMDNumDataIDs;
pContext->_pcoChangeList[i].pdwMDDataIDs = new DWORD[pContext->_pcoChangeList[i].dwMDNumDataIDs];
if (NULL == pContext->_pcoChangeList[i].pdwMDDataIDs)
{
hr = E_OUTOFMEMORY;
goto done;
}
memcpy(pContext->_pcoChangeList[i].pdwMDDataIDs,
pcoChangeList[i].pdwMDDataIDs,
sizeof(DWORD) * pContext->_pcoChangeList[i].dwMDNumDataIDs);
}
EnterCriticalSection(&s_critSec);
InsertTailList(&s_listEntry, &pContext->_listEntry);
LeaveCriticalSection(&s_critSec);
fRet = SetEvent(s_hDataAvailable);
if (FALSE == fRet)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
hr = S_OK;
done:
if (FAILED(hr))
{
delete pContext;
}
return hr;
}
//static
HRESULT
NOTIFY_CONTEXT::Initialize()
{
HRESULT hr = S_OK;
BOOL fRet = FALSE;
s_hShutdown = NULL;
s_hDataAvailable = NULL;
s_fInitializedCritSec = FALSE;
InitializeListHead(&s_listEntry);
fRet = InitializeCriticalSectionAndSpinCount(&s_critSec, 0x80000001);
if (FALSE == fRet)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
s_fInitializedCritSec = TRUE;
s_hShutdown = CreateEvent(NULL, // security descrpitor
TRUE, // manual reset
FALSE, // initial state
NULL); // name
if (NULL == s_hShutdown)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
s_hDataAvailable = CreateEvent(NULL, // security descriptor
FALSE, // auto reset
FALSE, // initial state
NULL); // name
if (NULL == s_hDataAvailable)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
s_hThread = CreateThread(NULL,
0,
NotifyThreadProc,
NULL,
0,
&s_dwThreadId);
if (NULL == s_hThread)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
hr = S_OK;
done:
return hr;
}
//static
VOID
NOTIFY_CONTEXT::RemoveWorkFor(
CADMCOMW *pCADMCOMW,
BOOL fWaitForCurrent)
{
LIST_ENTRY ListToDelete;
LIST_ENTRY *ple;
NOTIFY_CONTEXT *pContext;
LIST_ENTRY *pleNext;
CADMCOMW *pCurrentCADMCOMW;
if ( !s_fInitializedCritSec )
{
return;
}
InitializeListHead( &ListToDelete );
EnterCriticalSection(&s_critSec);
// eat all remaining data
ple = s_listEntry.Flink;
while ( ple != &s_listEntry )
{
pleNext = ple->Flink;
pContext = NOTIFY_CONTEXTFromListEntry( ple );
if ( pContext->_pCADMCOMW == pCADMCOMW )
{
RemoveEntryList( ple );
InitializeListHead( ple );
InsertTailList( &ListToDelete, ple );
}
pContext = NULL;
ple = pleNext;
}
LeaveCriticalSection(&s_critSec);
// Delete all collected notification contexts
ple = ListToDelete.Flink;
while ( ple != &ListToDelete )
{
pleNext = ple->Flink;
RemoveEntryList( ple );
InitializeListHead( ple );
pContext = NOTIFY_CONTEXTFromListEntry( ple );
delete pContext;
pContext = NULL;
ple = pleNext;
}
if ( fWaitForCurrent && ( s_dwThreadId != GetCurrentThreadId() ) )
{
do
{
s_LockCurrentlyWorkingOn.ReadLock();
if ( s_pCurrentlyWorkingOn != NULL )
{
pCurrentCADMCOMW = s_pCurrentlyWorkingOn->_pCADMCOMW;
}
else
{
pCurrentCADMCOMW = NULL;
}
s_LockCurrentlyWorkingOn.ReadUnlock();
// waiting for the NOTIFY_CONTEXT currently being processed to be
// released so we can return and guarantee that no more work is queued.
if ( pCADMCOMW == pCurrentCADMCOMW )
{
Sleep(100);
}
}
while ( pCADMCOMW == pCurrentCADMCOMW );
}
}
//static
VOID
NOTIFY_CONTEXT::RemoveAllWork( VOID )
{
LIST_ENTRY * ple;
NOTIFY_CONTEXT * pContext;
LIST_ENTRY * pleNext;
LIST_ENTRY ListToDelete;
BOOL fShutdownNotifications = FALSE;
if ( !s_fInitializedCritSec )
{
return;
}
InitializeListHead( &ListToDelete );
EnterCriticalSection( &s_critSec );
// Delete all data notifications
ple = s_listEntry.Flink;
while ( ple != &s_listEntry )
{
pleNext = ple->Flink;
pContext = NOTIFY_CONTEXTFromListEntry( ple );
if ( pContext->_bIsMainNotification )
{
RemoveEntryList( ple );
InitializeListHead( ple );
InsertTailList( &ListToDelete, ple );
}
else
{
fShutdownNotifications = TRUE;
}
pContext = NULL;
ple = pleNext;
}
LeaveCriticalSection( &s_critSec );
// Delete all collected notification contexts
ple = ListToDelete.Flink;
while ( ple != &ListToDelete )
{
pleNext = ple->Flink;
RemoveEntryList( ple );
InitializeListHead( ple );
pContext = NOTIFY_CONTEXTFromListEntry( ple );
delete pContext;
pContext = NULL;
ple = pleNext;
}
if ( fShutdownNotifications )
{
Sleep( 1000 );
}
EnterCriticalSection( &s_critSec );
ple = s_listEntry.Flink;
InitializeListHead( &s_listEntry );
LeaveCriticalSection( &s_critSec );
// eat all remaining data
while ( ple != &s_listEntry )
{
pleNext = ple->Flink;
InitializeListHead( ple );
pContext = NOTIFY_CONTEXTFromListEntry(ple);
delete pContext;
pContext = NULL;
ple = pleNext;
}
if ( s_dwThreadId != GetCurrentThreadId() )
{
while( s_pCurrentlyWorkingOn )
{
// waiting for the NOTIFY_CONTEXT currently being processed to be
// released so we can return and guarantee that no more work is queued.
Sleep(100);
}
}
}
//static
VOID
NOTIFY_CONTEXT::Terminate()
{
DWORD dwRet;
DBG_ASSERT(IsListEmpty(&s_listEntry));
if (s_hShutdown)
{
SetEvent(s_hShutdown);
}
if (s_hThread)
{
// need thread to terminate before shutting down more
dwRet = WaitForSingleObject(s_hThread, INFINITE);
DBG_ASSERT(WAIT_OBJECT_0 == dwRet);
CloseHandle(s_hThread);
s_hThread = NULL;
}
if (s_hDataAvailable)
{
CloseHandle(s_hDataAvailable);
s_hDataAvailable = NULL;
}
if (s_hShutdown)
{
CloseHandle(s_hShutdown);
s_hShutdown = NULL;
}
if (s_fInitializedCritSec)
{
DeleteCriticalSection(&s_critSec);
s_fInitializedCritSec = FALSE;
}
}
//static
HRESULT
NOTIFY_CONTEXT::GetNextContext(
NOTIFY_CONTEXT ** ppContext)
{
HRESULT hr = S_OK;
DWORD dwRet;
NOTIFY_CONTEXT * pContext = NULL;
PLIST_ENTRY ple;
HANDLE arrHandles[2];
DBG_ASSERT(ppContext != NULL);
*ppContext = NULL;
EnterCriticalSection(&s_critSec);
if (!IsListEmpty(&s_listEntry))
{
ple = RemoveHeadList(&s_listEntry);
InitializeListHead( ple );
pContext = NOTIFY_CONTEXTFromListEntry(ple);
}
LeaveCriticalSection(&s_critSec);
arrHandles[0] = s_hDataAvailable;
arrHandles[1] = s_hShutdown;
while ( pContext == NULL )
{
dwRet = WaitForMultipleObjects( 2,
arrHandles,
FALSE,
INFINITE);
if (dwRet == WAIT_OBJECT_0)
{
// data was signalled as available
EnterCriticalSection(&s_critSec);
if (!IsListEmpty(&s_listEntry))
{
ple = RemoveHeadList(&s_listEntry);
InitializeListHead( ple );
pContext = NOTIFY_CONTEXTFromListEntry(ple);
}
LeaveCriticalSection(&s_critSec);
}
else if (dwRet == WAIT_OBJECT_0 + 1)
{
hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
goto done;
}
else
{
DBG_ASSERT( ( dwRet == WAIT_OBJECT_0 ) || ( dwRet == WAIT_OBJECT_0+1 ) );
hr = E_UNEXPECTED;
goto done;
}
}
DBG_ASSERT( pContext );
s_LockCurrentlyWorkingOn.WriteLock();
s_pCurrentlyWorkingOn = pContext;
s_LockCurrentlyWorkingOn.WriteUnlock();
*ppContext = pContext;
pContext = NULL;
hr = S_OK;
done:
DBG_ASSERT( pContext == NULL );
return hr;
}
//static
DWORD WINAPI
NOTIFY_CONTEXT::NotifyThreadProc(
LPVOID )
{
HRESULT hr = S_OK;
CADMCOMW *pCADMCOMW;
NOTIFY_CONTEXT *pContext = NULL;
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if (FAILED(hr))
{
// bleh. Nothing to be done then.
return (DWORD)-1;
}
for( ; ; )
{
hr = NOTIFY_CONTEXT::GetNextContext(&pContext);
if (FAILED(hr))
{
goto done;
}
DBG_ASSERT(NULL != pContext);
DBG_ASSERT(NULL != pContext->_pCADMCOMW);
pCADMCOMW = pContext->_pCADMCOMW;
// Keep the object alive during the call to NotifySinksAsync
pCADMCOMW->AddRef();
pCADMCOMW->NotifySinksAsync(
pContext->_dwMDNumElements,
pContext->_pcoChangeList,
pContext->_bIsMainNotification );
delete pContext;
// Release after deleting the context
pCADMCOMW->Release();
pCADMCOMW = NULL;
}
done:
CoUninitialize();
return 0;
}
HRESULT
CADMCOMW::NotifySinksAsync(
DWORD dwMDNumElements,
MD_CHANGE_OBJECT_W pcoChangeList[],
BOOL bIsMainNotification)
{
HRESULT hr = S_OK;
CONNECTDATA *pConnData = NULL;
ULONG cConnData = 0;
ULONG i;
if ( m_bTerminated )
{
goto exit;
}
hr = m_ConnectionPoint.ListenersPresent();
if ( hr != S_OK )
{
goto exit;
}
hr = m_ConnectionPoint.InternalEnumSinks( &pConnData, &cConnData );
if ( FAILED( hr ) || ( cConnData == 0 ) )
{
goto exit;
}
// Loop thru the connection point's connections
// and dispatch the event notification to that sink.
for ( i = 0; i<cConnData; i++ )
{
DBG_ASSERT( pConnData[i].pUnk != NULL );
// Notify the sink
NotifySinkHelper( pConnData[i].pUnk,
dwMDNumElements,
pcoChangeList,
bIsMainNotification );
pConnData[i].pUnk->Release();
pConnData[i].pUnk = NULL;
pConnData[i].dwCookie = 0;
}
exit:
if ( pConnData != NULL )
{
for ( i = 0; i<cConnData; i++ )
{
if ( pConnData[i].pUnk != NULL )
{
pConnData[i].pUnk->Release();
pConnData[i].pUnk = NULL;
}
}
delete [] pConnData;
pConnData = NULL;
}
return hr;
}
HRESULT
CADMCOMW::NotifySinkHelper(
IUnknown *pUnk,
DWORD dwMDNumElements,
MD_CHANGE_OBJECT_W pcoChangeList[],
BOOL bIsMainNotification)
{
// Locals
HRESULT hr = S_OK;
ICallFactory *pCF = NULL;
IMSAdminBaseSinkW *pIADMCOMSINKW_Synchro = NULL;
AsyncIMSAdminBaseSinkW *pIADMCOMSINKW_Async = NULL;
// Check args
DBG_ASSERT( pUnk != NULL );
if ( pUnk == NULL )
{
hr = E_INVALIDARG;
goto exit;
}
// If we are talking to a proxy
if ( m_dwProcessIdCaller != sm_dwProcessIdThis )
{
//
// asynchronous callback
//
// Get the call factory
hr = pUnk->QueryInterface( IID_ICallFactory,
(VOID**)&pCF );
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT, "Failled in to get ICallFactory !!!\n" ));
goto exit;
}
// Create a asynchronous call
hr = pCF->CreateCall( IID_AsyncIMSAdminBaseSink_W,
NULL,
IID_AsyncIMSAdminBaseSink_W,
(IUnknown**)&pIADMCOMSINKW_Async );
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT, "Failled in CreateCall to ICallFactory !!!\n" ));
goto exit;
}
// Set the impersonation level to identify to prevent
// elevation to LocalSystem in the client process.
hr = SetSinkCallbackSecurityBlanket( pIADMCOMSINKW_Async );
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT, "SetSinkCallbackSecurityBlanket failled for the async sink !!!\n" ));
goto exit;
}
if (bIsMainNotification)
{
hr = pIADMCOMSINKW_Async->Begin_SinkNotify( dwMDNumElements,
pcoChangeList );
}
else
{
hr = pIADMCOMSINKW_Async->Begin_ShutdownNotify();
}
}
else
{
// The client is inproc -> synchronous notifications
//
// synchronous callback
//
hr = pUnk->QueryInterface( IID_IMSAdminBaseSink_W,
(VOID**)&pIADMCOMSINKW_Synchro );
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT, "Failled in QueryInterface for IID_IMSAdminBaseSink_W\n" ));
goto exit;
}
if (bIsMainNotification)
{
hr = pIADMCOMSINKW_Synchro->SinkNotify( dwMDNumElements,
pcoChangeList );
}
else
{
hr = pIADMCOMSINKW_Synchro->ShutdownNotify();
}
}
exit:
if ( pIADMCOMSINKW_Synchro != NULL )
{
pIADMCOMSINKW_Synchro->Release();
pIADMCOMSINKW_Synchro = NULL;
}
if ( pIADMCOMSINKW_Async != NULL )
{
pIADMCOMSINKW_Async->Release();
pIADMCOMSINKW_Async = NULL;
}
if ( pCF != NULL )
{
pCF->Release();
pCF = NULL;
}
return hr;
}
//
// Stubs for routine that clients shouldn't be calling anyway.
//
HRESULT
CADMCOMW::KeyExchangePhase1()
{
return E_FAIL;
}
HRESULT
CADMCOMW::KeyExchangePhase2()
{
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::GetServerGuid( void)
{
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE
CADMCOMW::UnmarshalInterface(
IMSAdminBaseW * *piadmbwInterface)
{
AddRef(); // Always return interfaces addref'ed
*piadmbwInterface = (IMSAdminBaseW *)this;
return (S_OK);
}
BOOL
CADMCOMW::CheckGetAttributes(
DWORD dwAttributes)
{
DWORD dwReturn = TRUE;
if ((dwAttributes & METADATA_REFERENCE) ||
((dwAttributes & METADATA_PARTIAL_PATH) &&
!(dwAttributes & METADATA_INHERIT)))
{
dwReturn = FALSE;
}
return dwReturn;
}
VOID
WaitForServiceStatus(
SC_HANDLE schDependent,
DWORD dwDesiredServiceState)
{
DWORD dwSleepTotal = 0;
SERVICE_STATUS ssDependent;
while (dwSleepTotal < MAX_SLEEP)
{
if (QueryServiceStatus(schDependent, &ssDependent))
{
if (ssDependent.dwCurrentState == dwDesiredServiceState)
{
break;
}
else
{
//
// Still pending...
//
Sleep(SLEEP_INTERVAL);
dwSleepTotal += SLEEP_INTERVAL;
}
}
else
{
break;
}
}
}
DWORD
CADMCOMW::IsReadAccessGranted(
METADATA_HANDLE hHandle,
LPWSTR pszPath,
METADATA_RECORD* pmdRecord)
/*++
Routine Description:
Check if read access to property granted based on ACL visible at point in metabase
where property is stored ( as opposed as check made by AdminAclAccessCheck which uses
the ACL visible at path specified during data access )
Arguments:
hHandle - DCOM metabase handle
pszPath - path relative to hHandle
pmdRecord - metadata info to access property
Returns:
ERROR_SUCCESS if access granted, otherwise error code
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
LPWSTR pszPropPath;
//
// If property is not inherited then we already checked the correct ACL
//
if ( !(pmdRecord->dwMDAttributes & METADATA_ISINHERITED) )
{
return dwStatus;
}
// determine from where we got it
// do AccessCheck
if ( (dwStatus = FindClosestProp( hHandle,
pszPath,
&pszPropPath,
pmdRecord->dwMDIdentifier,
pmdRecord->dwMDDataType,
pmdRecord->dwMDUserType,
METADATA_SECURE,
TRUE )) == ERROR_SUCCESS )
{
if ( pszPropPath ) // i.e such a property exist
{
dwStatus = AdminAclAccessCheck( m_pMdObject,
(LPVOID)this,
METADATA_MASTER_ROOT_HANDLE,
pszPropPath,
pmdRecord->dwMDIdentifier,
METADATA_PERMISSION_READ,
&g_ohMasterRootHandle ) ?
ERROR_SUCCESS :
GetLastError();
LocalFree( pszPropPath );
}
else
{
dwStatus = MD_ERROR_DATA_NOT_FOUND;
//
// Should not happen unless handle is master root :
// if we are here then we succeeded accessing data and as we have a read handle
// nobody should be able to delete it
// if master root handle we don't have such protection, so property could
// have been deleted.
//
DBG_ASSERT ( METADATA_MASTER_ROOT_HANDLE == hHandle );
}
}
return dwStatus;
}
DWORD
CADMCOMW::FindClosestProp(
METADATA_HANDLE hHandle,
LPWSTR pszRelPath,
LPWSTR* ppszPropPath,
DWORD dwPropId,
DWORD dwDataType,
DWORD dwUserType,
DWORD dwAttr,
BOOL fSkipCurrentNode)
/*++
Routine Description:
Find the closest path where the specified property exist ( in the direction of
the root ) in metabase
Arguments:
hHandle - DCOM metabase handle
pszRelPath - path relative to hHandle
ppszPropPath - updated with path to property or NULL if property not found
dwPropId - property ID
dwDataType - property data type
dwUserType - property user type
dwAttr - property attribute
fSkipCurrentNode - TRUE to skip current node while scanning for property
Returns:
TRUE if success ( including property not found ), otherwise FALSE
--*/
{
DWORD dwReturn;
LPWSTR pszParentPath;
METADATA_HANDLE hActualHandle;
COpenHandle *pohParent;
HRESULT hRes;
METADATA_RECORD mdRecord;
DWORD dwRequiredLen;
LPWSTR pszPath;
BOOL fFound;
DWORD dwRelPathLen;
DWORD dwParentPathLen;
DWORD dwTotalSize;
dwReturn = Lookup( hHandle,
&hActualHandle,
&pohParent);
if ( dwReturn != ERROR_SUCCESS )
{
return dwReturn;
}
pszParentPath = pohParent->GetPath();
if (pszRelPath == NULL)
{
pszRelPath = L"";
}
DBG_ASSERT(pszParentPath != NULL);
DBG_ASSERT((*pszParentPath == (WCHAR)'\0') ||
ISPATHDELIMW(*pszParentPath));
//
// Strip front slash now, add it in later
//
if (ISPATHDELIMW(*pszRelPath))
{
pszRelPath++;
}
dwRelPathLen = (DWORD)wcslen(pszRelPath);
dwParentPathLen = (DWORD)wcslen(pszParentPath);
DBG_ASSERT((dwParentPathLen == 0) ||
(!ISPATHDELIMW(pszParentPath[dwParentPathLen -1])));
//
// Get rid of trailing slash for good
//
if ((dwRelPathLen > 0) && (ISPATHDELIMW(pszRelPath[dwRelPathLen -1])))
{
dwRelPathLen--;
}
//
// Include space for mid slash if Relpath exists
// Include space for termination
//
dwTotalSize =
(dwRelPathLen + dwParentPathLen + 1 + ((dwRelPathLen > 0) ? 1 : 0)) * sizeof(WCHAR);
*ppszPropPath = pszPath = (LPWSTR)LocalAlloc(LMEM_FIXED, dwTotalSize);
if (pszPath == NULL)
{
dwReturn = GetLastError();
}
else
{
//
// OK to always copy the first part
//
memcpy(pszPath,
pszParentPath,
dwParentPathLen * sizeof(WCHAR));
//
// Don't need slash if there is no RelPath
//
if (dwRelPathLen > 0)
{
pszPath[dwParentPathLen] = (WCHAR)'/';
memcpy(pszPath + dwParentPathLen + 1,
pszRelPath,
dwRelPathLen * sizeof(WCHAR));
}
pszPath[(dwTotalSize / sizeof(WCHAR)) - 1] = (WCHAR)'\0';
//
// Now convert \ to / for string compares
//
LPWSTR pszPathIndex = pszPath;
while ((pszPathIndex = wcschr(pszPathIndex, (WCHAR)'\\')) != NULL)
{
*pszPathIndex = (WCHAR)'/';
}
// scan for property
pszPathIndex = pszPath + wcslen(pszPath);
for ( ; ; )
{
if ( !fSkipCurrentNode )
{
// check prop exist
mdRecord.dwMDIdentifier = dwPropId;
mdRecord.dwMDAttributes = dwAttr;
mdRecord.dwMDUserType = dwUserType;
mdRecord.dwMDDataType = dwDataType;
mdRecord.dwMDDataLen = 0;
mdRecord.pbMDData = NULL;
mdRecord.dwMDDataTag = NULL;
hRes = m_pMdObject->ComMDGetMetaDataW( METADATA_MASTER_ROOT_HANDLE,
pszPath,
&mdRecord,
&dwRequiredLen );
if ( hRes == RETURNCODETOHRESULT(ERROR_INSUFFICIENT_BUFFER) )
{
break;
}
}
// scan backward for delimiter
fFound = FALSE;
if ( pszPathIndex > pszPath )
{
do
{
if ( *--pszPathIndex == L'/' )
{
*pszPathIndex = L'\0';
fFound = TRUE;
break;
}
} while ( pszPathIndex > pszPath );
}
if ( !fFound )
{
// Property not found, return OK status with NULL string
*ppszPropPath = NULL;
LocalFree( pszPath );
break;
}
fSkipCurrentNode = FALSE;
}
}
pohParent->Release( this );
return dwReturn;
}
BOOL
MakeParentPath(
LPWSTR pszPath)
/*++
Routine Description:
Make the path points to parent
Arguments:
pszPath - path to adjust
Returns:
TRUE if success, otherwise FALSE ( no parent )
--*/
{
LPWSTR pszPathIndex = pszPath + wcslen( pszPath );
BOOL fFound = FALSE;
while ( pszPathIndex > pszPath )
{
if ( *--pszPathIndex == L'/' )
{
*pszPathIndex = L'\0';
fFound = TRUE;
break;
}
}
return fFound;
}
HRESULT
CADMCOMW::InitializeCallerWatch(VOID)
/*++
Routine Description:
Sets up watching the caller process.
Arguments:
none
Returns:
HRESULT.
--*/
{
// Locals
HRESULT hr = S_OK;
RPC_STATUS RpcStatus = RPC_S_OK;
HANDLE hProcessCaller = NULL;
HANDLE hWaitCaller = NULL;
HANDLE hToken = NULL;
IServerSecurity *pServerSecurity = NULL;
unsigned int fClientLocal = 0;
DWORD dwProcessId = 0;
BOOL fRet;
// If already initialized
if ( IsCallerWatchInitialized() )
{
// Just exit
goto exit;
}
// Get the COM context for the call
hr = CoGetCallContext( IID_IServerSecurity, (VOID**)&pServerSecurity );
if ( FAILED( hr ) )
{
// Succeess if there is no call context (inproc call).
if ( hr == RPC_E_CALL_COMPLETE )
{
// This is not a failure
hr = S_OK;
// The caller is our process
m_dwProcessIdCaller = sm_dwProcessIdThis;
}
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"CoGetCallContext() failed in InitializeCallerWatch hr=0x%08x.\n",
hr ));
}
// Bail out
goto exit;
}
// Check whether the client is on the same machine
RpcStatus = I_RpcBindingIsClientLocal( NULL, &fClientLocal );
// Not a RPC call?
if ( ( RpcStatus == RPC_S_NO_CALL_ACTIVE ) ||
( RpcStatus == RPC_S_INVALID_BINDING ) )
{
// The caller is our process
m_dwProcessIdCaller = sm_dwProcessIdThis;
goto exit;
}
// Remote client?
if ( ( RpcStatus == RPC_S_OK ) &&
( fClientLocal == 0 ) )
{
// There is no way to watch the caller
m_dwProcessIdCaller = (DWORD)-1;
goto exit;
}
// Failed?
if ( ( RpcStatus != RPC_S_OK ) &&
( RpcStatus != RPC_S_CANNOT_SUPPORT ) )
{
hr = HRESULT_FROM_WIN32( RpcStatus );
DBGPRINTF(( DBG_CONTEXT,
"I_RpcBindingIsClientLocal() failed in InitializeCallerWatch hr=0x%08x.\n",
hr ));
goto exit;
}
// Try to get the caller pid
RpcStatus = I_RpcBindingInqLocalClientPID( NULL, &dwProcessId );
// Not a RPC call?
if ( ( RpcStatus == RPC_S_NO_CALL_ACTIVE ) ||
( RpcStatus == RPC_S_INVALID_BINDING ) )
{
// The caller is our process
m_dwProcessIdCaller = sm_dwProcessIdThis;
goto exit;
}
// Failed?
if ( RpcStatus != RPC_S_OK )
{
hr = HRESULT_FROM_WIN32( RpcStatus );
DBGPRINTF(( DBG_CONTEXT,
"I_RpcBindingInqLocalClientPID() failed in InitializeCallerWatch hr=0x%08x.\n",
hr ));
goto exit;
}
// If the caller is RpcSs
if ( dwProcessId == sm_dwProcessIdRpcSs )
{
// wait for the next call to come
goto exit;
}
// If the caller is in our process
if ( dwProcessId == sm_dwProcessIdThis )
{
// Save the caller pid
m_dwProcessIdCaller = sm_dwProcessIdThis;
// Not need to watch outself
goto exit;
}
// Try to get the thread impersonation token
fRet = OpenThreadToken( GetCurrentThread(),
TOKEN_IMPERSONATE | TOKEN_QUERY,
TRUE,
&hToken );
DBG_ASSERT( !fRet || hToken );
// Check whether the thread was impersonated
if ( fRet && ( hToken != NULL ) )
{
// Revet to LocalSystem
fRet = RevertToSelf();
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"RevertToSelf() failed in InitializeCallerWatch hr=0x%08x.\n",
hr ));
// Since RevertToSelf failed do not try to impersonate
CloseHandle( hToken );
hToken = NULL;
goto exit;
}
}
// Open the process
hProcessCaller = OpenProcess( SYNCHRONIZE,
FALSE,
dwProcessId );
if ( hProcessCaller == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"OpenProcess() failed in InitializeCallerWatch hr=0x%08x.\n",
hr ));
goto exit;
}
// Register wait for the process to end
fRet = RegisterWaitForSingleObject( &hWaitCaller,
hProcessCaller,
CallerWatchWaitOrTimerCallback,
this,
INFINITE,
WT_EXECUTEONLYONCE);
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"RegisterWaitForSingleObject() failed in InitializeCallerWatch hr=0x%08x.\n",
hr ));
goto exit;
}
DBG_ASSERT( hWaitCaller != NULL );
// Save the handles in the object
if ( NULL == InterlockedCompareExchangePointer( &m_hProcessCaller,
hProcessCaller,
NULL ) )
{
m_hWaitCaller = hWaitCaller;
m_dwProcessIdCaller = dwProcessId;
hProcessCaller = NULL;
hWaitCaller = NULL;
}
DBG_ASSERT( m_dwProcessIdCaller == dwProcessId );
exit:
// Cleanup
if ( pServerSecurity )
{
pServerSecurity->Release();
pServerSecurity = NULL;
}
if ( hWaitCaller != NULL)
{
UnregisterWaitEx( hWaitCaller, INVALID_HANDLE_VALUE );
hWaitCaller = NULL;
}
if ( hProcessCaller != NULL )
{
CloseHandle( hProcessCaller );
hProcessCaller = NULL;
}
if ( hToken )
{
fRet = ImpersonateLoggedOnUser( hToken );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"ImpersonateLoggedOnUser() failed in InitializeCallerWatch hr=0x%08x.\n",
hr ));
}
CloseHandle( hToken );
hToken = NULL;
}
DBG_ASSERT( SUCCEEDED( hr ) );
// Done
return hr;
}
HRESULT
CADMCOMW::ShutdownCallerWatch(VOID)
/*++
Routine Description:
Shuts down watching the caller process.
Arguments:
none
Returns:
HRESULT.
--*/
{
// Locals
HRESULT hr = S_OK;
BOOL fRet;
HANDLE hWaitCaller = NULL;
HANDLE hProcessCaller = NULL;
DWORD dwOldThreadId;
DWORD dwThreadId = GetCurrentThreadId();
// Set the thread id
dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect,
(LONG)dwThreadId,
0 );
// Get the wait object
hWaitCaller = InterlockedExchangePointer( &m_hWaitCaller, NULL );
// If not initialized/already closed
if ( hWaitCaller == NULL )
{
// Nothing
goto exit;
}
// Get the process
hProcessCaller = InterlockedExchangePointer( &m_hProcessCaller, NULL );
// If we are in the same thread as the callback function
// we cannot wait for it to finish
if ( dwOldThreadId == dwThreadId )
{
// Delete the registered wait for the caller process
// NULL passed as completion event causes
// UnregisterWaitEx to return w/o blocking
fRet = UnregisterWaitEx( hWaitCaller, NULL );
// The call fails with ERROR_IO_PENDING if the callback function
// is still running (which is expected, because it called us indirectly),
// but any other error is failure
if ( !fRet && ( GetLastError() == ERROR_IO_PENDING ) )
{
// This is not a failure
fRet = TRUE;
SetLastError( ERROR_SUCCESS );
}
}
else
{
// Delete the registered wait for the caller process
// INVALID_HANDLE_VALUE passed as completion event causes
// UnregisterWaitEx not to return until the callback function returns
fRet = UnregisterWaitEx( hWaitCaller, INVALID_HANDLE_VALUE );
}
hWaitCaller = NULL;
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"UnregisterWaitEx() failed in ShutdownCallerWatch hr=0x%08x.\n",
hr ));
goto exit;
}
exit:
if ( hProcessCaller )
{
// Close the process handle
CloseHandle( hProcessCaller );
hProcessCaller = NULL;
}
if ( dwOldThreadId == 0 )
{
// Restore the thread id
dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect,
0,
(LONG)dwThreadId );
DBG_ASSERT( dwOldThreadId == dwThreadId );
}
DBG_ASSERT( SUCCEEDED( hr ) );
// Done
return hr;
}
HRESULT
CADMCOMW::DisconnectOrphaned(VOID)
/*++
Routine Description:
Calls CoDisconnectObject on the object and the connection point.
Arguments:
none
Returns:
HRESULT.
--*/
{
// Locals
HRESULT hr = S_OK;
DWORD dwOldThreadId = (DWORD)-1;
DWORD dwThreadId = GetCurrentThreadId();
DWORD cRef = 0;
// If already terminated
if ( m_bTerminated )
{
// Do nothing
goto exit;
}
// Set the thread id
dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect,
(LONG)dwThreadId,
0 );
if ( dwOldThreadId != 0 )
{
goto exit;
}
// AddRef, to prevent the object being destroyed by one of the CoDisconnect calls
AddRef();
// Disconnect the object
CoDisconnectObject( static_cast<IUnknown*>( this ), 0 );
// Disconnect the connection point
CoDisconnectObject( static_cast<IConnectionPoint*>( &m_ConnectionPoint ), 0 );
// Stop getting and firing notifications.
// This is a forcible termination so remove all pending notifications
StopNotifications( TRUE );
// Release (which may delete the object)
cRef = Release();
exit:
if ( ( cRef > 1 ) && ( dwOldThreadId == 0 ) )
{
// Set the thread id
dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect,
0,
(LONG)dwThreadId );
DBG_ASSERT( dwOldThreadId == dwThreadId );
}
DBG_ASSERT( SUCCEEDED( hr ) );
// Done
return hr;
}
VOID
CALLBACK
CADMCOMW::CallerWatchWaitOrTimerCallback(
PVOID lpParameter, // thread data
BOOLEAN TimerOrWaitFired) // reason
/*++
Routine Description:
Passed as a callback to RegisterWaitForSingleObject.
Called when the caller process terminates.
CoDisconnects the object to force COM to release it before the 10 min timeout.
Arguments:
lpParameter - the callback context passed to RegisterWaitForSingleObject.
Must be the CADMCOMW object used by the process waiting on.
TimerOrWaitFired - The reason to call the callback. Since we are waiting
on the handle for INFINITE time must be always FALSE.
Returns:
VOID.
--*/
{
// Locals
HRESULT hr = S_OK;
CADMCOMW *pThis;
BOOL fUninitCom = FALSE;
DBG_ASSERT( lpParameter != NULL );
DBG_ASSERT( TimerOrWaitFired == FALSE );
// Check
if ( ( lpParameter == NULL ) || ( TimerOrWaitFired != FALSE ) )
{
hr = E_INVALIDARG;
DBGPRINTF(( DBG_CONTEXT,
"Invadil args passed to CallerWatchWaitOrTimerCallback.\n",
hr ));
goto exit;
}
// Initialize COM
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
// If succeeded
if ( SUCCEEDED( hr ) )
{
// Remember to uninit
fUninitCom = TRUE;
}
// If already initialized w/ different model
if ( hr == RPC_E_CHANGED_MODE )
{
// Ok it is thread is apartment threaded, but this means that
// COM is already initialized and there is no need to uninitialize it.
hr = S_OK;
}
DBG_ASSERT( SUCCEEDED( hr ) );
// If really failed
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"CoInitializeEx() failed in CallerWatchWaitOrTimerCallback hr=0x%08x.\n",
hr ));
// Ouch! Nothing we can do. The object will have to wait the timeout.
goto exit;
}
// Get the object
pThis = (CADMCOMW*)lpParameter;
// Disconnect it
hr = pThis->DisconnectOrphaned();
DBG_ASSERT( SUCCEEDED( hr ) );
if ( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"DisconnectOrphaned() failed in CallerWatchWaitOrTimerCallback hr=0x%08x.\n",
hr ));
goto exit;
}
exit:
DBG_ASSERT( SUCCEEDED( hr ) );
// Cleanup
if ( fUninitCom )
{
// Uninitialize COM
CoUninitialize();
}
}
HRESULT
CADMCOMW::GetPids(VOID)
/*++
Routine Description:
Gets the pid of this process (inetinfo.exe) and saves it to
sm_dwProcessIdThis.
Gets the pid of the svchost process running RpcSs and saves it to
sm_dwProcessIdRpcSs.
Arguments:
None
Returns:
HRESULT.
--*/
{
// Locals
HRESULT hr = S_OK;
BOOL fRet;
SC_HANDLE schSCM = NULL;
SC_HANDLE schRpcSs = NULL;
SERVICE_STATUS_PROCESS ServiceStatusProcessRcpSs = {0};
DWORD cbRequired = 0;
// Save the current processid
sm_dwProcessIdThis = GetCurrentProcessId();
DBG_ASSERT( sm_dwProcessIdThis != 0 );
// If already initialized don't play w/ SCM again
if ( sm_dwProcessIdRpcSs != 0 )
{
goto exit;
}
// Open SCM
schSCM = OpenSCManager( NULL,
NULL,
SC_MANAGER_ENUMERATE_SERVICE );
if (schSCM == NULL)
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"OpenSCManager() failed in GetPids hr=0x%08x.\n",
hr ));
goto exit;
}
// Open RpcSs
schRpcSs = OpenServiceA( schSCM,
"RpcSs",
SERVICE_QUERY_STATUS);
if ( schRpcSs == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"OpenService() failed in GetPids hr=0x%08x.\n",
hr ));
goto exit;
}
// Query the status of RpcSs to get the pid
fRet = QueryServiceStatusEx( schRpcSs,
SC_STATUS_PROCESS_INFO,
(BYTE*)&ServiceStatusProcessRcpSs,
sizeof(ServiceStatusProcessRcpSs),
&cbRequired );
if ( !fRet )
{
DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"QueryServiceStatusEx() failed in GetPids hr=0x%08x.\n",
hr ));
goto exit;
}
// Save the pid
sm_dwProcessIdRpcSs = ServiceStatusProcessRcpSs.dwProcessId;
DBG_ASSERT( sm_dwProcessIdRpcSs != 0 );
exit:
// Cleanup
if ( schRpcSs != NULL )
{
CloseServiceHandle( schRpcSs );
schRpcSs = NULL;
}
if ( schSCM != NULL )
{
CloseServiceHandle( schSCM );
schSCM = NULL;
}
DBG_ASSERT( SUCCEEDED( hr ) );
return hr;
}
HRESULT
MakePathCanonicalizationProof(
LPCWSTR pwszName,
BOOL fResolve,
STRAU *pstrPath)
/*++
Routine Description:
The function tries make a file name proof to all canonicalization problems.
If fResolve is FALSE:
This functions adds a prefix to the string,
which is "\\?\UNC\" for a UNC path, and "\\?\" for other paths.
This prefix tells Windows not to parse the path.
If fResolve is TRUE:
This function constructs a file name as above, opens the file and gets
the real name from the handle.
Stolen from iisutil.dll, because coadmin links only with iisrtl
Arguments:
IN pszName - The path to be converted
IN fResolve - Whether the caller can live with the name just prefixed or needs the real name.
OUT pstrPath - Output path created
Return Values:
HRESULT
--*/
{
HRESULT hr = S_OK;
DWORD dwError;
BOOL fRet;
NTSTATUS Status = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE hFile = NULL;
BUFFER Buff;
FILE_NAME_INFORMATION *pFileNameInfo = NULL;
DWORD dwSize;
DWORD dwReqSize;
WCHAR wszPrefix[4];
WCHAR wchT;
// Check args
if ( ( pwszName == NULL ) || ( pstrPath == NULL ) )
{
hr = E_INVALIDARG;
goto exit;
}
pstrPath->Reset();
if ( pwszName[ 0 ] == L'\\' && pwszName[ 1 ] == L'\\' )
{
// If the path is already canonicalized, just return
if ( ( pwszName[ 2 ] == '?' || pwszName[ 2 ] == '.' ) &&
pwszName[ 3 ] == '\\' )
{
// Prepend "\\?\"
// If the path was in DOS form ("\\.\"),
// we need to change it to Win32 from ("\\?\")
fRet = pstrPath->Append( L"\\\\?\\" );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Copy the path into the string
fRet = pstrPath->Append( (const LPWSTR)(pwszName+4) );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Just return the copy
goto exit;
}
if ( fResolve )
{
wszPrefix[0] = L'\\';
wszPrefix[1] = L'\0';
}
// Skip the "\\"
pwszName += 2;
// Prepend "\\?\UNC\"
fRet = pstrPath->Append( L"\\\\?\\UNC\\" );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
}
else
{
if ( fResolve )
{
wchT = pwszName[0];
if ( ( wchT >= L'A' ) && ( wchT <= L'Z' ) )
{
wchT = wchT - L'A' + L'a';
}
if ( ( wchT < L'a' ) || ( wchT > L'z' ) )
{
hr = E_INVALIDARG;
goto exit;
}
if ( pwszName[1] != L':' )
{
hr = E_INVALIDARG;
goto exit;
}
if ( pwszName[2] != L'\\' )
{
hr = E_INVALIDARG;
goto exit;
}
wszPrefix[0] = pwszName[0];
wszPrefix[1] = pwszName[1];
wszPrefix[2] = L'\0';
}
// Prepend "\\?\"
fRet = pstrPath->Append( L"\\\\?\\" );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
}
// Add the path
fRet = pstrPath->Append( (const LPWSTR)pwszName );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Don't use the original filename anymore
pwszName = NULL;
// If the caller can work with the anti-canonicalized file name
if ( !fResolve )
{
// We are done, return
goto exit;
}
// Well have to do it the hard way
hFile = CreateFileW( pstrPath->QueryStrW(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if ( ( hFile == NULL ) || ( hFile == INVALID_HANDLE_VALUE ) )
{
dwError = GetLastError();
hr = HRESULT_FROM_WIN32( dwError );
goto exit;
}
// Assume the size of the name should approximate the same
dwSize = sizeof( FILE_NAME_INFORMATION ) + pstrPath->QueryCBW()+ sizeof( WCHAR );
dwSize = ( dwSize + 0x0000000Ful ) & ~0x0000000Ful;
// Set the size
fRet = Buff.Resize( dwSize );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
pFileNameInfo = (FILE_NAME_INFORMATION*)Buff.QueryPtr();
DBG_ASSERT( pFileNameInfo );
// Get the name from the handle
Status = NtQueryInformationFile( hFile,
&IoStatusBlock,
pFileNameInfo,
dwSize,
FileNameInformation );
// If the buffer is not big enough
if ( ( Status == STATUS_BUFFER_OVERFLOW ) ||
( Status == STATUS_BUFFER_TOO_SMALL ) )
{
// Calculate the new size
dwReqSize = sizeof(FILE_NAME_INFORMATION) + pFileNameInfo->FileNameLength + sizeof( WCHAR );
DBG_ASSERT( dwReqSize > dwSize );
dwSize = ( dwReqSize + 0x0000000Ful ) & ~0x0000000Ful;
fRet = Buff.Resize( dwSize );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
pFileNameInfo = (FILE_NAME_INFORMATION*)Buff.QueryPtr();
DBG_ASSERT( pFileNameInfo );
// Retry getting the name from the handle
Status = NtQueryInformationFile( hFile,
&IoStatusBlock,
pFileNameInfo,
dwSize,
FileNameInformation );
}
if ( !NT_SUCCESS( Status ) )
{
dwError = RtlNtStatusToDosError( Status );
DBG_ASSERT( dwError != ERROR_SUCCESS );
if ( dwError != ERROR_MR_MID_NOT_FOUND )
{
hr = HRESULT_FROM_WIN32( dwError );
}
else
{
hr = HRESULT_FROM_NT( Status );
DBG_ASSERT ( FAILED( hr ) );
}
goto exit;
}
pstrPath->Reset();
// Set the prefix
fRet = pstrPath->Copy( wszPrefix );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
// Append the name
fRet = pstrPath->Append( pFileNameInfo->FileName, pFileNameInfo->FileNameLength/sizeof( WCHAR ) );
if ( !fRet )
{
hr = E_OUTOFMEMORY;
goto exit;
}
exit:
if ( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) )
{
CloseHandle(hFile);
hFile = NULL;
}
if ( FAILED( hr ) )
{
pstrPath->Reset();
}
return hr;
}