514 lines
14 KiB
C++
514 lines
14 KiB
C++
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// S H L K M G R . C P P
|
|
//
|
|
// HTTP 1.1/DAV 1.0 request handling via ISAPI
|
|
//
|
|
// This file contains the CSharedLockMgr class that handles the shared
|
|
// memory mapped file implementation of the lock cache. It is used by
|
|
// HTTPEXT only.
|
|
//
|
|
// Copyright 2000 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
#include <_davfs.h>
|
|
#include <smh.h>
|
|
#include <shlkcache.h>
|
|
#include <xlock.h>
|
|
#include "_shlkmgr.h"
|
|
#include "pipeline.h"
|
|
|
|
// Structure used to send data to the callback
|
|
// function for printing data about a lock.
|
|
//
|
|
struct PrintingContext
|
|
{
|
|
IMethUtil* pmu;
|
|
CXMLEmitter* pemitter;
|
|
CXNode* pxnParent;
|
|
CEmitterNode* penLockDiscovery;
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
// Supporting functions.
|
|
// =========================================================================
|
|
|
|
//
|
|
// Callback function use to print out
|
|
// information about a lock properties.
|
|
//
|
|
void PrintOne( SharedPtr<CInShLockData>* plock
|
|
, LPVOID pContext )
|
|
{
|
|
CLockFS fslock;
|
|
|
|
// Validate we have the data we need to print with.
|
|
//
|
|
Assert (pContext);
|
|
if (!pContext) return;
|
|
|
|
// Pull out the pieces so we can use them to print.
|
|
//
|
|
IMethUtil* pmu = ((PrintingContext*) pContext)->pmu;
|
|
CXMLEmitter* pemitter = ((PrintingContext*)pContext)->pemitter;
|
|
CXNode* pxnParent = ((PrintingContext*)pContext)->pxnParent;
|
|
CEmitterNode* penLockDiscovery = ((PrintingContext*)pContext)->penLockDiscovery;
|
|
|
|
SCODE sc = S_OK;
|
|
|
|
// Construct the 'DAV:lockdiscovery' node
|
|
//
|
|
sc = penLockDiscovery->ScConstructNode (*pemitter, pxnParent, gc_wszLockDiscovery);
|
|
if (FAILED (sc))
|
|
goto ret;
|
|
|
|
// Bind to the data represented by the plock past in.
|
|
// This is so we can use the CLock function to print the lock.
|
|
//
|
|
fslock.SharedData().FBind(plock->GetHandle());
|
|
|
|
// now pass fslock to the ScLockDiscoveryFromCLock;
|
|
|
|
// Add the 'DAV:activelock' property for this plock.
|
|
//$HACK:ROSEBUD_TIMEOUT_HACK
|
|
// For the bug where rosebud waits until the last second
|
|
// before issueing the refresh. Need to filter out this check with
|
|
// the user agent string. The hack is to increase the timeout
|
|
// by 30 seconds and return the actual timeout. So we
|
|
// need the ecb/pmu to findout the user agent. At this point
|
|
// we do not know. So we pass NULL. If we remove this
|
|
// hack ever (I doubt if we can ever do that), then
|
|
// change the interface of ScLockDiscoveryFromCLock.
|
|
//
|
|
sc = ScLockDiscoveryFromCLock (pmu,
|
|
*pemitter,
|
|
*penLockDiscovery,
|
|
&fslock);
|
|
|
|
//$HACK:END ROSEBUD_TIMEOUT_HACK
|
|
//
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
sc = penLockDiscovery->ScDone();
|
|
if (FAILED(sc))
|
|
goto ret;
|
|
|
|
|
|
ret:
|
|
// Ignore errors if printing the XML failed and try
|
|
// to continue to print out the rest of the properties.
|
|
|
|
// CodeWork: Might want to change this to stop printing
|
|
// properties if an earlier property had problems.
|
|
return;
|
|
}
|
|
|
|
// ========================================================================
|
|
// CLASS CSharedLockMgr (Private Functions)
|
|
// =========================================================================
|
|
|
|
//
|
|
// class constructor
|
|
//
|
|
CSharedLockMgr::CSharedLockMgr()
|
|
: m_hDavProc(INVALID_HANDLE_VALUE)
|
|
, m_fSharedMemoryInitalized(FALSE)
|
|
{
|
|
// Initalization of shared memory is now done on demand.
|
|
// SharedMemoryInitalized();
|
|
}
|
|
|
|
//
|
|
// class destructor
|
|
//
|
|
CSharedLockMgr::~CSharedLockMgr()
|
|
{
|
|
// If we have openned the davproc process
|
|
// then we should close it.
|
|
//
|
|
if (m_hDavProc != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(m_hDavProc);
|
|
m_hDavProc = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Function which will hook up the underlying shared memory cache if
|
|
// we have not all ready hooked into it.
|
|
//
|
|
BOOL CSharedLockMgr::SharedMemoryInitalized()
|
|
{
|
|
// check if the shared memory has been initalized.
|
|
// if it hasn't then we need to roll back to local
|
|
// system before we link up, because DAVProc will
|
|
// have setup the files as local system.
|
|
//
|
|
if (!m_fSharedMemoryInitalized)
|
|
{
|
|
CSynchronizedBlock sb(m_cs);
|
|
|
|
if (!m_fSharedMemoryInitalized)
|
|
{
|
|
safe_revert_self rs;
|
|
|
|
if (FAILED(InitalizeSharedCache(m_spSharedCache, m_spSharedStatic, FALSE)))
|
|
m_fSharedMemoryInitalized = FALSE;
|
|
else
|
|
m_fSharedMemoryInitalized = TRUE;
|
|
}
|
|
}
|
|
|
|
if (m_fSharedMemoryInitalized)
|
|
{
|
|
// Assuming we have shared memory than these
|
|
// should be set correctly all ready.
|
|
//
|
|
Assert(!(m_spSharedStatic.FIsNull()));
|
|
Assert(!(m_spSharedCache.FIsNull()));
|
|
|
|
}
|
|
|
|
return m_fSharedMemoryInitalized;
|
|
}
|
|
|
|
//
|
|
// Function creates a new lock token ID
|
|
// using information stored in the shared
|
|
// static class. You should not call this
|
|
// function unless you are creating a new
|
|
// lock token and are going to use the value.
|
|
//
|
|
HRESULT CSharedLockMgr::GetNewLockTokenId(__int64* pi64LockID)
|
|
{
|
|
// validate that we have linked to shared memory.
|
|
//
|
|
if (!SharedMemoryInitalized()) return E_OUTOFMEMORY;
|
|
|
|
LARGE_INTEGER llLockID;
|
|
|
|
// validate that we have got a reasonable out param in.
|
|
//
|
|
if (pi64LockID == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Code copied from lockmgr implementation.
|
|
// I assume that since the low token has to wrap all the way around to
|
|
// make the high token change, it is safe to assume that the high token
|
|
// won't be changing fast enough to have race conditions.
|
|
//
|
|
llLockID.LowPart = InterlockedIncrement(&(m_spSharedStatic->m_lTokenLow));
|
|
if (llLockID.LowPart == 0)
|
|
(m_spSharedStatic->m_lTokenHigh)++;
|
|
|
|
llLockID.HighPart = m_spSharedStatic->m_lTokenHigh;
|
|
|
|
*pi64LockID = llLockID.QuadPart;
|
|
|
|
return S_OK;
|
|
|
|
};
|
|
|
|
|
|
// ========================================================================
|
|
// CLASS CSharedLockMgr (Public Functions - not inherited)
|
|
// =========================================================================
|
|
//
|
|
|
|
// Gets a handle to the davproc process.
|
|
// This will open the process if we have not
|
|
// all ready openned it.
|
|
//
|
|
HRESULT CSharedLockMgr::GetDAVProcessHandle(HANDLE* phDavProc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (phDavProc == NULL)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto ret;
|
|
}
|
|
|
|
if (m_hDavProc == INVALID_HANDLE_VALUE)
|
|
{
|
|
safe_revert_self s;
|
|
|
|
m_hDavProc = OpenProcess(PROCESS_DUP_HANDLE, false, GetDAVProcessId());
|
|
if (m_hDavProc==NULL)
|
|
{
|
|
FsLockTrace ("Openning DAV process failed\n");
|
|
m_hDavProc=INVALID_HANDLE_VALUE;
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
|
|
*phDavProc = m_hDavProc;
|
|
|
|
ret:
|
|
return hr;
|
|
}
|
|
|
|
// Generates a new lock data item
|
|
// that represents the information passed
|
|
// in here.
|
|
//
|
|
HRESULT CSharedLockMgr::GetNewLockData(LPCWSTR wszResource
|
|
, HANDLE hfile
|
|
, DWORD dwAccess
|
|
, DWORD dwLockType
|
|
, DWORD dwLockScope
|
|
, DWORD dwTimeout
|
|
, LPCWSTR wszOwnerComment
|
|
, HANDLE hit
|
|
, CLockFS& lock)
|
|
{
|
|
if (!SharedMemoryInitalized()) return E_OUTOFMEMORY;
|
|
|
|
__int64 i64LockID = 0;
|
|
|
|
// Grab a reference to the lock items shared object.
|
|
//
|
|
SharedPtr<CInShLockData>& spSharedLockData = lock.SharedData();
|
|
|
|
// First make sure we can get a new lock token id.
|
|
//
|
|
HRESULT hr = GetNewLockTokenId(&i64LockID);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Then create the new shared memory data object.
|
|
//
|
|
if (!(spSharedLockData.FCreate()))
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Then initalize the new object to hold the information
|
|
// about the new lock.
|
|
//
|
|
spSharedLockData->Init(wszResource
|
|
, dwAccess
|
|
, i64LockID
|
|
, m_spSharedStatic->GetGuidString()
|
|
, dwLockType
|
|
, dwLockScope
|
|
, dwTimeout
|
|
, wszOwnerComment
|
|
, hit);
|
|
|
|
// Need to lock the file with WAS, this will also set in the
|
|
// appropriate DAVProcFileHandle to the LockData object.
|
|
//
|
|
PIPELINE::LockFile(hfile, spSharedLockData.GetHandle());
|
|
|
|
// Lastly initalize the hash code values so we are ready
|
|
// to go into the cache.
|
|
//
|
|
spSharedLockData->SetHashes(m_spSharedCache->GetHashCodeByID(i64LockID), m_spSharedCache->GetHashCodeByName(wszResource));
|
|
|
|
return S_OK;
|
|
};
|
|
|
|
|
|
|
|
// ========================================================================
|
|
// CLASS CSharedLockMgr (Public Functions - inherited from ILockCache)
|
|
// =========================================================================
|
|
|
|
//
|
|
// Return the guid string stored in shared memory
|
|
// that is used in a lock tokens for this server.
|
|
//
|
|
LPCWSTR CSharedLockMgr::WszGetGuid()
|
|
{
|
|
if (!SharedMemoryInitalized()) return NULL;
|
|
|
|
return m_spSharedStatic->GetGuidString();
|
|
}
|
|
|
|
//
|
|
// Print out all lock token information for locks of this type on this resource.
|
|
// If the fEmitXML is false, just return if there are any locks.
|
|
//
|
|
BOOL CSharedLockMgr::FGetLockOnError( IMethUtil * pmu,
|
|
LPCWSTR wszResource,
|
|
DWORD dwLockType,
|
|
BOOL fEmitXML,
|
|
CXMLEmitter * pemitter,
|
|
CXNode * pxnParent)
|
|
{
|
|
// If we can not get connected to the shared memory we
|
|
// need to return that we did not find the lock.
|
|
//
|
|
if (!SharedMemoryInitalized()) return FALSE;
|
|
|
|
CEmitterNode enLockDiscovery;
|
|
PrintingContext Context;
|
|
|
|
// Save the objects into our data structure so we can
|
|
// access them when we need to print them.
|
|
//
|
|
Context.pmu = pmu;
|
|
Context.pemitter = pemitter;
|
|
Context.pxnParent = pxnParent;
|
|
Context.penLockDiscovery = &enLockDiscovery;
|
|
|
|
// Now let the shared lock cache start looking up all the locks
|
|
// that match for the resource and printing them out.
|
|
//
|
|
BOOL fRet = m_spSharedCache->FLookUpAll(wszResource, dwLockType, fEmitXML, &Context, PrintOne);
|
|
|
|
Context.penLockDiscovery->ScDone();
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
//
|
|
// Lock a resource
|
|
//
|
|
BOOL CSharedLockMgr::FLock (CLockFS& lock, DWORD dwsecTimeout)
|
|
{
|
|
// If we don't have shared memory then we can't lock anything.
|
|
//
|
|
if (!SharedMemoryInitalized()) return FALSE;
|
|
|
|
// Add, using the copy of the string stored in the lockitem as the key.
|
|
//
|
|
m_spSharedCache->Add (lock.SharedData() );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Delete a lock
|
|
//
|
|
void CSharedLockMgr::DeleteLock(__int64 i64LockId)
|
|
{
|
|
// If we don't have shared memory then the delete is just
|
|
// not going to happen.
|
|
//
|
|
if (!SharedMemoryInitalized()) return;
|
|
|
|
m_spSharedCache->DeleteByID( i64LockId );
|
|
}
|
|
|
|
//
|
|
// Get a lock from the lock cache, checking for proper authentication.
|
|
//
|
|
HRESULT CSharedLockMgr::HrGetLock (LPMETHUTIL pmu, __int64 i64LockId, CLockFS** pplock)
|
|
{
|
|
|
|
auto_ref_ptr<CLockFS> plock;
|
|
HRESULT hr = S_OK;
|
|
DWORD dwErr;
|
|
|
|
// Get the lock by the Id.
|
|
//
|
|
if (!FGetLockNoAuth (i64LockId, plock.load()))
|
|
{
|
|
hr = E_DAV_LOCK_NOT_FOUND;
|
|
goto ret;
|
|
}
|
|
|
|
// Validate that we have the appropriate ownership to get the lock.
|
|
//
|
|
dwErr = plock->DwCompareLockOwner (pmu);
|
|
if (dwErr)
|
|
{
|
|
hr = HRESULT_FROM_WIN32 (dwErr);
|
|
goto ret;
|
|
}
|
|
|
|
Assert (S_OK == hr);
|
|
|
|
// Need to AddRef the lock here so that when the auto ref goes away it
|
|
// will not release the object.
|
|
//
|
|
plock->AddRef();
|
|
*pplock = plock.get();
|
|
|
|
ret:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Get a lock without worrying about the ownership of the lock.
|
|
//
|
|
BOOL CSharedLockMgr::FGetLockNoAuth( __int64 i64LockId, CLockFS** pplock )
|
|
{
|
|
if (!SharedMemoryInitalized()) return FALSE;
|
|
|
|
CLockFS* plock = NULL;
|
|
|
|
// Validate out parameters
|
|
//
|
|
Assert(pplock);
|
|
if (pplock==NULL)
|
|
return FALSE;
|
|
|
|
*pplock = NULL;
|
|
|
|
// Allocate the a CLockFS object to hold the lock we find.
|
|
//
|
|
plock = new CLockFS();
|
|
if (plock == NULL)
|
|
return FALSE; // out of memory
|
|
|
|
// We need to bump up it's ref count from zero.
|
|
//
|
|
plock->AddRef();
|
|
|
|
// Grab the shared lock data object to use to link
|
|
// to the lock if we find it.
|
|
//
|
|
SharedPtr<CInShLockData>& splock = plock->SharedData();
|
|
|
|
// This Lookup happens inside a correct read-lock.
|
|
// If the return is non-NULL, then we are given a ref on the LOCKITEM.
|
|
//
|
|
if (m_spSharedCache->FLookupByID( i64LockId, splock ))
|
|
{
|
|
FILETIME ftNow;
|
|
|
|
GetSystemTimeAsFileTime(&ftNow);
|
|
|
|
// Once it is marked as expired nothing can
|
|
// change that setting. However, if it's not marked as expired
|
|
// than it is fine for last writer to win on setting the
|
|
// last access time, so I am not locking here.
|
|
//
|
|
if (splock->IsExpired(ftNow))
|
|
{
|
|
// Release the lock token from the cache.
|
|
//
|
|
m_spSharedCache->DeleteByID( i64LockId );
|
|
}
|
|
else
|
|
{
|
|
// No locking on this update, but since it
|
|
// is a time stamp, last writer winning seems
|
|
// pretty much ok to me.
|
|
//
|
|
splock->SetLastAccess(ftNow);
|
|
|
|
// returning plock, user is responsible for deleting.
|
|
//
|
|
*pplock = plock;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// If we found a lock, but it was expired this will
|
|
// release the last handle on it and we will be free of it.
|
|
//
|
|
delete plock;
|
|
|
|
return FALSE;
|
|
}
|