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.
3314 lines
77 KiB
3314 lines
77 KiB
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// 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;
|
|
}
|