424 lines
13 KiB
C
424 lines
13 KiB
C
|
/*
|
||
|
* DRIVEARB.C
|
||
|
*
|
||
|
*
|
||
|
* DRIVEARB.DLL - Shared Drive Aribiter for shared disks and libraries
|
||
|
* - inter-machine sharing client
|
||
|
* - inter-app sharing service
|
||
|
*
|
||
|
* Author: ErvinP
|
||
|
*
|
||
|
* (c) 2000 Microsoft Corporation
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <wtypes.h>
|
||
|
|
||
|
#include <dlmhdr.h> // BUGBUG - get a common DLM header from Cluster team
|
||
|
|
||
|
#include <drivearb.h>
|
||
|
#include "internal.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
* SHARED DATA Section
|
||
|
*
|
||
|
* Note: in order for global data items to be placed
|
||
|
* in a non-default section, they must be
|
||
|
* explicitly initialized!
|
||
|
*/
|
||
|
#pragma data_seg("DRIVEARB_SharedDataSection")
|
||
|
DWORD g_initializingGlobals = 0;
|
||
|
DWORD g_numDrivesInFileMap = 0;
|
||
|
#pragma data_seg()
|
||
|
#pragma comment(linker, "/section:DRIVEARB_SharedDataSection,rws")
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* These are the process-local handles to the inter-process SHARED global:
|
||
|
* mutex
|
||
|
* fileMapping for the drives array
|
||
|
* view pointer into that fileMapping
|
||
|
*/
|
||
|
HANDLE g_hSharedGlobalMutex = NULL;
|
||
|
HANDLE g_allDrivesFileMap = NULL;
|
||
|
driveContext *g_allDrivesViewPtr = NULL;
|
||
|
|
||
|
|
||
|
STDAPI_(BOOL) DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved)
|
||
|
{
|
||
|
BOOL result = FALSE;
|
||
|
|
||
|
switch (dwReason){
|
||
|
|
||
|
case DLL_PROCESS_ATTACH:
|
||
|
/*
|
||
|
* DllMain calls are synchronized on a per-process basis,
|
||
|
* but not between processes, so we need some synchronization
|
||
|
* here while creating our globalMutex handle.
|
||
|
* Since we don't even have our global mutex yet,
|
||
|
* use InterlockedIncrement on a shared-data counter here
|
||
|
* to synchronize multiple DLL_PROCESS_ATTACH calls.
|
||
|
*/
|
||
|
while (InterlockedIncrement(&g_initializingGlobals) != 1){
|
||
|
InterlockedDecrement(&g_initializingGlobals);
|
||
|
Sleep(1);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* See if the global mutex is already allocated
|
||
|
* for some other process by trying to open it.
|
||
|
* If not, allocate it.
|
||
|
*/
|
||
|
g_hSharedGlobalMutex = OpenMutex(SYNCHRONIZE, FALSE, GLOBAL_MUTEX_NAME);
|
||
|
if (!g_hSharedGlobalMutex){
|
||
|
g_hSharedGlobalMutex = CreateMutex(NULL, FALSE, GLOBAL_MUTEX_NAME);
|
||
|
}
|
||
|
|
||
|
if (g_hSharedGlobalMutex){
|
||
|
result = InitDrivesFileMappingForProcess();
|
||
|
}
|
||
|
|
||
|
InterlockedDecrement(&g_initializingGlobals);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case DLL_PROCESS_DETACH:
|
||
|
|
||
|
/*
|
||
|
* Don't need to synchronize with other processes here
|
||
|
* because we're only closing our process-local handles.
|
||
|
*/
|
||
|
|
||
|
DestroyDrivesFileMappingForProcess();
|
||
|
|
||
|
if (g_hSharedGlobalMutex){
|
||
|
CloseHandle(g_hSharedGlobalMutex);
|
||
|
g_hSharedGlobalMutex = NULL;
|
||
|
}
|
||
|
|
||
|
result = TRUE;
|
||
|
break;
|
||
|
|
||
|
case DLL_THREAD_ATTACH:
|
||
|
case DLL_THREAD_DETACH:
|
||
|
default:
|
||
|
result = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
HANDLE __stdcall OpenDriveSession(LPSTR driveName, INVALIDATE_DRIVE_HANDLE_PROC invalidateHandleProc)
|
||
|
{
|
||
|
clientSessionContext *session;
|
||
|
|
||
|
if (lstrlen(driveName) > 0){
|
||
|
|
||
|
if (invalidateHandleProc){
|
||
|
|
||
|
session = NewClientSession(driveName);
|
||
|
if (session){
|
||
|
BOOL ok;
|
||
|
|
||
|
WaitForSingleObject(g_hSharedGlobalMutex, INFINITE);
|
||
|
|
||
|
ok = InitializeClientArbitration(session);
|
||
|
if (ok){
|
||
|
session->driveViewPtr->sessionReferenceCount++;
|
||
|
}
|
||
|
|
||
|
ReleaseMutex(g_hSharedGlobalMutex);
|
||
|
|
||
|
if (!ok){
|
||
|
FreeClientSession(session);
|
||
|
session = NULL;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(session);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(invalidateHandleProc);
|
||
|
session = NULL;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(lstrlen(driveName) > 0);
|
||
|
session = NULL;
|
||
|
}
|
||
|
|
||
|
return (HANDLE)session;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID __stdcall CloseDriveSession(HANDLE hSession)
|
||
|
{
|
||
|
clientSessionContext *session = (clientSessionContext *)hSession;
|
||
|
|
||
|
if (session->sig == DRIVEARB_SIG){ // sanity check
|
||
|
|
||
|
WaitForSingleObject(g_hSharedGlobalMutex, INFINITE);
|
||
|
|
||
|
/*
|
||
|
* Need to lock drive to make sure driveViewPtr is valid.
|
||
|
*/
|
||
|
if (LOCKDriveForSession(session)){
|
||
|
|
||
|
ASSERT(session->driveViewPtr->sessionReferenceCount > 0);
|
||
|
session->driveViewPtr->sessionReferenceCount--;
|
||
|
|
||
|
UNLOCKDriveForSession(session);
|
||
|
}
|
||
|
|
||
|
ShutDownClientArbitration(session);
|
||
|
|
||
|
ReleaseMutex(g_hSharedGlobalMutex);
|
||
|
|
||
|
FreeClientSession(session);
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(session->sig == DRIVEARB_SIG);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL __stdcall AcquireDrive(HANDLE hDriveSession, DWORD flags)
|
||
|
{
|
||
|
clientSessionContext *session = (clientSessionContext *)hDriveSession;
|
||
|
BOOL result = FALSE;
|
||
|
|
||
|
if (session->sig == DRIVEARB_SIG){ // sanity check
|
||
|
|
||
|
if (LOCKDriveForSession(session)){
|
||
|
driveContext *drive = session->driveViewPtr;
|
||
|
BOOL gotAppOwnership = FALSE;
|
||
|
BOOL unlockedAbort = FALSE;
|
||
|
|
||
|
if (drive->state == DRIVESTATE_UNAVAILABLE_LOCALLY){
|
||
|
/*
|
||
|
* Attempt to get ownership of the drive
|
||
|
* for this node synchronously.
|
||
|
* This should not fail.
|
||
|
*
|
||
|
* BUGBUG - need to implement DRIVEARB_NOWAIT flag in arbiter, too
|
||
|
*/
|
||
|
BOOL gotNodeOwnership;
|
||
|
gotNodeOwnership = AcquireNodeLevelOwnership(session);
|
||
|
if (gotNodeOwnership){
|
||
|
drive->state = DRIVESTATE_AVAILABLE_LOCALLY;
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(gotNodeOwnership);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (drive->state == DRIVESTATE_AVAILABLE_LOCALLY){
|
||
|
gotAppOwnership = TRUE;
|
||
|
}
|
||
|
else if (drive->state == DRIVESTATE_INUSE_LOCALLY){
|
||
|
|
||
|
/*
|
||
|
* The drive is owned by the local node,
|
||
|
* but some other app is using it.
|
||
|
* Wait to get ownership by the calling app.
|
||
|
*/
|
||
|
while (TRUE){
|
||
|
BOOL availableNow;
|
||
|
|
||
|
if (drive->state == DRIVESTATE_UNAVAILABLE_LOCALLY){
|
||
|
/*
|
||
|
* This is not possible now, but may become possible
|
||
|
* if we implement some sort of fairness (such that
|
||
|
* we may release node-level ownership in ReleaseDrive
|
||
|
* even though local clients are waiting).
|
||
|
*/
|
||
|
availableNow = FALSE;
|
||
|
}
|
||
|
else if (drive->state == DRIVESTATE_AVAILABLE_LOCALLY){
|
||
|
availableNow = TRUE;
|
||
|
}
|
||
|
else if ((flags & DRIVEARB_REQUEST_READ) && drive->denyRead){
|
||
|
availableNow = FALSE;
|
||
|
}
|
||
|
else if ((flags & DRIVEARB_REQUEST_WRITE) && drive->denyWrite){
|
||
|
availableNow = FALSE;
|
||
|
}
|
||
|
else if ((drive->numCurrentReaders > 0) &&
|
||
|
!(flags & DRIVEARB_INTRANODE_SHARE_READ)){
|
||
|
availableNow = FALSE;
|
||
|
}
|
||
|
else if ((drive->numCurrentWriters > 0) &&
|
||
|
!(flags & DRIVEARB_INTRANODE_SHARE_WRITE)){
|
||
|
availableNow = FALSE;
|
||
|
}
|
||
|
else {
|
||
|
availableNow = TRUE;
|
||
|
}
|
||
|
|
||
|
if (availableNow){
|
||
|
gotAppOwnership = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
else if (flags & DRIVEARB_NOWAIT){
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
/*
|
||
|
* We need to wait for the drive to become available.
|
||
|
*/
|
||
|
DWORD waitRes;
|
||
|
|
||
|
// DBGMSG("AcquireDrive waiting ...", (ULONG_PTR)session->sessionDriveEvent);
|
||
|
|
||
|
session->state = CLIENTSTATE_WAITING;
|
||
|
drive->numWaitingSessions++;
|
||
|
|
||
|
/*
|
||
|
* Unlock the drive while waiting for the event.
|
||
|
*/
|
||
|
UNLOCKDriveForSession(session);
|
||
|
|
||
|
waitRes = WaitForSingleObject(session->sessionDriveEvent, INFINITE);
|
||
|
|
||
|
// DBGMSG(" ... AcquireDrive done waiting.", waitRes);
|
||
|
if (waitRes == WAIT_FAILED){
|
||
|
DWORD errCode = GetLastError();
|
||
|
DBGMSG("WaitForSingleObject failed with:", errCode);
|
||
|
}
|
||
|
|
||
|
if (LOCKDriveForSession(session)){
|
||
|
drive->numWaitingSessions--;
|
||
|
}
|
||
|
else {
|
||
|
/*
|
||
|
* Couldn't re-lock, abort.
|
||
|
*
|
||
|
* BUGBUG - numWaitingSessions is wrong now,
|
||
|
* but can't correct it because can't lock the drive
|
||
|
*/
|
||
|
unlockedAbort = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (gotAppOwnership){
|
||
|
|
||
|
drive->state = DRIVESTATE_INUSE_LOCALLY;
|
||
|
|
||
|
if (flags & DRIVEARB_REQUEST_READ){
|
||
|
drive->numCurrentReaders++;
|
||
|
}
|
||
|
if (flags & DRIVEARB_REQUEST_WRITE){
|
||
|
drive->numCurrentWriters++;
|
||
|
}
|
||
|
|
||
|
if (!(flags & DRIVEARB_INTRANODE_SHARE_READ)){
|
||
|
drive->denyRead = TRUE;
|
||
|
}
|
||
|
if (!(flags & DRIVEARB_INTRANODE_SHARE_WRITE)){
|
||
|
drive->denyWrite = TRUE;
|
||
|
}
|
||
|
|
||
|
session->shareFlags = flags;
|
||
|
session->state = CLIENTSTATE_ACTIVE;
|
||
|
|
||
|
result = TRUE;
|
||
|
}
|
||
|
|
||
|
if (!unlockedAbort){
|
||
|
UNLOCKDriveForSession(session);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(session->sig == DRIVEARB_SIG);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID __stdcall ReleaseDrive(HANDLE hDriveSession)
|
||
|
{
|
||
|
clientSessionContext *session = (clientSessionContext *)hDriveSession;
|
||
|
|
||
|
// DBGMSG("> ReleaseDrive", 0);
|
||
|
|
||
|
if (session->sig == DRIVEARB_SIG){ // sanity check
|
||
|
driveContext *drive;
|
||
|
|
||
|
if (LOCKDriveForSession(session)){
|
||
|
BOOL eventSetOk;
|
||
|
|
||
|
drive = session->driveViewPtr;
|
||
|
|
||
|
if (session->shareFlags & DRIVEARB_REQUEST_READ){
|
||
|
ASSERT(drive->numCurrentReaders > 0);
|
||
|
drive->numCurrentReaders--;
|
||
|
}
|
||
|
if (session->shareFlags & DRIVEARB_REQUEST_WRITE){
|
||
|
ASSERT(drive->numCurrentWriters > 0);
|
||
|
drive->numCurrentWriters--;
|
||
|
}
|
||
|
|
||
|
if (!(session->shareFlags & DRIVEARB_INTRANODE_SHARE_READ)){
|
||
|
ASSERT(drive->denyRead);
|
||
|
drive->denyRead = FALSE;
|
||
|
}
|
||
|
if (!(session->shareFlags & DRIVEARB_INTRANODE_SHARE_WRITE)){
|
||
|
ASSERT(drive->denyWrite);
|
||
|
drive->denyWrite = FALSE;
|
||
|
}
|
||
|
|
||
|
session->state = CLIENTSTATE_INACTIVE;
|
||
|
|
||
|
/*
|
||
|
* Only release the drive to other machines if there are
|
||
|
* no clients waiting for it on this machine.
|
||
|
*
|
||
|
* BUGBUG - implement some fairness to apps on other nodes
|
||
|
*/
|
||
|
ASSERT(drive->state == DRIVESTATE_INUSE_LOCALLY);
|
||
|
if (drive->numWaitingSessions == 0){
|
||
|
ReleaseNodeLevelOwnership(session);
|
||
|
drive->state = DRIVESTATE_UNAVAILABLE_LOCALLY;
|
||
|
}
|
||
|
else {
|
||
|
drive->state = DRIVESTATE_AVAILABLE_LOCALLY;
|
||
|
}
|
||
|
|
||
|
UNLOCKDriveForSession(session);
|
||
|
|
||
|
// DBGMSG("ReleaseDrive setting event:", (ULONG_PTR)session->sessionDriveEvent);
|
||
|
eventSetOk = PulseEvent(session->sessionDriveEvent);
|
||
|
if (!eventSetOk){
|
||
|
DWORD errCode = GetLastError();
|
||
|
DBGMSG("PulseEvent failed with:", errCode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(session->sig == DRIVEARB_SIG);
|
||
|
}
|
||
|
|
||
|
// DBGMSG("< ReleaseDrive", 0);
|
||
|
}
|
||
|
|