|
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// D A V C D A T A . C P P
//
// HTTP 1.1/DAV 1.0 request handling via ISAPI
//
// DAVCDATA is the dav process executable for storing handles that should
// not be recycled when worker process recycle. It also contains the timing
// code for timing out locks, and it establishes the shared memory for
// the DAV worker processes.
//
// This process must run under the same identity as the worker processes.
//
// Copyright 2000 Microsoft Corporation, All Rights Reserved
//
/////////////////////////////////////////////////////////////////////////////
#include "_davcdata.h"
#include <caldbg.h>
#include <crc.h>
#include <davsc.h>
#include <fhcache.h>
#include <ex\autoptr.h>
#include <ex\baselist.h>
#include <ex\buffer.h>
#include <ex\calcom.h>
#include <ex\gencache.h>
#include <ex\reg.h>
#include <ex\synchro.h>
#include <ex\sz.h>
// Code borrowed from htpext mem.cpp so we have use of the global heap
//
#define g_szMemDll L"staxmem.dll"
struct CHeap { static BOOL FInit(); static void Deinit(); static LPVOID Alloc( SIZE_T cb ); static LPVOID Realloc( LPVOID lpv, SIZE_T cb ); static VOID Free( LPVOID pv ); };
#include <memx.h>
// Mapping the exdav non-throwing allocators to something local
//
LPVOID __fastcall ExAlloc( UINT cb ) { return g_heap.Alloc( cb ); } LPVOID __fastcall ExRealloc( LPVOID pv, UINT cb ) { return g_heap.Realloc( pv, cb ); } VOID __fastcall ExFree( LPVOID pv ) { g_heap.Free( pv ); }
// GUIDs
//
const GUID CLSID_FileHandleCache = { 0xa93b88df, 0xef9d, 0x420c, { 0xb4, 0x69, 0xce, 0x07, 0x4e, 0xbe, 0x94, 0xbc}}; const GUID IID_IFileHandleCache = { 0x3017e0e1, 0x94d6, 0x4896, { 0xbc, 0x57, 0xb2, 0xdf, 0x75, 0x92, 0xd1, 0x75 }};
DEC_CONST WCHAR gc_wsz_RegServer[] = L"/RegServer"; DEC_CONST INT gc_cch_RegServer = CchConstString(gc_wsz_RegServer); DEC_CONST WCHAR gc_wsz_UnregServer[] = L"/UnregServer"; DEC_CONST INT gc_cch_UnregServer = CchConstString(gc_wsz_UnregServer); DEC_CONST WCHAR gc_wsz_Embedding[] = L"-Embedding"; DEC_CONST INT gc_cch_Embedding = CchConstString(gc_wsz_Embedding); DEC_CONST WCHAR gc_wsz_CLSIDWW[] = L"CLSID\\"; DEC_CONST INT gc_cch_CLSIDWW = CchConstString(gc_wsz_CLSIDWW); DEC_CONST WCHAR gc_wsz_AppIDWW[] = L"AppID\\"; DEC_CONST INT gc_cch_AppIDWW = CchConstString(gc_wsz_AppIDWW); DEC_CONST WCHAR gc_wsz_AppID[] = L"AppID";
DEC_CONST WCHAR gc_wsz_WebDAVFileHandleCache[] = L"Web DAV File Handle Cache"; DEC_CONST INT gc_cch_WebDAVFileHandleCache = CchConstString(gc_wsz_WebDAVFileHandleCache); DEC_CONST WCHAR gc_wszLaunchPermission[] = L"LaunchPermission"; DEC_CONST INT gc_cchLaunchPermission = CchConstString(gc_wszLaunchPermission); DEC_CONST WCHAR gc_wszIIS_WPG[] = L"IIS_WPG"; DEC_CONST INT gc_cchIIS_WPG = CchConstString(gc_wszIIS_WPG); DEC_CONST WCHAR gc_wsz_WWLocalServer32[]= L"\\LocalServer32"; DEC_CONST INT gc_cch_WWLocalServer32 = CchConstString(gc_wsz_WWLocalServer32); DEC_CONST WCHAR gc_wsz_IFileHandleCache[] = L"IFileHandleCache"; DEC_CONST INT gc_cch_IFileHandleCache = CchConstString(gc_wsz_IFileHandleCache);
#ifdef DBG
BOOL g_fDavTrace = FALSE; DEC_CONST CHAR gc_szDbgIni[] = "DAVCData.INI"; DEC_CONST INT gc_cchDbgIni = CchConstString(gc_szDbgIni); #endif
// Timer constants and globals.
//
const DWORD WAIT_PERIOD = 60000; // 1 min = 60 sec = 60,000 milliseconds
// Helper functions
//
BOOL FCanUnloadServer(); static DWORD s_dwMainTID = 0;
// ===============================================================
// Supporting class definitions
// ===============================================================
class CHandleArray {
protected: HANDLE m_rgHandles[MAXIMUM_WAIT_OBJECTS]; UINT m_uiHandles;
public:
CHandleArray() : m_uiHandles(0) { }
HANDLE * PhGetHandles() { return m_rgHandles; }
UINT UiGetHandleCount() { return m_uiHandles; }
BOOL FIsFull() { return (MAXIMUM_WAIT_OBJECTS == m_uiHandles); }
VOID AddHandle(HANDLE h) { Assert(FALSE == FIsFull()); m_rgHandles[m_uiHandles++] = h; }
VOID RemoveHandle(UINT uiIndex, BOOL fCloseHandle) { Assert(m_uiHandles > uiIndex);
if (fCloseHandle) { CloseHandle(m_rgHandles[uiIndex]); } memcpy(m_rgHandles + uiIndex, m_rgHandles + uiIndex + 1, (m_uiHandles - uiIndex - 1) * sizeof(HANDLE)); m_uiHandles--; } };
class CHandleArrayForHandlePool : public CListElement<CHandleArrayForHandlePool>, public CHandleArray {
public:
// Indexes to the handles in the array
//
enum { ih_external_update, ih_delete_timer, c_events, ih_wp = c_events };
CHandleArrayForHandlePool(HANDLE hEvtNewWP, HANDLE hEvtDelTimer) { Assert(c_events < MAXIMUM_WAIT_OBJECTS); AddHandle(hEvtNewWP); AddHandle(hEvtDelTimer); }
BOOL FIsEmpty() { Assert(c_events <= m_uiHandles); return (c_events == m_uiHandles); } };
class CHandlePool : public Singleton<CHandlePool> { HANDLE m_hEvtUpdatesAllowed; HANDLE m_hEvtStartListening; HANDLE m_hEvtExternalUpdate; HANDLE m_hEvtDelTimer; LONG m_lUpdatesInProgress; LONG m_lShutDown;
CCriticalSection m_cs; CListHead<CHandleArrayForHandlePool> m_listHandleArrayForHandlePool;
// Wait period in miliseconds for looking at single handle bucket
//
enum { WAIT_POLL_PERIOD = 5000 };
//
// Friend declarations required by Singleton template
//
friend class Singleton<CHandlePool>;
// CREATORS
//
CHandlePool() : m_hEvtUpdatesAllowed(NULL), m_hEvtStartListening(NULL), m_hEvtExternalUpdate(NULL), m_hEvtDelTimer(NULL), m_lUpdatesInProgress(0), m_lShutDown(0) { } ~CHandlePool() { UnInitialize(); }
// NOT IMPLEMENTED
//
CHandlePool& operator=( const CHandlePool& ); CHandlePool( const CHandlePool& );
public:
// CREATORS
//
// Instance creating/destroying routines provided
// by the Singleton template.
//
using Singleton<CHandlePool>::CreateInstance; using Singleton<CHandlePool>::DestroyInstance; using Singleton<CHandlePool>::Instance; HRESULT HrInitialize() { HRESULT hr = S_OK; HANDLE hWaitingThread = NULL; auto_handle<HANDLE> a_hEvtUpdatesAllowed; auto_handle<HANDLE> a_hEvtStartListening; auto_handle<HANDLE> a_hEvtExternalUpdate; auto_handle<HANDLE> a_hEvtDelTimer; auto_ptr<CHandleArrayForHandlePool> a_pHandleArrayForHandlePool;
// Create the event that is used to indicate if the updates are allowed.
// While this event is set the updates can be performed and noone is
// listening on the handles in the pool handle arrays.
//
a_hEvtUpdatesAllowed = CreateEvent (NULL, // lpEventAttributes
TRUE, // bManualReset
FALSE, // bInitialState
NULL); // lpName
if (NULL == a_hEvtUpdatesAllowed.get()) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("CreateEvent failed 0x%08lX\n", hr); goto ret; }
// Create event that triggers the thread to again start listening on
// process handes.
//
a_hEvtStartListening = CreateEvent (NULL, // lpEventAttributes
FALSE, // bManualReset
FALSE, // bInitialState
NULL); // lpName
if (NULL == a_hEvtStartListening.get()) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("CreateEvent failed 0x%08lX\n", hr); goto ret; } // Create the event that is used to notify the arrival of new event
//
a_hEvtExternalUpdate = CreateEvent (NULL, // lpEventAttributes
FALSE, // bManualReset
FALSE, // bInitialState
NULL); // lpName
if (NULL == a_hEvtExternalUpdate.get()) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("CreateEvent failed 0x%08lX\n", hr); goto ret; }
// Create the event that listens for timer deletion
//
a_hEvtDelTimer = CreateEvent (NULL, // lpEventAttributes
FALSE, // bManualReset
FALSE, // bInitialState
NULL); // lpName
if (NULL == a_hEvtDelTimer.get()) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("CreateEvent failed 0x%08lX\n", hr); goto ret; }
a_pHandleArrayForHandlePool = new CHandleArrayForHandlePool(a_hEvtExternalUpdate.get(), a_hEvtDelTimer.get()); if (NULL == a_pHandleArrayForHandlePool.get()) { hr = E_OUTOFMEMORY; DebugTrace ("Allocation failed 0x%08lX\n", hr); goto ret; }
m_listHandleArrayForHandlePool.Append(a_pHandleArrayForHandlePool.relinquish()); m_hEvtUpdatesAllowed = a_hEvtUpdatesAllowed.relinquish(); m_hEvtStartListening = a_hEvtStartListening.relinquish(); m_hEvtExternalUpdate = a_hEvtExternalUpdate.relinquish(); m_hEvtDelTimer = a_hEvtDelTimer.relinquish();
ret:
if (FAILED(hr)) { UnInitialize(); }
return hr; }
VOID UnInitialize() { CHandleArrayForHandlePool * pHandleArrayForHandlePool; pHandleArrayForHandlePool = m_listHandleArrayForHandlePool.GetListHead();
while (pHandleArrayForHandlePool) { HANDLE * pHandles = pHandleArrayForHandlePool->PhGetHandles(); UINT uiHandles = pHandleArrayForHandlePool->UiGetHandleCount(); for (UINT ui = CHandleArrayForHandlePool::ih_wp; ui < uiHandles; ui++) { CloseHandle(pHandles[ui]); } m_listHandleArrayForHandlePool.Remove(pHandleArrayForHandlePool); delete pHandleArrayForHandlePool; pHandleArrayForHandlePool = m_listHandleArrayForHandlePool.GetListHead(); }
if (m_hEvtUpdatesAllowed) { CloseHandle(m_hEvtUpdatesAllowed); }
if (m_hEvtStartListening) { CloseHandle(m_hEvtStartListening); }
if (m_hEvtExternalUpdate) { CloseHandle(m_hEvtExternalUpdate); }
if (m_hEvtDelTimer) { CloseHandle(m_hEvtDelTimer); } }
VOID AllowUpdatesToExecute() { // Allow updates and start waiting for them to end
//
SetEvent(m_hEvtUpdatesAllowed); WaitForSingleObject(m_hEvtStartListening, INFINITE); }
VOID AllowShutdownToExecute() { InterlockedExchange(&m_lShutDown, 1); SetEvent(m_hEvtUpdatesAllowed); }
VOID DisallowUpdates() { // We disallow updates only if we are not in shutdown. I.e.
// we are in the listening loop
//
if (0 == InterlockedCompareExchange(&m_lShutDown, 1, 1)) { ResetEvent(m_hEvtUpdatesAllowed); } }
HRESULT HrAddHandle(HANDLE h) { HRESULT hr = S_OK; BOOL fHandleAdded = FALSE;
// Inform the thread that is waiting on process handles
// that the update has arrived. We do this only if there
// were no other updates in progress.
//
if (1 == InterlockedIncrement(&m_lUpdatesInProgress)) { DisallowUpdates(); SetEvent(m_hEvtExternalUpdate); }
// Wait until the listening thread is ready for updates, I.e. it
// stopped listening on process handles or doing other work.
//
WaitForSingleObject(m_hEvtUpdatesAllowed, INFINITE);
{ CSynchronizedBlock sb(m_cs);
CHandleArrayForHandlePool * pHandleArrayForHandlePoolNext; pHandleArrayForHandlePoolNext = m_listHandleArrayForHandlePool.GetListHead();
do { Assert(NULL != pHandleArrayForHandlePoolNext); if (pHandleArrayForHandlePoolNext->FIsFull()) { pHandleArrayForHandlePoolNext = pHandleArrayForHandlePoolNext->GetNextListElement(); } else { pHandleArrayForHandlePoolNext->AddHandle(h); fHandleAdded = TRUE; break; } } while (NULL != pHandleArrayForHandlePoolNext);
if (FALSE == fHandleAdded) { auto_ptr<CHandleArrayForHandlePool> a_pHandleArrayForHandlePool;
Assert(NULL != m_hEvtExternalUpdate); Assert(NULL != m_hEvtDelTimer); a_pHandleArrayForHandlePool = new CHandleArrayForHandlePool(m_hEvtExternalUpdate, m_hEvtDelTimer); if (NULL == a_pHandleArrayForHandlePool.get()) { hr = E_OUTOFMEMORY; DebugTrace ("Allocation failed 0x%08lX\n", hr); goto ret; } a_pHandleArrayForHandlePool->AddHandle(h); m_listHandleArrayForHandlePool.Append(a_pHandleArrayForHandlePool.relinquish()); } }
// If this is last update to leave allow the thread listening
// on process handles to proceed
//
if (0 == InterlockedDecrement(&m_lUpdatesInProgress)) { DisallowUpdates(); SetEvent(m_hEvtStartListening); }
ret:
return hr; }
VOID RemoveHandleInternal(CHandleArrayForHandlePool * pHandleArrayForHandlePool, UINT uiIndex) { pHandleArrayForHandlePool->RemoveHandle(uiIndex, TRUE); if (pHandleArrayForHandlePool->FIsEmpty()) { // Do not remove the last buffer in the list as we
// still want to wait for the events of external update
//
if (1 < m_listHandleArrayForHandlePool.ListSize()) { m_listHandleArrayForHandlePool.Remove(pHandleArrayForHandlePool); delete pHandleArrayForHandlePool; } } }
VOID SignalTimerDelete() { SetEvent(m_hEvtDelTimer); }
static DWORD __stdcall DwWaitOnWPs (PVOID pvThreadData); };
class CLockData { // Constant values
//
enum { DEFAULT_LOCK_TIMEOUT = 60 * 3 };
// Lock ID
//
LARGE_INTEGER m_liLockID;
// Lock description data
//
DWORD m_dwAccess; DWORD m_dwLockType; DWORD m_dwLockScope; DWORD m_dwSecondsTimeout;
// Resource and comment strings
//
auto_ptr<WCHAR> m_pwszResourceString; auto_ptr<WCHAR> m_pwszOwnerComment;
// Owner of the lock
//
DWORD m_dwSidLength; auto_ptr<BYTE> m_pbSid;
// File handle that this process is holding to keep the
// file open. It must be duplicated before using
//
auto_handle<HANDLE> m_hFileHandle;
// Lock cache timeout data
//
FILETIME m_ftLastAccess;
// Cached values to speed up timeout calculation
//
FILETIME m_ftRememberNow; BOOL m_fHasTimedOut;
// Lock token string
//
UINT m_cchToken; WCHAR m_rgwchToken[MAX_LOCKTOKEN_LENGTH];
public:
// CREATORS
//
CLockData() : m_dwAccess(0), m_dwLockType(0), m_dwLockScope(0), m_dwSecondsTimeout(DEFAULT_LOCK_TIMEOUT), m_dwSidLength(0), m_fHasTimedOut(FALSE), m_cchToken(0) { m_liLockID.QuadPart = 0; m_ftLastAccess.dwLowDateTime = 0; m_ftLastAccess.dwHighDateTime = 0; m_ftRememberNow.dwLowDateTime = 0; m_ftRememberNow.dwHighDateTime = 0; }
~CLockData() { }
HRESULT HrInitialize(LPCWSTR pwszGuid, LARGE_INTEGER liLockID, DWORD dwAccess, DWORD dwLockType, DWORD dwLockScope, DWORD dwSecondsTimeout, LPCWSTR pwszResourceString, LPCWSTR pwszOwnerComment, DWORD dwSid, BYTE * pbSid) { HRESULT hr = S_OK;
// Opaquelocktoken format is partially defined by our IETF spec.
// First opaquelocktoken:<our guid>, then our specific lock id.
//
m_cchToken = _snwprintf(m_rgwchToken, CElems(m_rgwchToken), L"<opaquelocktoken:%ls:%I64d>", pwszGuid, liLockID); if (((-1) == static_cast<INT>(m_cchToken)) || (CElems(m_rgwchToken) == m_cchToken)) { // This should not happen as we give sufficient buffer. But let us
// handle this to the best of our ability for preventive reasons.
//
Assert(0); m_cchToken = CElems(m_rgwchToken) - 1; m_rgwchToken[m_cchToken] = L'\0'; }
m_liLockID = liLockID; m_dwAccess = dwAccess; m_dwLockType = dwLockType; m_dwLockScope = dwLockScope; if (dwSecondsTimeout) { m_dwSecondsTimeout = dwSecondsTimeout; }
if (pwszResourceString) { UINT cchResourceString = static_cast<UINT>(wcslen(pwszResourceString)); m_pwszResourceString = static_cast<LPWSTR>(ExAlloc(CbSizeWsz(cchResourceString))); if (NULL == m_pwszResourceString.get()) { hr = E_OUTOFMEMORY; goto ret; } memcpy(m_pwszResourceString.get(), pwszResourceString, sizeof(WCHAR) * cchResourceString); m_pwszResourceString[cchResourceString] = L'\0'; }
if (pwszOwnerComment) { UINT cchOwnerComment = static_cast<UINT>(wcslen(pwszOwnerComment)); m_pwszOwnerComment = static_cast<LPWSTR>(ExAlloc(CbSizeWsz(cchOwnerComment))); if (NULL == m_pwszOwnerComment.get()) { hr = E_OUTOFMEMORY; goto ret; } memcpy(m_pwszOwnerComment.get(), pwszOwnerComment, sizeof(WCHAR) * cchOwnerComment); m_pwszOwnerComment[cchOwnerComment] = L'\0'; }
m_pbSid = static_cast<BYTE *>(ExAlloc(dwSid)); if (NULL == m_pbSid.get()) { hr = E_OUTOFMEMORY; goto ret; } if (!CopySid(dwSid, m_pbSid.get(), pbSid)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; } m_dwSidLength = dwSid;
// Lastly set in the last file time that this was accessed.
//
GetSystemTimeAsFileTime(&m_ftLastAccess);
ret:
return hr; }
HRESULT HrLockFile(HANDLE hFile, DWORD dwProcessId) { HRESULT hr = S_OK;
auto_handle<HANDLE> a_hProcess = NULL; auto_handle<HANDLE> a_hDupFileHandle = NULL;
a_hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessId); if (NULL == a_hProcess.get()) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace("Opening original process failed 0x%08lX\n", hr); goto ret; }
if (!DuplicateHandle(a_hProcess.get(), hFile, GetCurrentProcess(), a_hDupFileHandle.load(), 0, FALSE, DUPLICATE_SAME_ACCESS)) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace("Failed to duplicate handle 0x%08lX\n", hr); goto ret; }
m_hFileHandle = a_hDupFileHandle.relinquish();
ret:
return hr; }
// Lock ID
//
LARGE_INTEGER LiLockID() { return m_liLockID; }
// Lock token string
//
UINT CchLockTokenString(LPCWSTR * ppwszLockToken) const { if (ppwszLockToken) { *ppwszLockToken = m_rgwchToken; } return m_cchToken; }
// Resource string
//
LPCWSTR PwszResourceString() const { return m_pwszResourceString.get(); }
// Owner comment
//
LPCWSTR PwszOwnerComment() const { return m_pwszOwnerComment.get(); }
// Touch the lock
//
VOID SetLastAccess(FILETIME ftNow) { m_ftLastAccess = ftNow; }
// Set timeout
//
VOID SetSecondsTimeout(DWORD dwSecondsTimeout) { m_dwSecondsTimeout = dwSecondsTimeout; }
// Check the owner
//
BOOL FIsSameOwner(PSID pSid) const { BOOL fIsSameOwner = TRUE; Assert(pSid); Assert(IsValidSid(m_pbSid.get())); Assert(IsValidSid(pSid));
if (!EqualSid(m_pbSid.get(), pSid)) { fIsSameOwner = FALSE; }
return fIsSameOwner; }
// Check the resource
//
BOOL FIsSameResource(LPCWSTR pwszResource) const { BOOL fIsSameResource = TRUE;
Assert(pwszResource);
if (_wcsicmp(m_pwszResourceString.get(), pwszResource)) { fIsSameResource = FALSE; }
return fIsSameResource; }
// Check the type
//
BOOL FIsSameType(DWORD dwLockType) const { return (0 != (dwLockType & m_dwLockType)); }
// Fill the data of the lock into the structure for marshalling
//
VOID FillSNewLockData(SNewLockData * pnld) const { Assert(pnld); pnld->m_dwAccess = m_dwAccess; pnld->m_dwLockType = m_dwLockType; pnld->m_dwLockScope = m_dwLockScope; pnld->m_dwSecondsTimeout = m_dwSecondsTimeout; pnld->m_pwszResourceString = m_pwszResourceString.get(); pnld->m_pwszOwnerComment = m_pwszOwnerComment.get(); }
// Fill the handle data of the lock for marshalling
//
VOID FillSLockHandleData(SLockHandleData * plhd) const { Assert(plhd); plhd->h = reinterpret_cast<DWORD_PTR>(m_hFileHandle.get()); plhd->dwProcessID = /*CLockCache::Instance().DwGetThisProcessID();*/ GetCurrentProcessId(); }
BOOL FIsExpired(FILETIME ftNow) { // This function will be called twice during each ExpiredLocks
// check. The first time it is called we should do the checks
// and set the m_fHasTimedOut call. The second time we want to
// avoid it because we all ready know the answer. By keeping
// track of the ftNow times we are called with we can determine
// if we have all ready done the calculation or not.
// We also know that no matter what once a lock times out, it should
// remain timed out for it's lifetime.
//
if ((!m_fHasTimedOut) && (0 != CompareFileTime(&ftNow, &m_ftRememberNow))) { // Find out if based on the time passed in has the lock expired
//
INT64 i64TimePassed; DWORD dwSecondsPassed;
// Do the math to figure out when this lock expires/expired.
//
// First calculate how many seconds have passed since this lock
// was last accessed.
// Subtract the filetimes to get the time passed in 100-nanosecond
// increments. (That's how filetimes count.)
// NOTE: Operation bellow is very dangerous on 64 bit platforms,
// as the filetimes need to be alligned on 8 byte boundary. So even
// change in order of current member variables or adding new ones
// can get us in the trouble
//
i64TimePassed = ((*(INT64*)&ftNow) - (*(INT64*)&m_ftLastAccess));
// Convert our time passed into seconds (10,000,000 100-nanosec incs in a second).
//
dwSecondsPassed = static_cast<DWORD>(i64TimePassed/10000000);
// Compare the timeout from the lock object to the count of seconds.
// If this lock object has expired, remove it.
//
m_fHasTimedOut = m_dwSecondsTimeout < dwSecondsPassed; m_ftRememberNow = ftNow; }
return m_fHasTimedOut; } }; typedef CLockData* PLockData;
class CLockCache : public Singleton<CLockCache> { //
// Friend declarations required by Singleton template
//
friend class Singleton<CLockCache>;
// Guid string for our lock IDs
//
WCHAR m_rgwchGuid[gc_cchMaxGuid];
// Current process ID
//
DWORD m_dwThisProcessId;
// Next lock ID counter
//
LARGE_INTEGER m_liLastLockID;
class LIKey { public:
LARGE_INTEGER m_li;
LIKey(LARGE_INTEGER li) : m_li(li) { }
// operators for use with the hash cache
//
int hash( const int rhs ) const { return (m_li.LowPart % rhs); } bool isequal( const LIKey& rhs ) const { return (m_li.QuadPart == rhs.m_li.QuadPart); } };
typedef CCache<LIKey, PLockData> CLockCacheById; typedef CCache<CRCWsziLI, PLockData> CLockCacheByName;
CMRWLock m_mrwCache; CLockCacheById m_lockCacheById; CLockCacheByName m_lockCacheByName; HANDLE m_hTimer;
class COpClear : public CLockCacheById::IOp { // NOT IMPLEMENTED
//
COpClear& operator=( const COpClear& );
public:
// CREATORS
//
COpClear() { }
BOOL operator() (const LIKey&, const PLockData& pLockData) { delete pLockData; return TRUE; } };
class COpExpire : public CLockCacheById::IOp { FILETIME m_ftNow; CLockCacheById& m_lockCacheById; CLockCacheByName& m_lockCacheByName; // NOT IMPLEMENTED
//
COpExpire& operator=( const COpExpire& );
public:
// CREATORS
//
COpExpire(FILETIME ftNow, CLockCacheById& lockCacheById, CLockCacheByName& lockCacheByName) : m_ftNow(ftNow), m_lockCacheById(lockCacheById), m_lockCacheByName(lockCacheByName) { }
BOOL operator() (const LIKey&, const PLockData& pLockData) { if (pLockData->FIsExpired(m_ftNow)) { m_lockCacheById.Remove(LIKey(pLockData->LiLockID())); m_lockCacheByName.Remove(CRCWsziLI(pLockData->PwszResourceString(), pLockData->LiLockID(), TRUE)); delete pLockData; }
return TRUE; } };
class COpGatherLockData : public CLockCacheByName::IOp { // The path to match
//
LPCWSTR m_pwszPath;
// Lock type to match
//
DWORD m_dwLockType;
// Error code in which operation has ended
//
HRESULT m_hr;
// Results gathered by the operation
//
DWORD m_dwLocksFound; ChainedBuffer<SNewLockData> m_chBufNewLockData; ChainedBuffer<LPWSTR> m_chBufPLockTokens;
// NOT IMPLEMENTED
//
COpGatherLockData& operator=( const COpGatherLockData& );
public:
// CREATORS
//
COpGatherLockData( LPCWSTR pwszPath, DWORD dwLockType ) : m_pwszPath(pwszPath), m_dwLockType(dwLockType), m_hr(S_OK), m_dwLocksFound(0) { }
// MANIPULATORS
//
VOID Invoke( CLockCacheByName& cache ) { // Do the ForEachMatch()
//
LARGE_INTEGER li; li.QuadPart = 0; cache.ForEachMatch( CRCWsziLI(m_pwszPath, li, FALSE), *this ); }
BOOL operator() (const CRCWsziLI&, const PLockData& pLockData) { BOOL fSuccess = TRUE; if (pLockData->FIsSameType(m_dwLockType)) { SNewLockData * pNewLockData; LPWSTR * ppLockToken; pNewLockData = m_chBufNewLockData.Alloc(sizeof(SNewLockData)); if (NULL == pNewLockData) { m_hr = E_OUTOFMEMORY; fSuccess = FALSE; goto ret; } pLockData->FillSNewLockData(pNewLockData);
ppLockToken = m_chBufPLockTokens.Alloc(sizeof(LPWSTR)); if (NULL == ppLockToken) { m_hr = E_OUTOFMEMORY; fSuccess = FALSE; goto ret; } pLockData->CchLockTokenString(const_cast<LPCWSTR *>(ppLockToken));
m_dwLocksFound++; } ret:
return fSuccess; }
HRESULT HrLocksFound(DWORD * pdwLocksFound) { HRESULT hr = m_hr;
Assert(pdwLocksFound);
if (FAILED(hr)) { goto ret; } *pdwLocksFound = m_dwLocksFound;
ret:
return hr; }
HRESULT HrGetData(SNewLockData * pNewLockData, LPWSTR * ppwszLockToken) { HRESULT hr = m_hr; auto_co_task_mem<WCHAR> a_pwszResourceString; auto_co_task_mem<WCHAR> a_pwszOwnerComment; auto_co_task_mem<WCHAR> a_pwszLockToken; UINT cch; DWORD dw1 = 0; DWORD dw2 = 0;
Assert(pNewLockData); Assert(ppwszLockToken);
if (FAILED(hr)) { goto ret; }
m_chBufNewLockData.Dump(pNewLockData, sizeof(SNewLockData) * m_dwLocksFound); m_chBufPLockTokens.Dump(ppwszLockToken, sizeof(LPWSTR) * m_dwLocksFound);
for (dw1 = 0; dw1 < m_dwLocksFound; dw1++) { cch = static_cast<UINT>(wcslen(pNewLockData[dw1].m_pwszResourceString)); a_pwszResourceString = static_cast<LPWSTR>(CoTaskMemAlloc(CbSizeWsz(cch))); if (NULL == a_pwszResourceString.get()) { hr = E_OUTOFMEMORY; goto ret; } memcpy(a_pwszResourceString.get(), pNewLockData[dw1].m_pwszResourceString, sizeof(WCHAR) * (cch + 1));
cch = static_cast<UINT>(wcslen(pNewLockData[dw1].m_pwszOwnerComment)); a_pwszOwnerComment = static_cast<LPWSTR>(CoTaskMemAlloc(CbSizeWsz(cch))); if (NULL == a_pwszOwnerComment.get()) { hr = E_OUTOFMEMORY; goto ret; } memcpy(a_pwszOwnerComment.get(), pNewLockData[dw1].m_pwszOwnerComment, sizeof(WCHAR) * (cch + 1));
cch = static_cast<UINT>(wcslen(ppwszLockToken[dw1])); a_pwszLockToken= static_cast<LPWSTR>(CoTaskMemAlloc(CbSizeWsz(cch))); if (NULL == a_pwszLockToken.get()) { hr = E_OUTOFMEMORY; goto ret; } memcpy(a_pwszLockToken.get(), ppwszLockToken[dw1], sizeof(WCHAR) * (cch + 1));
pNewLockData[dw1].m_pwszResourceString = a_pwszResourceString.relinquish(); pNewLockData[dw1].m_pwszOwnerComment = a_pwszOwnerComment.relinquish(); ppwszLockToken[dw1] = a_pwszLockToken.relinquish(); }
ret:
if (FAILED(hr)) { // Cleanup whatever we have allocated so far
//
for (dw2 = 0; dw2 < dw1; dw2++) { CoTaskMemFree(pNewLockData[dw1].m_pwszResourceString); CoTaskMemFree(pNewLockData[dw1].m_pwszOwnerComment); CoTaskMemFree(ppwszLockToken[dw1]); } }
return hr; } };
// CREATORS
//
CLockCache() : m_dwThisProcessId(0), m_hTimer(NULL) { m_liLastLockID.QuadPart = 0x0000003200000032; } ~CLockCache() { COpClear opClear; m_lockCacheById.ForEach(opClear); }
// NOT IMPLEMENTED
//
CLockCache& operator=( const CLockCache& ); CLockCache( const CLockCache& );
LARGE_INTEGER LiGetNewLockID() { LARGE_INTEGER liLockID;
//$BUGBUG. There is a problem if while high part is incrementing
// other thread comes in and tries to get the next ID. As lower part
// would be already incremented it still may get old high part. This
// is very rare condition, but we should have some synchronization
// here.
//
liLockID.LowPart = InterlockedIncrement(reinterpret_cast<LONG *>(&m_liLastLockID.LowPart)); if (0 == liLockID.LowPart) { liLockID.HighPart = InterlockedIncrement(&m_liLastLockID.HighPart); } else { liLockID.HighPart = m_liLastLockID.HighPart; }
return liLockID; }
VOID ExpireLocks() { FILETIME ftNow;
// Get the current time
//
GetSystemTimeAsFileTime(&ftNow);
// Protect ourselves for the operation
//
CSynchronizedWriteBlock swb(m_mrwCache);
// Initialize the operation
//
COpExpire opExpire(ftNow, m_lockCacheById, m_lockCacheByName);
// Iterate through the cache trying to expire items
//
m_lockCacheById.ForEach(opExpire);
// Attempt to kill the timer if there are no locks
//
if (0 == m_lockCacheById.CItems()) { CHandlePool::Instance().SignalTimerDelete(); } }
static VOID CALLBACK CheckLocks(PVOID pvIgnored, BOOLEAN fIgnored) { Instance().ExpireLocks(); }
HRESULT HrLaunchLockTimer() { HRESULT hr = S_OK; HANDLE hTimer = NULL;
// We do not protect ourselves for the operation as
// the only caller already protects us...
if (NULL == m_hTimer) { if (!CreateTimerQueueTimer(&hTimer, // timer that we created
NULL, // use default timer queue
CheckLocks, // function that will check the locks in the cache
// and release any expired locks.
NULL, // parameter to the callback function
WAIT_PERIOD, // how long to wait before calling the callback function
// the first time.
WAIT_PERIOD, // how long to wait between calls to the callback function
WT_EXECUTEINIOTHREAD)) // where to execute the function call...
{ hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
m_hTimer = hTimer; }
ret:
return hr; }
VOID DeleteLockTimer(HANDLE hTimer) { if (NULL != hTimer) { // Try to delete the timer, but if it fails just leave it there
//
if (!DeleteTimerQueueTimer(NULL, //default timer queue
hTimer, // timer
INVALID_HANDLE_VALUE)) // blocking call
{ DebugTrace ("Failed to delete timer 0x%08lX\n", HRESULT_FROM_WIN32(GetLastError())); } } }
public:
// CREATORS
//
// Instance creating/destroying routines provided
// by the Singleton template.
//
using Singleton<CLockCache>::CreateInstance; using Singleton<CLockCache>::DestroyInstance; using Singleton<CLockCache>::Instance;
HRESULT HrInitialize() { HRESULT hr = S_OK; DWORD dwResult; UUID guid = {0};
if (!m_lockCacheById.FInit()) { hr = E_OUTOFMEMORY; goto ret; }
if (!m_lockCacheByName.FInit()) { hr = E_OUTOFMEMORY; goto ret; }
if (!m_mrwCache.FInitialize()) { hr = E_OUTOFMEMORY; goto ret; }
dwResult = UuidCreate(&guid); if (RPC_S_OK != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
wsprintfW(m_rgwchGuid, gc_wszGuidFormat, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
m_dwThisProcessId = GetCurrentProcessId();
ret:
return hr; }
DWORD DwGetThisProcessID() { return m_dwThisProcessId; }
VOID DeleteLockTimerIfNotUsed() { HANDLE hTimer = NULL; { // Protect ourselves for the operation
//
CSynchronizedWriteBlock swb(m_mrwCache);
if (NULL != m_hTimer) { // Kill the timer if there are no locks
//
if (0 == m_lockCacheById.CItems()) { hTimer = m_hTimer; m_hTimer = NULL; } } }
DeleteLockTimer(hTimer); }
VOID DeleteLockTimerFinal() { // We do not null out the member variable as this
// will serve as a flag for potential COM threads still
// coming in and trying to create new timers. They
// will not do that if the handle is not NULL.
//
DeleteLockTimer(m_hTimer); }
HRESULT HrGetGUIDString( UINT cchBufferLen, WCHAR * pwszGUIDString, UINT * pcchGUIDString) { HRESULT hr = S_OK; if (gc_cchMaxGuid > cchBufferLen) { *pcchGUIDString = gc_cchMaxGuid; hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); goto ret; }
memcpy(pwszGUIDString, m_rgwchGuid, sizeof(WCHAR) * gc_cchMaxGuid); *pcchGUIDString = gc_cchMaxGuid;
ret:
return hr; }
HRESULT HrGetNewLockData(HANDLE hFile, DWORD dwProcessId, DWORD dwSid, BYTE * pbSid, SNewLockData * pnld, UINT cchBufferLen, WCHAR * pwszLockToken, UINT * pcchLockToken) { HRESULT hr = S_OK; LARGE_INTEGER liLockID; LPCWSTR pwszLockTokenT; UINT cchLockTokenT; auto_ptr<CLockData> a_pLockData;
a_pLockData = new CLockData(); if (NULL == a_pLockData.get()) { hr = E_OUTOFMEMORY; goto ret; }
liLockID = LiGetNewLockID();
hr = a_pLockData->HrInitialize(m_rgwchGuid, liLockID, pnld->m_dwAccess, pnld->m_dwLockType, pnld->m_dwLockScope, pnld->m_dwSecondsTimeout, pnld->m_pwszResourceString, pnld->m_pwszOwnerComment, dwSid, pbSid); if (FAILED(hr)) { goto ret; }
// Check if we will have enough space to return lock token header
//
cchLockTokenT = a_pLockData->CchLockTokenString(&pwszLockTokenT); if (cchBufferLen < cchLockTokenT + 1) { *pcchLockToken = cchLockTokenT + 1; hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); goto ret; }
hr = a_pLockData->HrLockFile(hFile, dwProcessId); if (FAILED(hr)) { goto ret; }
// Add the lock data to the cache
//
{ LIKey liKey(liLockID); CRCWsziLI crcWsziLIKey(a_pLockData->PwszResourceString(), a_pLockData->LiLockID(), TRUE); CSynchronizedWriteBlock swb(m_mrwCache);
if (!m_lockCacheById.FAdd(liKey, a_pLockData.get())) { hr = E_OUTOFMEMORY; goto ret; } if (!m_lockCacheByName.FAdd(crcWsziLIKey, a_pLockData.get())) { // Remove the previous entry
//
m_lockCacheById.Remove(liKey); hr = E_OUTOFMEMORY; goto ret; }
hr = HrLaunchLockTimer(); if (FAILED(hr)) { // Remove the previous entries
//
m_lockCacheById.Remove(liKey); m_lockCacheByName.Remove(crcWsziLIKey);
goto ret; }
memcpy(pwszLockToken, pwszLockTokenT, sizeof(WCHAR) * cchLockTokenT); pwszLockToken[cchLockTokenT] = L'\0'; *pcchLockToken = cchLockTokenT + 1; a_pLockData.relinquish(); } ret:
return hr; }
HRESULT HrGetLockData(LARGE_INTEGER liLockID, DWORD dwSid, BYTE * pbSid, LPCWSTR pwszPath, DWORD dwTimeout, SNewLockData * pnld, SLockHandleData * plhd, UINT cchBufferLen, WCHAR * pwszLockToken, UINT *pcchLockToken) { HRESULT hr = S_OK;
auto_co_task_mem<WCHAR> a_pwszResourceString; auto_co_task_mem<WCHAR> a_pwszOwnerComment;
LIKey liKey(liLockID); CSynchronizedWriteBlock swb(m_mrwCache); CLockData * pLockData;
if (m_lockCacheById.FFetch(liKey, &pLockData)) { FILETIME ftNow; GetSystemTimeAsFileTime(&ftNow);
if (pLockData->FIsExpired(ftNow)) { m_lockCacheById.Remove(liKey); m_lockCacheByName.Remove(CRCWsziLI(pLockData->PwszResourceString(), pLockData->LiLockID(), TRUE)); delete pLockData;
hr = E_DAV_LOCK_NOT_FOUND; goto ret; } else { pLockData->SetLastAccess(ftNow);
if (!pLockData->FIsSameOwner(pbSid)) { hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); goto ret; } if (!pLockData->FIsSameResource(pwszPath)) { hr = E_DAV_CONFLICTING_PATHS; goto ret; }
if (pnld) { LPCWSTR pwszLockTokenT; UINT cchLockTokenT = pLockData->CchLockTokenString(&pwszLockTokenT) + 1; UINT cchOwnerOrResource; if (cchBufferLen < cchLockTokenT) { *pcchLockToken = cchLockTokenT; hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); goto ret; } memcpy(pwszLockToken, pwszLockTokenT, sizeof(WCHAR) * cchLockTokenT); *pcchLockToken = cchLockTokenT;
cchOwnerOrResource = static_cast<UINT>(wcslen(pLockData->PwszResourceString())); a_pwszResourceString = static_cast<LPWSTR>(CoTaskMemAlloc(CbSizeWsz(cchOwnerOrResource))); if (NULL == a_pwszResourceString.get()) { hr = E_OUTOFMEMORY; goto ret; } memcpy(a_pwszResourceString.get(), pLockData->PwszResourceString(), sizeof(WCHAR) * (cchOwnerOrResource + 1));
cchOwnerOrResource = static_cast<UINT>(wcslen(pLockData->PwszOwnerComment())); a_pwszOwnerComment = static_cast<LPWSTR>(CoTaskMemAlloc(CbSizeWsz(cchOwnerOrResource))); if (NULL == a_pwszOwnerComment.get()) { hr = E_OUTOFMEMORY; goto ret; } memcpy(a_pwszOwnerComment.get(), pLockData->PwszOwnerComment(), sizeof(WCHAR) * (cchOwnerOrResource + 1));
pLockData->FillSNewLockData(pnld); pnld->m_pwszResourceString = a_pwszResourceString.relinquish(); pnld->m_pwszOwnerComment = a_pwszOwnerComment.relinquish(); }
if (plhd) { pLockData->FillSLockHandleData(plhd); }
// If there was a timeout passed in for a refresh then set it
//
if (dwTimeout) { pLockData->SetSecondsTimeout(dwTimeout); } } } else { hr = E_DAV_LOCK_NOT_FOUND; goto ret; }
ret:
return hr; }
HRESULT HrDeleteLock(LARGE_INTEGER liLockID) { CSynchronizedWriteBlock swb(m_mrwCache);
CLockData * pLockData;
if (m_lockCacheById.FFetch(LIKey(liLockID), &pLockData)) { m_lockCacheById.Remove(LIKey(liLockID)); m_lockCacheByName.Remove(CRCWsziLI(pLockData->PwszResourceString(), pLockData->LiLockID(), TRUE)); delete pLockData; }
return S_OK; }
HRESULT HrGetAllLockDataForName(LPCWSTR pwszPath, DWORD dwLockType, DWORD * pdwLocksFound, SNewLockData ** ppNewLockDatas, LPWSTR ** ppwszLockTokens) { HRESULT hr = S_OK;
auto_co_task_mem<SNewLockData> a_pNewLockData; auto_co_task_mem<LPWSTR> a_ppwszLockToken; DWORD dwLocksFound;
{ CSynchronizedReadBlock srb(m_mrwCache); COpGatherLockData op(pwszPath, dwLockType); op.Invoke(m_lockCacheByName);
hr = op.HrLocksFound(&dwLocksFound); if (FAILED(hr)) { goto ret; }
a_pNewLockData = static_cast<SNewLockData *>(CoTaskMemAlloc(sizeof(SNewLockData) * dwLocksFound)); if (NULL == a_pNewLockData.get()) { hr = E_OUTOFMEMORY; goto ret; } a_ppwszLockToken = static_cast<LPWSTR *>(CoTaskMemAlloc(sizeof(LPCWSTR) * dwLocksFound)); if (NULL == a_ppwszLockToken.get()) { hr = E_OUTOFMEMORY; goto ret; }
hr = op.HrGetData(a_pNewLockData.get(), a_ppwszLockToken.get()); if (FAILED(hr)) { goto ret; } }
*pdwLocksFound = dwLocksFound; *ppNewLockDatas = a_pNewLockData.relinquish(); *ppwszLockTokens = a_ppwszLockToken.relinquish();
ret:
return hr; } };
// DAV File Handle Cache
//
class CFileHandleCache : public IFileHandleCache { static BOOL s_fHasBeenStarted; static LONG s_cActiveComponents;
LONG m_cRef;
public:
static BOOL FNoActiveComponents();
// Constructor
//
CFileHandleCache(); virtual ~CFileHandleCache();
// IUnknown
//
virtual HRESULT __stdcall QueryInterface(REFIID iid, void ** ppvObject); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release();
// IFileHandleCache
//
virtual HRESULT __stdcall HrRegisterWorkerProcess(DWORD dwProcessId); virtual HRESULT _stdcall HrGetGUIDString( UINT cchBufferLen, WCHAR * pwszGUIDString, UINT * pcchGUIDString);
virtual HRESULT __stdcall HrGetNewLockData(DWORD_PTR hFile, DWORD dwProcessId, DWORD dwSid, BYTE * pbSid, SNewLockData * pnld, UINT cchBufferLen, WCHAR * pwszLockToken, UINT * pcchLockToken);
virtual HRESULT __stdcall HrGetLockData(LARGE_INTEGER liLockID, DWORD dwSid, BYTE * pbSid, LPCWSTR pwszPath, DWORD dwTimeout, SNewLockData *pnld, SLockHandleData * plhd, UINT cchBufferLen, WCHAR * pwszLockToken, UINT * pcchLockToken);
virtual HRESULT __stdcall HrCheckLockID(LARGE_INTEGER liLockID, DWORD dwSid, BYTE * pbSid, LPCWSTR pwszPath);
virtual HRESULT __stdcall HrDeleteLock(LARGE_INTEGER liLockID);
virtual HRESULT __stdcall HrGetAllLockDataForName(LPCWSTR pwszPath, DWORD dwLockType, DWORD * pdwLocksFound, SNewLockData ** ppNewLockDatas, LPWSTR ** ppwszLockTokens); };
BOOL CFileHandleCache::s_fHasBeenStarted = FALSE; LONG CFileHandleCache::s_cActiveComponents = 0;
CFileHandleCache::FNoActiveComponents() { if (0 == InterlockedCompareExchange(&s_cActiveComponents, 0, 0)) { return s_fHasBeenStarted; } else { return FALSE; } }
CFileHandleCache::CFileHandleCache() : m_cRef(1) { InterlockedIncrement(&s_cActiveComponents); s_fHasBeenStarted = TRUE; }
CFileHandleCache::~CFileHandleCache() { InterlockedDecrement(&s_cActiveComponents); }
HRESULT CFileHandleCache::QueryInterface(REFIID iid, void ** ppvObject) { HRESULT hr = S_OK; if ((IID_IUnknown == iid) || (IID_IFileHandleCache == iid)) { AddRef(); *ppvObject = static_cast<IFileHandleCache *>(this); } else { *ppvObject = NULL; hr = E_NOINTERFACE; }
return hr; }
ULONG CFileHandleCache::AddRef() { return InterlockedIncrement(&m_cRef); }
ULONG CFileHandleCache::Release() { if (0 == InterlockedDecrement(&m_cRef)) { delete this; return 0; }
return m_cRef; }
HRESULT CFileHandleCache::HrRegisterWorkerProcess(DWORD dwProcessId) { HRESULT hr = S_OK; auto_handle<HANDLE> a_hWP; // Open the worker process handle so that we can synchronize on it
//
a_hWP = OpenProcess(SYNCHRONIZE, FALSE, dwProcessId); if (NULL == a_hWP.get()) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("Failed to open worker process handle 0x%08lX\n", hr); goto ret; }
// Add the handle to the handle pool, so that we could listen on it
//
hr = CHandlePool::Instance().HrAddHandle(a_hWP.get()); if (FAILED(hr)) { DebugTrace ("Failed to add worker process handle 0x%08lX\n", hr); goto ret; } a_hWP.relinquish();
ret: return hr; }
HRESULT CFileHandleCache::HrGetGUIDString( UINT cchBufferLen, WCHAR * pwszGUIDString, UINT * pcchGUIDString) { return CLockCache::Instance().HrGetGUIDString(cchBufferLen, pwszGUIDString, pcchGUIDString); }
HRESULT CFileHandleCache::HrGetNewLockData(DWORD_PTR hFile, DWORD dwProcessId, DWORD dwSid, BYTE * pbSid, SNewLockData * pnld, UINT cchBufferLen, WCHAR * pwszLockToken, UINT * pcchLockToken) { return CLockCache::Instance().HrGetNewLockData(reinterpret_cast<HANDLE>(hFile), dwProcessId, dwSid, pbSid, pnld, cchBufferLen, pwszLockToken, pcchLockToken); }
HRESULT CFileHandleCache::HrGetLockData(LARGE_INTEGER liLockID, DWORD dwSid, BYTE * pbSid, LPCWSTR pwszPath, DWORD dwTimeout, SNewLockData *pnld, SLockHandleData * plhd, UINT cchBufferLen, WCHAR * pwszLockToken, UINT * pcchLockToken) { return CLockCache::Instance().HrGetLockData(liLockID, dwSid, pbSid, pwszPath, dwTimeout, pnld, plhd, cchBufferLen, pwszLockToken, pcchLockToken); }
HRESULT CFileHandleCache::HrCheckLockID(LARGE_INTEGER liLockID, DWORD dwSid, BYTE * pbSid, LPCWSTR pwszPath) { return CLockCache::Instance().HrGetLockData(liLockID, dwSid, pbSid, pwszPath, 0, NULL, NULL, 0, NULL, NULL); }
HRESULT CFileHandleCache::HrDeleteLock(LARGE_INTEGER liLockID) { return CLockCache::Instance().HrDeleteLock(liLockID); }
HRESULT CFileHandleCache::HrGetAllLockDataForName(LPCWSTR pwszPath, DWORD dwLockType, DWORD * pdwLocksFound, SNewLockData ** ppNewLockDatas, LPWSTR ** ppwszLockTokens) { return CLockCache::Instance().HrGetAllLockDataForName(pwszPath, dwLockType, pdwLocksFound, ppNewLockDatas, ppwszLockTokens); }
// DAV File Handle Cache class factory
//
class CFileHandleCacheClassFactory : public IClassFactory { // Count of locks
//
static LONG s_cServerLocks;
static IUnknown * s_pIClassFactory; static DWORD s_dwRegister;
LONG m_cRef;
public:
static HRESULT HrStartFactory(); static HRESULT HrStopFactory(); static BOOL FServerNotLocked();
// Constructor
//
CFileHandleCacheClassFactory();
// IUnknown
//
virtual HRESULT __stdcall QueryInterface(REFIID iid, void** ppvObject) ; virtual ULONG __stdcall AddRef() ; virtual ULONG __stdcall Release() ; // IClassFactory
//
virtual HRESULT __stdcall CreateInstance(IUnknown* pUnkOuter, REFIID iid, void ** ppvObject); virtual HRESULT __stdcall LockServer(BOOL fLock); };
LONG CFileHandleCacheClassFactory::s_cServerLocks = 0; IUnknown * CFileHandleCacheClassFactory::s_pIClassFactory = NULL; DWORD CFileHandleCacheClassFactory::s_dwRegister = 0;
HRESULT CFileHandleCacheClassFactory::HrStartFactory() { HRESULT hr = S_OK; auto_ref_ptr<IUnknown> a_pIClassFactory; DWORD dwRegister;
a_pIClassFactory.take_ownership(new CFileHandleCacheClassFactory()); if (NULL == a_pIClassFactory.get()) { hr = E_OUTOFMEMORY; goto ret; }
hr = CoRegisterClassObject(CLSID_FileHandleCache, a_pIClassFactory.get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister); if (FAILED(hr)) { goto ret; }
s_pIClassFactory = a_pIClassFactory.relinquish(); s_dwRegister = dwRegister;
ret:
return hr; }
HRESULT CFileHandleCacheClassFactory::HrStopFactory() { HRESULT hr = S_OK; IUnknown * pIClassFactory; DWORD dwRegister; Assert(s_pIClassFactory); Assert(s_dwRegister);
pIClassFactory = s_pIClassFactory; dwRegister = s_dwRegister;
hr = CoRevokeClassObject(dwRegister); if (FAILED(hr)) { goto ret; }
pIClassFactory->Release();
s_pIClassFactory = NULL; s_dwRegister = 0;
ret:
return hr; }
BOOL CFileHandleCacheClassFactory::FServerNotLocked() { return (0 == InterlockedCompareExchange(&s_cServerLocks, 0, 0)); }
CFileHandleCacheClassFactory::CFileHandleCacheClassFactory() : m_cRef(1) { }
HRESULT CFileHandleCacheClassFactory::QueryInterface(REFIID iid, void** ppvObject) { HRESULT hr = S_OK; if ((IID_IUnknown == iid) || (IID_IClassFactory == iid)) { AddRef(); *ppvObject = static_cast<IClassFactory *>(this); } else { *ppvObject = NULL; hr = E_NOINTERFACE; } return hr; }
ULONG CFileHandleCacheClassFactory::AddRef() { return InterlockedIncrement(&m_cRef); }
ULONG CFileHandleCacheClassFactory::Release() { ULONG cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; return 0; }
return cRef; }
HRESULT CFileHandleCacheClassFactory::CreateInstance(IUnknown* pUnkOuter, REFIID iid, void ** ppvObject) { HRESULT hr = S_OK; auto_ref_ptr<IUnknown> a_pIFileHandleCache; if (NULL != pUnkOuter) { // Don't allow aggregation. No need for it.
//
hr = CLASS_E_NOAGGREGATION; goto ret; }
a_pIFileHandleCache.take_ownership(new CFileHandleCache()); if (NULL == a_pIFileHandleCache.get()) { hr = E_OUTOFMEMORY; goto ret; }
hr = a_pIFileHandleCache->QueryInterface(iid, ppvObject); if (FAILED(hr)) { goto ret; }
ret: return hr; }
HRESULT CFileHandleCacheClassFactory::LockServer(BOOL fLock) { if (fLock) { InterlockedIncrement(&s_cServerLocks); } else { InterlockedDecrement(&s_cServerLocks); }
return S_OK; }
BOOL FCanUnloadServer() { return (CFileHandleCache::FNoActiveComponents() && CFileHandleCacheClassFactory::FServerNotLocked()); }
// CHandlePool class
//
DWORD CHandlePool::DwWaitOnWPs (PVOID pvThreadData) { DWORD dwRet;
CHandleArrayForHandlePool * pHandleArrayForHandlePool; pHandleArrayForHandlePool = Instance().m_listHandleArrayForHandlePool.GetListHead(); Assert(NULL != pHandleArrayForHandlePool); while (!FCanUnloadServer()) { dwRet = WaitForMultipleObjects (pHandleArrayForHandlePool->UiGetHandleCount(), // nCount
pHandleArrayForHandlePool->PhGetHandles(), // lpHandles,
FALSE, // fWaitAll,
WAIT_POLL_PERIOD); // wait for specified period
switch (dwRet) { case WAIT_TIMEOUT: pHandleArrayForHandlePool = pHandleArrayForHandlePool->GetNextListElementInCircle(); break; case WAIT_OBJECT_0 + CHandleArrayForHandlePool::ih_external_update:
// Allow the updates to execute and then get the list head
// as the array you had may be already gone.
//
Instance().AllowUpdatesToExecute(); pHandleArrayForHandlePool = Instance().m_listHandleArrayForHandlePool.GetListHead(); break;
case WAIT_OBJECT_0 + CHandleArrayForHandlePool::ih_delete_timer: CLockCache::Instance().DeleteLockTimerIfNotUsed(); break;
default:
Assert(CHandleArrayForHandlePool::ih_wp <= pHandleArrayForHandlePool->UiGetHandleCount()); if ((WAIT_OBJECT_0 + CHandleArrayForHandlePool::ih_wp <= dwRet) && (WAIT_OBJECT_0 + pHandleArrayForHandlePool->UiGetHandleCount() - 1 >= dwRet)) { // Remove the handle and then get the list head
// as the array you had may be already gone.
//
Instance().RemoveHandleInternal(pHandleArrayForHandlePool, dwRet - WAIT_OBJECT_0); pHandleArrayForHandlePool = Instance().m_listHandleArrayForHandlePool.GetListHead(); } break; } };
// This call will loose all updates to be executed so incoming COM calls would
// not block waiting on permission to execute the update
//
Instance().AllowShutdownToExecute();
// This call will block untill all expiry callbacks have completed
//
CLockCache::Instance().DeleteLockTimerFinal();
// Post the quit message. We do this in a loop to catch
// the case if this code has been reached faster than
// the message queue was created on the original thread
//
while (0 == PostThreadMessage(s_dwMainTID, WM_QUIT, 0, 0)) { Sleep(WAIT_POLL_PERIOD); }
return S_OK; }
// ===============================================================
// File lock cache server registration routines
// ===============================================================
HRESULT HrRegisterServer(LPCWSTR pwszModulePath, // EXE module path
UINT cchModulePath, // Module path length
LPCWSTR pwszModuleName, // EXE module name
UINT cchModuleName, // Module name length
const CLSID& clsid) // Class ID
{ HRESULT hr = S_OK; DWORD dwResult;
SECURITY_DESCRIPTOR sdAbsolute; CStackBuffer<BYTE> pSidOwnerAndGroup; CStackBuffer<BYTE> pSidIIS_WPG; CStackBuffer<BYTE> pSidLocalService; CStackBuffer<BYTE> pSidNetworkService; CStackBuffer<WCHAR> pwszDomainName; DWORD cbSidOwnerAndGroup = 0; DWORD cbSidIIS_WPG = 0; DWORD cbSidLocalService = 0; DWORD cbSidNetworkService = 0; DWORD cchDomainName = 0; SID_NAME_USE snu; CStackBuffer<BYTE> pACL; DWORD cbACL = 0; CStackBuffer<BYTE> pSelfRelativeSD; DWORD cbSelfRelativeSD = 0;
CStackBuffer<WCHAR, (MAX_PATH + 1) * sizeof(WCHAR)> pwszKey; CRegKey regKeyCLSID; CRegKey regKeyCLSIDLocalServer; CRegKey regKeyAppIdCLSID; CRegKey regKeyAppIdModule;
auto_co_task_mem<WCHAR> pwszCLSID; UINT cchCLSID;
// First of all try to build up security descriptor for launch permissions
//
// Initialize security descriptor
//
if (FALSE == InitializeSecurityDescriptor(&sdAbsolute, SECURITY_DESCRIPTOR_REVISION)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Get the SID for the Administrators group
//
// Get the size of memory needed for the sid.
//
if (FALSE == CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, NULL, &cbSidOwnerAndGroup)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidOwnerAndGroup); if (!pSidOwnerAndGroup.resize(cbSidOwnerAndGroup)) { hr = E_OUTOFMEMORY; goto ret; }
// Ok now we can get the SID
//
if (FALSE == CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, pSidOwnerAndGroup.get(), &cbSidOwnerAndGroup)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Set security descriptor owner and group
//
if (FALSE == SetSecurityDescriptorOwner(&sdAbsolute, pSidOwnerAndGroup.get(), FALSE)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == SetSecurityDescriptorGroup(&sdAbsolute, pSidOwnerAndGroup.get(), FALSE)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Lookup IIS worker process group SID
//
if (FALSE == LookupAccountNameW(NULL, gc_wszIIS_WPG, NULL, &cbSidIIS_WPG, NULL, &cchDomainName, &snu)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidIIS_WPG); if (!pSidIIS_WPG.resize(cbSidIIS_WPG)) { hr = E_OUTOFMEMORY; goto ret; }
if (!pwszDomainName.resize(cchDomainName * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; }
if (FALSE == LookupAccountNameW(NULL, gc_wszIIS_WPG, pSidIIS_WPG.get(), &cbSidIIS_WPG, pwszDomainName.get(), &cchDomainName, &snu)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (SidTypeAlias != snu) { hr = E_FAIL; goto ret; }
// Get the SID for the local service account
//
// Get the size of memory needed for the sid.
//
if (FALSE == CreateWellKnownSid(WinLocalServiceSid, NULL, NULL, &cbSidLocalService)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidLocalService); if (!pSidLocalService.resize(cbSidLocalService)) { hr = E_OUTOFMEMORY; goto ret; }
// Ok now we can get the SID
//
if (FALSE == CreateWellKnownSid(WinLocalServiceSid, NULL, pSidLocalService.get(), &cbSidLocalService)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Get the SID for the network service account
//
// Get the size of memory needed for the sid.
//
if (FALSE == CreateWellKnownSid(WinNetworkServiceSid, NULL, NULL, &cbSidNetworkService)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidNetworkService); if (!pSidNetworkService.resize(cbSidNetworkService)) { hr = E_OUTOFMEMORY; goto ret; }
// Ok now we can get the SID
//
if (FALSE == CreateWellKnownSid(WinNetworkServiceSid, NULL, pSidNetworkService.get(), &cbSidNetworkService)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; } // Set up the launch permissions ACL
// We will be adding 4 aces
// 1. IIS_WPG
// 2. Administrators
// 3. Local Service
// 4. Network Service
//
cbACL = sizeof(ACL) + (4 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof (DWORD))) + GetLengthSid(pSidIIS_WPG.get()) + GetLengthSid(pSidOwnerAndGroup.get()) + GetLengthSid(pSidLocalService.get()) + GetLengthSid(pSidNetworkService.get()); if (!pACL.resize(cbACL)) { hr = E_OUTOFMEMORY; goto ret; }
if (FALSE == InitializeAcl(reinterpret_cast<PACL>(pACL.get()), cbACL, ACL_REVISION)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidIIS_WPG.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidOwnerAndGroup.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidLocalService.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidNetworkService.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; } if (FALSE == SetSecurityDescriptorDacl(&sdAbsolute, TRUE, reinterpret_cast<PACL>(pACL.get()), FALSE)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Make self relative security descriptor out of absolute for storing in the registry
//
if (FALSE == MakeSelfRelativeSD(&sdAbsolute, NULL, &cbSelfRelativeSD)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
if (!pSelfRelativeSD.resize(cbSelfRelativeSD)) { hr = E_OUTOFMEMORY; goto ret; }
if (FALSE == MakeSelfRelativeSD(&sdAbsolute, pSelfRelativeSD.get(), &cbSelfRelativeSD)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Procceed with setting up registry keys
//
hr = StringFromCLSID(CLSID_FileHandleCache, &pwszCLSID); if (FAILED(hr)) { goto ret; } cchCLSID = static_cast<UINT>(wcslen(pwszCLSID.get()));
if (!pwszKey.resize((gc_cch_CLSIDWW + cchCLSID + 1) * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; } memcpy(pwszKey.get(), gc_wsz_CLSIDWW, gc_cch_CLSIDWW * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_CLSIDWW, pwszCLSID.get(), cchCLSID * sizeof(WCHAR)); pwszKey[gc_cch_CLSIDWW + cchCLSID] = L'\0'; dwResult = regKeyCLSID.DwCreate(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
dwResult = regKeyCLSID.DwSetValue(NULL, REG_SZ, gc_wsz_WebDAVFileHandleCache, (gc_cch_WebDAVFileHandleCache + 1) * sizeof(WCHAR)); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
dwResult = regKeyCLSID.DwSetValue(gc_wsz_AppID, REG_SZ, pwszCLSID.get(), (cchCLSID + 1) * sizeof(WCHAR)); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
if (!pwszKey.resize((gc_cch_CLSIDWW + cchCLSID + gc_cch_WWLocalServer32 + 1) * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; } memcpy(pwszKey.get(), gc_wsz_CLSIDWW, gc_cch_CLSIDWW * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_CLSIDWW, pwszCLSID.get(), cchCLSID * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_CLSIDWW + cchCLSID, gc_wsz_WWLocalServer32, gc_cch_WWLocalServer32 * sizeof(WCHAR)); pwszKey[gc_cch_CLSIDWW + cchCLSID + gc_cch_WWLocalServer32] = L'\0';
dwResult = regKeyCLSIDLocalServer.DwCreate(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
dwResult = regKeyCLSIDLocalServer.DwSetValue(NULL, REG_SZ, pwszModulePath, (cchModulePath + 1) * sizeof(WCHAR)); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; } if (!pwszKey.resize((gc_cch_AppIDWW + cchCLSID + 1) * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; } memcpy(pwszKey.get(), gc_wsz_AppIDWW, gc_cch_AppIDWW * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_AppIDWW, pwszCLSID.get(), cchCLSID * sizeof(WCHAR)); pwszKey[gc_cch_AppIDWW + cchCLSID] = L'\0';
dwResult = regKeyAppIdCLSID.DwCreate(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
dwResult = regKeyAppIdCLSID.DwSetValue(NULL, REG_SZ, gc_wsz_WebDAVFileHandleCache, (gc_cch_WebDAVFileHandleCache + 1) * sizeof(WCHAR)); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
dwResult = regKeyAppIdCLSID.DwSetValue(gc_wszLaunchPermission, REG_BINARY, pSelfRelativeSD.get(), cbSelfRelativeSD); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; } if (!pwszKey.resize((gc_cch_AppIDWW + cchModuleName + 1) * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; } memcpy(pwszKey.get(), gc_wsz_AppIDWW, gc_cch_AppIDWW * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_AppIDWW, pwszModuleName, cchModuleName * sizeof(WCHAR)); pwszKey[gc_cch_AppIDWW + cchModuleName] = L'\0';
dwResult = regKeyAppIdModule.DwCreate(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
dwResult = regKeyAppIdModule.DwSetValue(gc_wsz_AppID, REG_SZ, pwszCLSID.get(), (cchCLSID + 1) * sizeof(WCHAR)); if (ERROR_SUCCESS != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
ret:
return hr; }
HRESULT HrUnregisterServer(LPCWSTR pwszModuleName, // EXE module name
UINT cchModuleName, // Module name length
const CLSID& clsid) // Class ID)
{ HRESULT hr = S_OK; DWORD dwResult; CStackBuffer<WCHAR, (MAX_PATH + 1) * sizeof(WCHAR)> pwszKey;
auto_co_task_mem<WCHAR> pwszCLSID; UINT cchCLSID;
hr = StringFromCLSID(CLSID_FileHandleCache, &pwszCLSID); if (FAILED(hr)) { goto ret; } cchCLSID = static_cast<UINT>(wcslen(pwszCLSID.get()));
if (!pwszKey.resize((gc_cch_CLSIDWW + cchCLSID + gc_cch_WWLocalServer32 + 1) * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; } memcpy(pwszKey.get(), gc_wsz_CLSIDWW, gc_cch_CLSIDWW * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_CLSIDWW, pwszCLSID.get(), cchCLSID * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_CLSIDWW + cchCLSID, gc_wsz_WWLocalServer32, gc_cch_WWLocalServer32 * sizeof(WCHAR)); pwszKey[gc_cch_CLSIDWW + cchCLSID + gc_cch_WWLocalServer32] = L'\0';
dwResult = RegDeleteKeyW(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult && ERROR_FILE_NOT_FOUND != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
pwszKey[gc_cch_CLSIDWW + cchCLSID] = L'\0'; dwResult = RegDeleteKeyW(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult && ERROR_FILE_NOT_FOUND != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
if (!pwszKey.resize((gc_cch_AppIDWW + cchCLSID + 1) * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; } memcpy(pwszKey.get(), gc_wsz_AppIDWW, gc_cch_AppIDWW * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_AppIDWW, pwszCLSID.get(), cchCLSID * sizeof(WCHAR)); pwszKey[gc_cch_AppIDWW + cchCLSID] = L'\0';
dwResult = RegDeleteKeyW(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult && ERROR_FILE_NOT_FOUND != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
if (!pwszKey.resize((gc_cch_AppIDWW + cchModuleName + 1) * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; } memcpy(pwszKey.get(), gc_wsz_AppIDWW, gc_cch_AppIDWW * sizeof(WCHAR)); memcpy(pwszKey.get() + gc_cch_AppIDWW, pwszModuleName, cchModuleName * sizeof(WCHAR)); pwszKey[gc_cch_AppIDWW + cchModuleName] = L'\0';
dwResult = RegDeleteKeyW(HKEY_CLASSES_ROOT, pwszKey.get()); if (ERROR_SUCCESS != dwResult && ERROR_FILE_NOT_FOUND != dwResult) { hr = HRESULT_FROM_WIN32(dwResult); goto ret; }
ret:
return hr; }
HRESULT HrInitCOMSecurity () { HRESULT hr = S_OK;
SECURITY_DESCRIPTOR sdAbsolute; CStackBuffer<BYTE> pSidOwnerAndGroup; CStackBuffer<BYTE> pSidIIS_WPG; CStackBuffer<BYTE> pSidLocalService; CStackBuffer<BYTE> pSidNetworkService; CStackBuffer<WCHAR> pwszDomainName; DWORD cbSidOwnerAndGroup = 0; DWORD cbSidIIS_WPG = 0; DWORD cbSidLocalService = 0; DWORD cbSidNetworkService = 0; DWORD cchDomainName = 0; SID_NAME_USE snu; CStackBuffer<BYTE> pACL; DWORD cbACL = 0;
// First of all try to build up security descriptor for access permissions
//
// Initialize security descriptor
//
if (FALSE == InitializeSecurityDescriptor(&sdAbsolute, SECURITY_DESCRIPTOR_REVISION)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Lookup owner and primary group SID
//
// Get the size of memory needed for the sid.
//
if (FALSE == CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, NULL, &cbSidOwnerAndGroup)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidOwnerAndGroup); if (!pSidOwnerAndGroup.resize(cbSidOwnerAndGroup)) { hr = E_OUTOFMEMORY; goto ret; }
// Ok now we can get the SID
//
if (FALSE == CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, pSidOwnerAndGroup.get(), &cbSidOwnerAndGroup)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Set security descriptor owner and group
//
if (FALSE == SetSecurityDescriptorOwner(&sdAbsolute, pSidOwnerAndGroup.get(), FALSE)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == SetSecurityDescriptorGroup(&sdAbsolute, pSidOwnerAndGroup.get(), FALSE)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Lookup IIS worker process group SID
//
if (FALSE == LookupAccountNameW(NULL, gc_wszIIS_WPG, NULL, &cbSidIIS_WPG, NULL, &cchDomainName, &snu)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidIIS_WPG); if (!pSidIIS_WPG.resize(cbSidIIS_WPG)) { hr = E_OUTOFMEMORY; goto ret; }
if (!pwszDomainName.resize(cchDomainName * sizeof(WCHAR))) { hr = E_OUTOFMEMORY; goto ret; }
if (FALSE == LookupAccountNameW(NULL, gc_wszIIS_WPG, pSidIIS_WPG.get(), &cbSidIIS_WPG, pwszDomainName.get(), &cchDomainName, &snu)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (SidTypeAlias != snu) { hr = E_FAIL; goto ret; }
// Get the SID for the local service account
//
// Get the size of memory needed for the sid.
//
if (FALSE == CreateWellKnownSid(WinLocalServiceSid, NULL, NULL, &cbSidLocalService)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidLocalService); if (!pSidLocalService.resize(cbSidLocalService)) { hr = E_OUTOFMEMORY; goto ret; }
// Ok now we can get the SID
//
if (FALSE == CreateWellKnownSid(WinLocalServiceSid, NULL, pSidLocalService.get(), &cbSidLocalService)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
// Get the SID for the network service account
//
// Get the size of memory needed for the sid.
//
if (FALSE == CreateWellKnownSid(WinNetworkServiceSid, NULL, NULL, &cbSidNetworkService)) { hr = HRESULT_FROM_WIN32(GetLastError()); if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr) { goto ret; } else { hr = S_OK; } }
Assert (0 < cbSidNetworkService); if (!pSidNetworkService.resize(cbSidNetworkService)) { hr = E_OUTOFMEMORY; goto ret; }
// Ok now we can get the SID
//
if (FALSE == CreateWellKnownSid(WinNetworkServiceSid, NULL, pSidNetworkService.get(), &cbSidNetworkService)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; } // Set up the launch permissions ACL
// We will be adding 4 aces
// 1. IIS_WPG
// 2. Administrators
// 3. Local Service
// 4. Network Service
//
cbACL = sizeof(ACL) + (4 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof (DWORD))) + GetLengthSid(pSidIIS_WPG.get()) + GetLengthSid(pSidOwnerAndGroup.get()) + GetLengthSid(pSidLocalService.get()) + GetLengthSid(pSidNetworkService.get()); if (!pACL.resize(cbACL)) { hr = E_OUTOFMEMORY; goto ret; }
if (FALSE == InitializeAcl(reinterpret_cast<PACL>(pACL.get()), cbACL, ACL_REVISION)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidIIS_WPG.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidOwnerAndGroup.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidLocalService.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
if (FALSE == AddAccessAllowedAce(reinterpret_cast<PACL>(pACL.get()), ACL_REVISION, COM_RIGHTS_EXECUTE, pSidNetworkService.get())) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; } if (FALSE == SetSecurityDescriptorDacl(&sdAbsolute, TRUE, reinterpret_cast<PACL>(pACL.get()), FALSE)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ret; }
hr = CoInitializeSecurity(&sdAbsolute, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_DYNAMIC_CLOAKING | EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL, NULL); if (FAILED(hr)) { goto ret; } ret:
return hr; }
HRESULT HrExecuteServer() { HRESULT hr = S_OK; HANDLE hThread; MSG msg;
// Save of the current thread ID so that the thread we will create
// would know who post pessages to
//
s_dwMainTID = GetCurrentThreadId(); // Now create thread that waits on events and WP handles
//
hThread = CreateThread (NULL, // lpThreadAttributes
0, // dwStackSize, ignored
CHandlePool::DwWaitOnWPs, // lpStartAddress
NULL, // lpParam
0, // Start immediately
NULL); // lpThreadId
if (NULL == hThread) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("CreateThread failed 0x%08lX\n", hr); goto ret; } // We need to close the handle to avoid having the thread object remains in the system forever.
//
CloseHandle(hThread);
// Wait for shutdown message that will be posted by the thread we created above
//
while (::GetMessage(&msg, 0, 0, 0)) { ::DispatchMessage(&msg) ; }
ret:
return msg.wParam; }
// ===============================================================
// Main Routine
// ===============================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HRESULT hr = S_OK;
BOOL fStartDAVFileCacheServer = FALSE; BOOL fHeapInitialized = FALSE; BOOL fCOMInitialized = FALSE; BOOL fClassFactoryStarted = FALSE;
// Setup the heap for the process
//
if (!g_heap.FInit()) { hr = E_OUTOFMEMORY; DebugTrace ("Heap initialization failed 0x%08lX\n", hr); goto ret; } fHeapInitialized = TRUE;
{ CStackBuffer<WCHAR, (MAX_PATH + 1) * sizeof(WCHAR)> pwszModulePath; UINT cchModulePath = MAX_PATH + 1;
LPWSTR pwszModuleName; UINT cchModuleName;
LPWSTR * argv; INT argc = 0;
cchModulePath = GetModuleFileNameW(NULL, pwszModulePath.get(), cchModulePath); if (0 == cchModulePath) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("Getting module path failed 0x%08lX\n", hr); goto ret; }
pwszModuleName = wcsrchr(pwszModulePath.get(), L'\\'); if (NULL == pwszModuleName) { pwszModuleName = wcsrchr(pwszModulePath.get(), L':'); } if (pwszModuleName) { while (L'\\' == pwszModuleName[0] || L':' == pwszModuleName[0]) { pwszModuleName++; } } else { pwszModuleName = pwszModulePath.get(); } cchModuleName = static_cast<UINT>(wcslen(pwszModuleName));
argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (NULL == argv) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("Getting argument list failed 0x%08lX\n", hr); goto ret; }
// Do not fail up to the GlobalFree call
// If command line has parameters...
//
if (2 == argc) { if (!_wcsicmp(argv[1], gc_wsz_RegServer)) { hr = HrRegisterServer(pwszModulePath.get(), cchModulePath, pwszModuleName, cchModuleName, CLSID_FileHandleCache); } else if (!_wcsicmp(argv[1], gc_wsz_UnregServer)) { hr = HrUnregisterServer(pwszModuleName, cchModuleName, CLSID_FileHandleCache); } else if (!_wcsicmp(argv[1], gc_wsz_Embedding)) { fStartDAVFileCacheServer = TRUE; } }
if (NULL != GlobalFree(argv)) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugTrace ("Freeing argument list failed 0x%08lX\n", hr); goto ret; }
if (!fStartDAVFileCacheServer) { goto ret; } }
// Setup lock cache
//
hr = CLockCache::CreateInstance().HrInitialize(); if (FAILED(hr)) { DebugTrace ("Lock cache initialization failed 0x%08lX\n", hr); goto ret; }
// Setup handle pool for worker process handles
//
hr = CHandlePool::CreateInstance().HrInitialize(); if (FAILED(hr)) { DebugTrace ("Handle pool initialization failed 0x%08lX\n", hr); goto ret; }
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY); if (FAILED(hr)) { DebugTrace ("COM initialization failed 0x%08lX\n", hr); goto ret; } fCOMInitialized = TRUE;
hr = HrInitCOMSecurity (); if (FAILED(hr)) { DebugTrace ("COM security initialization failed 0x%08lX\n", hr); goto ret; }
hr = CFileHandleCacheClassFactory::HrStartFactory(); if (FAILED(hr)) { DebugTrace ("File handle cache class factory startup failed 0x%08lX\n", hr); goto ret; } fClassFactoryStarted = TRUE;
hr = HrExecuteServer(); if (FAILED(hr)) { DebugTrace ("Run failed 0x%08lX\n", hr); goto ret; }
ret:
if (fClassFactoryStarted) { (VOID) CFileHandleCacheClassFactory::HrStopFactory(); } if (fCOMInitialized) { CoUninitialize(); }
// Singleton takes care of tracking if it was initialized or not itself,
// so we simply always call DestroyInstance()
//
CHandlePool::DestroyInstance(); CLockCache::DestroyInstance();
if (fHeapInitialized) { g_heap.Deinit(); }
return hr; }
|