windows-nt/Source/XPSP1/NT/drivers/storage/drivearb/dll/drivearb.c
2020-09-26 16:20:57 +08:00

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);
}