914 lines
24 KiB
C++
914 lines
24 KiB
C++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// 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 <shlkcache.h>
|
|
#include <caldbg.h>
|
|
|
|
// Code borrowed from htpext mem.cpp so we have use of the global heap.
|
|
#define g_szMemDll L"staxmem.dll"
|
|
#include <autoptr.h>
|
|
#include <mem.h>
|
|
#include <memx.h>
|
|
#include <implstub.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 ); }
|
|
|
|
VOID IncrementGlobalPerfCounter( UINT iCounter )
|
|
{
|
|
// exceptions in DAVCData are not counted in perf counters (for now)
|
|
// BUGBUG: Decide if they should be.
|
|
}
|
|
|
|
#ifdef DBG
|
|
BOOL g_fDavTrace = FALSE;
|
|
const CHAR gc_szDbgIni[] = "DAVCData.INI";
|
|
#endif
|
|
|
|
// Signature must match that of HTTPEXT's so that the shared memory heap is
|
|
// shared between the two processes.
|
|
//
|
|
EXTERN_C const WCHAR gc_wszSignature[] = L"HTTPEXT";
|
|
|
|
// Timer constants and globals.
|
|
//
|
|
const DWORD WAIT_PERIOD = 60000; // 1 min = 60 sec = 60,000 milliseconds
|
|
|
|
// Event used to notify the existence of davcdata process
|
|
//
|
|
HANDLE g_hEventDavCDataUp = NULL;
|
|
|
|
// Array of handles we could wait on
|
|
//
|
|
class CDavCDataHandles
|
|
{
|
|
enum
|
|
{
|
|
MAX_TIMER_HANDLE = 8,
|
|
MAX_WAIT_HANDLE = 128
|
|
};
|
|
enum
|
|
{
|
|
ih_new_wp,
|
|
ih_delete_timer,
|
|
c_events,
|
|
ih_wp = c_events
|
|
};
|
|
|
|
|
|
private:
|
|
|
|
HANDLE m_rgHandles[MAX_WAIT_HANDLE]; // Array of handles
|
|
ULONG m_cHandlesWaiting;
|
|
ULONG m_uiAddStart; // Start index of handles added
|
|
ULONG m_uiAdd; // next index to add a handle
|
|
|
|
// Array of timers to be deleted.
|
|
// Using a fixed array, I don't believe we have more
|
|
//
|
|
HANDLE m_rgTimers[MAX_TIMER_HANDLE];
|
|
ULONG m_cTimersToDelete;
|
|
|
|
|
|
LONG m_lInUse;
|
|
|
|
public:
|
|
CDavCDataHandles() :
|
|
m_cHandlesWaiting(0),
|
|
m_cTimersToDelete(0),
|
|
m_uiAddStart(0),
|
|
m_uiAdd(0),
|
|
m_lInUse(0)
|
|
{}
|
|
|
|
VOID Lock()
|
|
{
|
|
// Simple spinlock
|
|
//
|
|
while (1 == InterlockedCompareExchange (&m_lInUse, 1, 0))
|
|
{
|
|
Sleep(1); // Sleep 1 millisecond
|
|
}
|
|
}
|
|
|
|
VOID Unlock()
|
|
{
|
|
Assert (1 == m_lInUse);
|
|
InterlockedDecrement(&m_lInUse);
|
|
}
|
|
|
|
ULONG CHandlesWaiting() { return m_cHandlesWaiting; }
|
|
HANDLE * PHandlesWaiting() { return m_rgHandles; }
|
|
|
|
SCODE ScInit();
|
|
BOOL FAddNewWP(DWORD dwClientProcess);
|
|
VOID Refresh();
|
|
BOOL FDeleteWPHandle (ULONG ulIndex);
|
|
VOID AddTimerToDelete (HANDLE hTimer);
|
|
VOID DeleteTimer();
|
|
|
|
static DWORD __stdcall DwWaitForWPShutdown (PVOID pvThreadData);
|
|
};
|
|
|
|
CDavCDataHandles g_handles;
|
|
|
|
// ===============================================================
|
|
// Supporting class definitions
|
|
// ===============================================================
|
|
|
|
#include "shlkcache.h"
|
|
|
|
// CWasLockCache holds the global information needed to handle
|
|
// any requests that the pipeline receives (or that bypass the pipeline)
|
|
// for setting up and maintaining the Shared Memory Lock Cache.
|
|
//
|
|
class CWasLockCache
|
|
{
|
|
private:
|
|
|
|
// Private functions for processing
|
|
//
|
|
LONG m_lTimerLaunched;
|
|
HANDLE m_hNewTimer;
|
|
|
|
// Shared Memory objects
|
|
//
|
|
SharedPtr<CInShLockCache> m_spCache;
|
|
SharedPtr<CInShCacheStatic> m_spStatic;
|
|
|
|
// NOT IMPLEMENTED
|
|
//
|
|
CWasLockCache& operator=( const CWasLockCache& );
|
|
CWasLockCache( const CWasLockCache& );
|
|
|
|
public:
|
|
|
|
CWasLockCache() {};
|
|
|
|
// Initialize sets up all variables that would normally be set in the constructor.
|
|
// It is done this way to avoid linking issues surronding when the global object
|
|
// g_wlc's constructor would be called.
|
|
//
|
|
VOID Initialize()
|
|
{
|
|
m_lTimerLaunched = 0;
|
|
m_hNewTimer = INVALID_HANDLE_VALUE;
|
|
};
|
|
|
|
HRESULT SetupSharedCache();
|
|
HANDLE SaveHandle(DWORD OrigProcess, HANDLE SavingHandle);
|
|
VOID LaunchLockTimer();
|
|
VOID DeleteLockTimer();
|
|
VOID ExpireLocks();
|
|
|
|
BOOL FEmpty() { return m_spCache.FIsNull() || m_spCache->FEmpty(); }
|
|
};
|
|
|
|
CWasLockCache g_wlc;
|
|
|
|
// Implement a waiting thread listens to WP shutdown event
|
|
//
|
|
DWORD __stdcall
|
|
CDavCDataHandles::DwWaitForWPShutdown(PVOID pvThreadData)
|
|
{
|
|
DWORD dwRet;
|
|
|
|
while (1)
|
|
{
|
|
dwRet = WaitForMultipleObjects (g_handles.CHandlesWaiting(), // nCount
|
|
g_handles.PHandlesWaiting(), // lpHandles,
|
|
FALSE, // fWaitAll,
|
|
INFINITE); // wait forever
|
|
switch (dwRet)
|
|
{
|
|
case WAIT_OBJECT_0 + ih_new_wp:
|
|
g_handles.Refresh();
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + ih_delete_timer:
|
|
g_handles.DeleteTimer();
|
|
break;
|
|
|
|
default:
|
|
if (FALSE == g_handles.FDeleteWPHandle(dwRet - WAIT_OBJECT_0))
|
|
{
|
|
// This means WaitForMultipleObject fails for some unknown reason
|
|
//
|
|
DebugTrace ("WaitForMultipleObject returns %d, last error = %d\n",
|
|
dwRet, GetLastError());
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
return 0;
|
|
}
|
|
|
|
// CDavCDataHandles functions
|
|
|
|
// CDavCDataHandles::ScInit
|
|
//
|
|
SCODE
|
|
CDavCDataHandles::ScInit()
|
|
{
|
|
SCODE sc = S_OK;
|
|
HANDLE hWaitingThread;
|
|
|
|
// Create the event that used to notify the arrival of new event
|
|
//
|
|
m_rgHandles[ih_new_wp] = CreateEvent (NULL, // lpEventAttributes
|
|
FALSE, // bManualReset
|
|
FALSE, // bInitialState
|
|
NULL);
|
|
if (m_rgHandles[ih_new_wp] == NULL)
|
|
{
|
|
DebugTrace ("CreateEvent failed %d\n", GetLastError());
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
goto ret;
|
|
}
|
|
|
|
// Create the event that listens for timer deletion
|
|
//
|
|
m_rgHandles[ih_delete_timer] = CreateEvent (NULL, // lpEventAttributes
|
|
FALSE, // bManualReset
|
|
FALSE, // bInitialState
|
|
NULL); // lpName
|
|
if (m_rgHandles[ih_delete_timer] == NULL)
|
|
{
|
|
DebugTrace ("CreateEvent failed %d\n", GetLastError());
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
goto ret;
|
|
}
|
|
|
|
m_cHandlesWaiting = c_events;
|
|
|
|
// Now create thread that waits on these events and wp handles
|
|
//
|
|
hWaitingThread = CreateThread (NULL, // lpThreadAttributes
|
|
0, // dwStackSize, ignored
|
|
CDavCDataHandles::DwWaitForWPShutdown, // lpStartAddress
|
|
NULL, // lpParam
|
|
0, // Start immediately
|
|
NULL); // lpThreadId
|
|
if (NULL == hWaitingThread)
|
|
{
|
|
DebugTrace ("HandleNewWorkerProcess - Failed to create thread\n");
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
goto ret;
|
|
}
|
|
|
|
// We don't need to thread handle. but we need to close the handle to avoid
|
|
// having the thread object remains in the system forever.
|
|
//
|
|
CloseHandle(hWaitingThread);
|
|
|
|
m_uiAddStart = c_events;
|
|
m_uiAdd = c_events;
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
// CDavCDataHandles::FAddNewWP
|
|
//
|
|
// The only thread call this function is ScNamedPipeListener,
|
|
//
|
|
BOOL
|
|
CDavCDataHandles::FAddNewWP (DWORD dwClientProcess)
|
|
{
|
|
// Open the worker process handle so that we can synchronize on
|
|
//
|
|
HANDLE hWP = OpenProcess(SYNCHRONIZE, false, dwClientProcess);
|
|
|
|
if (NULL == hWP)
|
|
{
|
|
DebugTrace ("Failed to open worker process, last error %d\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
Lock();
|
|
|
|
m_rgHandles[m_uiAdd++] = hWP;
|
|
|
|
Unlock();
|
|
|
|
// Now inform the waiting thread we have one more WP to wait for
|
|
//
|
|
SetEvent (m_rgHandles[ih_new_wp]);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// CDavCDataHandles::Refresh
|
|
//
|
|
// This method is called from waiting thread
|
|
//
|
|
VOID
|
|
CDavCDataHandles::Refresh ()
|
|
{
|
|
Lock();
|
|
for (ULONG i=m_uiAddStart; i<m_uiAdd; i++)
|
|
m_rgHandles[m_cHandlesWaiting++] = m_rgHandles[i];
|
|
|
|
m_uiAddStart = m_cHandlesWaiting;
|
|
m_uiAdd = m_uiAddStart;
|
|
|
|
Unlock();
|
|
}
|
|
|
|
// CDavCDataHandles::DeleteWPHandle
|
|
//
|
|
// This is called from the waiting thread
|
|
//
|
|
BOOL
|
|
CDavCDataHandles::FDeleteWPHandle(ULONG ulIndex)
|
|
{
|
|
if ((ulIndex < c_events) ||
|
|
(ulIndex >= m_cHandlesWaiting))
|
|
{
|
|
// This must be an error in WaitForMultpleObject.
|
|
// Don't think we can do anything here
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
// Close the process handle
|
|
//
|
|
CloseHandle (m_rgHandles[ulIndex]);
|
|
|
|
// No need to lock this operation, because we are the only
|
|
// thread that could touch m_chandlesWaiting
|
|
|
|
// Move the rest of handles forward
|
|
//
|
|
for (ULONG i = ulIndex; i < m_cHandlesWaiting-1; i++)
|
|
m_rgHandles[i] = m_rgHandles[i+1];
|
|
|
|
m_cHandlesWaiting--;
|
|
|
|
if ((m_cHandlesWaiting == c_events) && g_wlc.FEmpty())
|
|
{
|
|
//$REVIEW: Is this the right way to stop this process?
|
|
//
|
|
//$REVIEW: Another problem is that there may be outstanding
|
|
// worker processes whose first DO_NEW_WP request is on
|
|
// its way but we haven't finished processing it.
|
|
// To cover this problem, we try ScStartDavCData twice
|
|
// from the worker process side.
|
|
//
|
|
ExitProcess (0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// CDavCDataHandles::AddTimerToDelete
|
|
//
|
|
// This method is called from the timer callback
|
|
//
|
|
VOID
|
|
CDavCDataHandles::AddTimerToDelete(HANDLE h)
|
|
{
|
|
Lock();
|
|
m_rgTimers[m_cTimersToDelete++] = h;
|
|
Unlock();
|
|
|
|
// Notify waiting thread that a timer needs to be deleted
|
|
//
|
|
SetEvent (m_rgHandles[ih_delete_timer]);
|
|
}
|
|
|
|
// CDavCDataHandles::DeleteTimer
|
|
//
|
|
// This method is called from the waiting thread
|
|
//
|
|
VOID
|
|
CDavCDataHandles::DeleteTimer()
|
|
{
|
|
HANDLE rgTimers[MAX_TIMER_HANDLE];
|
|
LONG cTimers;
|
|
LONG i;
|
|
|
|
// Copy the timer handles locally, so that we can do
|
|
// the DeleteTimerQueueTimer outside the lock
|
|
// Otherwise, we may form a deadlock with timer callback
|
|
//
|
|
Lock();
|
|
|
|
cTimers = m_cTimersToDelete;
|
|
|
|
for (i=0; i<cTimers; i++)
|
|
{
|
|
rgTimers[i] = m_rgTimers[i];
|
|
}
|
|
|
|
m_cTimersToDelete = 0;
|
|
|
|
Unlock();
|
|
|
|
for (i=0; i<cTimers; i++)
|
|
{
|
|
if (!DeleteTimerQueueTimer(NULL, //default timer queue
|
|
rgTimers[i], // timer
|
|
INVALID_HANDLE_VALUE)) // blocking call
|
|
{
|
|
DebugTrace ("DeleteTimerQueueTimer failed %d\n", GetLastError());
|
|
}
|
|
}
|
|
|
|
if ((m_cHandlesWaiting == c_events) && g_wlc.FEmpty())
|
|
{
|
|
//$REVIEW: Is this the right way to stop this process?
|
|
//
|
|
//$REVIEW: Another problem is that there may be outstanding
|
|
// worker processes whose first DO_NEW_WP request is on
|
|
// its way but we haven't finished processing it.
|
|
// To cover this problem, we try ScStartDavCData twice
|
|
// from the worker process side.
|
|
//
|
|
ExitProcess (0);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// Stub Functions used for supporting skipping of pipeline calls
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Used to by-pass the named pipe calls when asking DAVCData to save
|
|
// a handle value. This is because we are all ready in the DAVCData
|
|
// process. It is useful when _shmem is trying to save it's handles.
|
|
//
|
|
VOID __fastcall
|
|
IMPLSTUB::SaveHandle(HANDLE h)
|
|
{
|
|
g_wlc.SaveHandle(GetCurrentProcessId(), h);
|
|
}
|
|
|
|
VOID
|
|
PIPELINE::SaveHandle(HANDLE h)
|
|
{
|
|
g_wlc.SaveHandle(GetCurrentProcessId(), h);
|
|
}
|
|
//
|
|
// Used to by-pass the named pipe calls when asking DAVCData to release
|
|
// a handle. This is because we are all ready in the DAVCData
|
|
// process. This is useful during timing out of locks.
|
|
//
|
|
VOID
|
|
PIPELINE::RemoveHandle(HANDLE hDAVHandle)
|
|
{
|
|
CloseHandle(hDAVHandle);
|
|
}
|
|
|
|
//
|
|
// Stub function for Duplicating handles. DAVCData should not use
|
|
// but needs a definition since pipeline.h defines it.
|
|
//
|
|
HRESULT DupHandle(HANDLE i_hOwningProcess
|
|
, HANDLE i_hOwningProcessHandle
|
|
, HANDLE* o_phCreatedHandle)
|
|
{
|
|
Assert(0);
|
|
return E_FAIL;
|
|
}
|
|
|
|
// ===============================================================
|
|
// Lock Timer Callback function
|
|
// ===============================================================
|
|
|
|
//
|
|
// Function is a callback function that CreateTimerQueueTimer routine
|
|
// will call when the timer queue fires. It's purpose is to run
|
|
// through all the locks and validate they are still valid, or release
|
|
// the handles associated with them, and then reset the timer.
|
|
//
|
|
VOID WINAPI CheckLocks(void* pvIgnored, BOOLEAN fIgnored)
|
|
{
|
|
g_wlc.ExpireLocks();
|
|
|
|
DebugTrace("Done Expiring Locks\r\n");
|
|
}
|
|
|
|
|
|
// ===============================================================
|
|
// CWasLockCache (public functions)
|
|
// ===============================================================
|
|
|
|
//
|
|
// Function expires any locks that have timed out
|
|
//
|
|
VOID CWasLockCache::ExpireLocks()
|
|
{
|
|
// Assuming we have a valid cache, tell the cache to do it's house cleaning.
|
|
//
|
|
if (!m_spCache.FIsNull())
|
|
{
|
|
// Do the real work
|
|
//
|
|
m_spCache->ExpireLocks();
|
|
|
|
if (m_spCache->FEmpty())
|
|
{
|
|
HANDLE hTimerToDelete = m_hNewTimer;
|
|
|
|
// Allow new timer to be created
|
|
//
|
|
InterlockedExchange (&m_lTimerLaunched, 0);
|
|
|
|
if (!m_spCache->FEmpty())
|
|
{
|
|
// Some new locks just added into the cache, and we
|
|
// don't know for sure if a new timer was started.
|
|
// Try to launch one anyway
|
|
//
|
|
LaunchLockTimer();
|
|
}
|
|
|
|
// We must delete the old timer, however, we can't do this
|
|
// in the time callback
|
|
//
|
|
g_handles.AddTimerToDelete (hTimerToDelete);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Function launches the time if the timer has not been launched yet.
|
|
//
|
|
VOID CWasLockCache::LaunchLockTimer()
|
|
{
|
|
if (InterlockedCompareExchange(&m_lTimerLaunched, 1, 0) == 0)
|
|
{
|
|
if (!CreateTimerQueueTimer(&m_hNewTimer, // 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..
|
|
{
|
|
DebugTrace("Failed to CreateTimerQueueTimer last error = %d\r\n", GetLastError());
|
|
|
|
// Set back the flags so we know that the timer isn't running.
|
|
//
|
|
InterlockedExchange(&m_lTimerLaunched, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Routine initilizes the shared cache and causes the m_spCache
|
|
// and m_spStatic variables to be linked to their shared memory
|
|
// objects.
|
|
//
|
|
HRESULT CWasLockCache::SetupSharedCache()
|
|
{
|
|
return InitalizeSharedCache(m_spCache, m_spStatic, TRUE);
|
|
}
|
|
|
|
//
|
|
// Routine will duplicate the handle that is passed in, using the
|
|
// original process that owns the passed in handle. It will then
|
|
// return the handle to the caller. Any failure and INVALID_HANDLE_VALUE
|
|
// will be returned.
|
|
//
|
|
// CODEWORK: Could return errors via LastError if neccessary.
|
|
//
|
|
HANDLE CWasLockCache::SaveHandle(DWORD OrigProcess, HANDLE SavingHandle)
|
|
{
|
|
HANDLE h = INVALID_HANDLE_VALUE;
|
|
|
|
HANDLE hOrigProcess = OpenProcess(PROCESS_DUP_HANDLE, false, OrigProcess);
|
|
if (hOrigProcess==NULL)
|
|
{
|
|
DebugTrace ("Getting Orig Process Failed \r\n");
|
|
return h;
|
|
}
|
|
|
|
if (!DuplicateHandle(hOrigProcess, SavingHandle, GetCurrentProcess(), &h, 0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
DebugTrace("Failed to Dup Handle \r\n");
|
|
}
|
|
|
|
DebugTrace("SaveHandle - Handle %x from process %d is duplicated to %x\n", SavingHandle, OrigProcess, h);
|
|
|
|
CloseHandle (hOrigProcess);
|
|
|
|
return h;
|
|
}
|
|
|
|
|
|
//
|
|
// Function is used to initalize the shared memory. Once it has
|
|
// created the global heap for the process and has initalized the
|
|
// shared memory heap, it will then use the InitalizeSharedCache
|
|
// routine to setup the shared memory as expected for a lock cache.
|
|
//
|
|
HRESULT ScInitialize()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
g_wlc.Initialize();
|
|
|
|
// Setup the global heap for the process.
|
|
//
|
|
if (!g_heap.FInit())
|
|
{
|
|
DebugTrace("Initalizing Heap Failed \r\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto ret;
|
|
}
|
|
|
|
// Initialize waiting handles
|
|
//
|
|
hr = g_handles.ScInit();
|
|
if (FAILED(hr))
|
|
goto ret;
|
|
|
|
// Now we can setup the shared memory appropriately.
|
|
//
|
|
hr = g_wlc.SetupSharedCache();
|
|
if (FAILED(hr))
|
|
goto ret;
|
|
|
|
// Create the event thread that can be used to inform work processes
|
|
//
|
|
g_hEventDavCDataUp = CreateEvent (NULL, // lpEventAttributes
|
|
TRUE, // bManualReset
|
|
FALSE, // bInitialState
|
|
g_szEventDavCData);
|
|
if (NULL == g_hEventDavCDataUp)
|
|
{
|
|
DebugTrace ("Davcdata - Failed to create event\n");
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto ret;
|
|
}
|
|
|
|
|
|
ret:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Routine will duplicate the handle that is passed in, using the
|
|
// original process that owns the passed in handle. It will then
|
|
// save the handle in the appropriate shared memory location.
|
|
//
|
|
VOID LockFile(DWORD OrigProcess, HANDLE SavingHandle, LPVOID pshLockData)
|
|
{
|
|
if (pshLockData)
|
|
{
|
|
HANDLE h = g_wlc.SaveHandle(OrigProcess, SavingHandle);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
// CODEWORK: Could also set in the davprocess id here, but then we would have issues
|
|
// with each lock data needing to open it's own davcdata process. Might be nice
|
|
// to save it anywhere here, and then use it as a way to tell if DAVCData recycled
|
|
// and a wp kept shared memory open.
|
|
|
|
// Get a shared pointer to the object.
|
|
//
|
|
SharedPtr<CInShLockData> spLockData;
|
|
|
|
// If we succeed in binding to the object then we can go ahead and set the value in.
|
|
//
|
|
if (spLockData.FBind(*(SharedHandle<CInShLockData>*)pshLockData))
|
|
{
|
|
spLockData->SetDAVProcFileHandle(h);
|
|
g_wlc.LaunchLockTimer();
|
|
}
|
|
else
|
|
{
|
|
// If we think that we are saving a file for a lock, but the lock data
|
|
// is gone, then we best just release our hold on the file.
|
|
//
|
|
CloseHandle(h);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ===============================================================
|
|
// Named pipe routines.
|
|
// ===============================================================
|
|
|
|
//
|
|
// Function handles listening for named pipe communications from the
|
|
// worker process and sends the requests to there supporting functions.
|
|
//
|
|
SCODE ScNamedPipeListener()
|
|
{
|
|
SCODE sc = S_OK;
|
|
DWORD dwAction = 0;
|
|
DWORD dwClientProcess = 0;
|
|
HANDLE hClientHandle = 0;
|
|
DWORD dwErr = 0;
|
|
DWORD outBufSize =0;
|
|
LPVOID pVoid = NULL;
|
|
|
|
// Buffer for retrieving data from the caller.
|
|
//
|
|
BYTE outBuf[PIPE_MESSAGE_SIZE];
|
|
|
|
// Create the named pipe communication.
|
|
//
|
|
HANDLE hNamedPipe = CreateNamedPipe("\\\\.\\pipe\\SaveHandle" // pipe name
|
|
, PIPE_ACCESS_DUPLEX // pipe open mode
|
|
, PIPE_TYPE_MESSAGE | PIPE_WAIT // pipe-specific modes
|
|
, 1 // maximum number of instances
|
|
, 0 // output buffer size (bytes)
|
|
, 32 // input buffer size (bytes)
|
|
, 3000 // time-out interval (milliseconds = 3 seconds)
|
|
, NULL); // Security Descriptor
|
|
|
|
if (hNamedPipe == INVALID_HANDLE_VALUE)
|
|
{
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
// No inform the worker processes that DAVCData is available
|
|
//
|
|
Assert (g_hEventDavCDataUp);
|
|
if (!SetEvent (g_hEventDavCDataUp))
|
|
{
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Wait for the client to signal that data is on the line.
|
|
//
|
|
if (!ConnectNamedPipe(hNamedPipe, NULL))
|
|
{
|
|
// The function may still return zero even if client connects in the interval
|
|
// between the call to CreateNamedPipe() and the call to ConnectNamedPipe().
|
|
// In that case the last error will be ERROR_PIPE_CONNECTED that will indicate that
|
|
// there is already good connection between the client and server processes
|
|
//
|
|
if (ERROR_PIPE_CONNECTED != GetLastError())
|
|
{
|
|
DebugTrace("ScNamedPipeListener() - ConnectNamedPipe() failed with error 0x%08lX, retrying.\r\n", GetLastError());
|
|
|
|
// Loop and try again to wait.
|
|
//
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Get the data from the line.
|
|
//
|
|
if (!ReadFile(hNamedPipe, (LPVOID) outBuf, PIPE_MESSAGE_SIZE, &outBufSize, NULL))
|
|
{
|
|
// CODEWORK: Do we still need this special error case, now that we are not reading the
|
|
// data in two passes?
|
|
// On the first call into read (per instance of the holder object) ERROR_MORE_DATA
|
|
// is returned from ReadFile, which is expected. However, on subsequent client
|
|
// sessions it is not returned (which is strange since there is more data in
|
|
// to be read below. In either case if we continue on, everything works correctly.
|
|
//
|
|
dwErr = GetLastError();
|
|
if (dwErr != ERROR_MORE_DATA)
|
|
{
|
|
sc = HRESULT_FROM_WIN32(dwErr);
|
|
goto disconnect;
|
|
}
|
|
}
|
|
|
|
// Verify we got the size of the data we expected.
|
|
//
|
|
if (outBufSize != PIPE_MESSAGE_SIZE)
|
|
{
|
|
DebugTrace("Output buffer size did not equal the # of dwords expected \r\n");
|
|
goto disconnect;
|
|
}
|
|
|
|
// Break the data out into the appropriate variables.
|
|
//
|
|
dwAction = *((DWORD*)outBuf);
|
|
dwClientProcess = *((DWORD*) (outBuf+sizeof(DWORD)));
|
|
hClientHandle = *((HANDLE*) (outBuf+2*sizeof(DWORD)));
|
|
pVoid = (LPVOID)(outBuf+2*sizeof(DWORD)+sizeof(HANDLE));
|
|
|
|
DebugTrace("Action = %d, Client Handle = %x, Client Process = %d\n",
|
|
dwAction,
|
|
hClientHandle,
|
|
dwClientProcess);
|
|
|
|
// End of code to be removed (see BUGBUG above)
|
|
|
|
// Launch the appropriate function to process the request.
|
|
// NOTE: handles are duplicated here and PREFIX finds the path
|
|
// that we are not releasing them on error (DisconnectNamedPipe()
|
|
// failure), but the thing is that on error we will terminate the
|
|
// process and the handles will still be released, so does not
|
|
// help us if we try to handle that in any way.
|
|
//
|
|
switch (dwAction)
|
|
{
|
|
case DO_NEW_WP:
|
|
(VOID)g_handles.FAddNewWP(dwClientProcess);
|
|
break;
|
|
|
|
case DO_SAVE:
|
|
g_wlc.SaveHandle(dwClientProcess, hClientHandle);
|
|
break;
|
|
|
|
case DO_LOCK:
|
|
LockFile(dwClientProcess, hClientHandle, pVoid);
|
|
break;
|
|
|
|
case DO_REMOVE:
|
|
CloseHandle (hClientHandle);
|
|
break;
|
|
|
|
default:
|
|
DebugTrace("Invalid action %i sent in \r\n", dwAction);
|
|
}
|
|
|
|
|
|
disconnect:
|
|
|
|
// Release the client from the named pipe so another client can call in.
|
|
//
|
|
if (!DisconnectNamedPipe(hNamedPipe))
|
|
{
|
|
sc = HRESULT_FROM_WIN32(GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
} while (TRUE);
|
|
|
|
cleanup:
|
|
|
|
return sc;
|
|
}
|
|
|
|
// ===============================================================
|
|
// Main Routine
|
|
// ===============================================================
|
|
|
|
//
|
|
// Setting up the shared memory for the lock cache and
|
|
// establishing the listener who will support the worker processes
|
|
// storing and releasing file handles.
|
|
//
|
|
int _cdecl main ()
|
|
{
|
|
CSmhInit si;
|
|
SCODE sc = S_OK;
|
|
|
|
// Initlize shared memory
|
|
//
|
|
if (FALSE == si.FInitialize(gc_wszSignature))
|
|
{
|
|
DebugTrace ("Failed to initialize shared memory\n");
|
|
sc = E_FAIL;
|
|
goto ret;
|
|
}
|
|
|
|
sc = ScInitialize();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
// If we have setup the shared memory then we are ready to take request
|
|
// to save handles, and start timing out locks.
|
|
//
|
|
sc = ScNamedPipeListener();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
|
|
|