mirror of https://github.com/tongzx/nt5src
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.
1462 lines
39 KiB
1462 lines
39 KiB
/*
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
forks.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the routines for manipulating the open forks.
|
|
|
|
Author:
|
|
|
|
Jameel Hyder (microsoft!jameelh)
|
|
|
|
|
|
Revision History:
|
|
25 Apr 1992 Initial Version
|
|
|
|
Notes: Tab stop: 4
|
|
--*/
|
|
|
|
#define FORK_LOCALS
|
|
#define FORKIO_LOCALS
|
|
#define FILENUM FILE_FORKS
|
|
|
|
#include <afp.h>
|
|
#include <client.h>
|
|
#include <fdparm.h>
|
|
#include <pathmap.h>
|
|
#include <scavengr.h>
|
|
#include <afpinfo.h>
|
|
#include <forkio.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( INIT, AfpForksInit)
|
|
#pragma alloc_text( PAGE, afpForkConvertToAbsOffSize)
|
|
#pragma alloc_text( PAGE_AFP, AfpAdmWForkClose)
|
|
#pragma alloc_text( PAGE_AFP, AfpForkReferenceById)
|
|
#endif
|
|
|
|
/*** AfpForksInit
|
|
*
|
|
* Initialize locks for forks.
|
|
*/
|
|
NTSTATUS
|
|
AfpForksInit(
|
|
VOID
|
|
)
|
|
{
|
|
INITIALIZE_SPIN_LOCK(&AfpForksLock);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*** AfpForkReferenceByRefNum
|
|
*
|
|
* Map a OForkRefNum to an open fork entry for the session and reference it.
|
|
*
|
|
* LOCKS: ofe_Lock
|
|
*
|
|
* CALLABLE at DISPATCH_LEVEL ONLY !!!
|
|
*/
|
|
POPENFORKENTRY FASTCALL
|
|
AfpForkReferenceByRefNum(
|
|
IN PSDA pSda,
|
|
IN DWORD OForkRefNum
|
|
)
|
|
{
|
|
POPENFORKSESS pOpenForkSess;
|
|
POPENFORKENTRY pOpenForkEntry;
|
|
POPENFORKENTRY pOFEntry = NULL;
|
|
|
|
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
ASSERT (VALID_SDA(pSda));
|
|
|
|
if (OForkRefNum == 0)
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_ERR,
|
|
("AfpForkReferenceByRefNum: client sent 0 for ForkRefNumFork\n"));
|
|
return(NULL);
|
|
}
|
|
|
|
pOpenForkSess = &pSda->sda_OpenForkSess;
|
|
while (OForkRefNum > FORK_OPEN_CHUNKS)
|
|
{
|
|
OForkRefNum -= FORK_OPEN_CHUNKS;
|
|
pOpenForkSess = pOpenForkSess->ofs_Link;
|
|
if (pOpenForkSess == NULL)
|
|
return NULL;
|
|
}
|
|
pOpenForkEntry = pOpenForkSess->ofs_pOpenForkEntry[OForkRefNum-1];
|
|
|
|
// If this has been marked closed, then return NULL
|
|
|
|
if (pOpenForkEntry != NULL)
|
|
{
|
|
ASSERT(VALID_OPENFORKENTRY(pOpenForkEntry));
|
|
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pOpenForkEntry->ofe_Lock);
|
|
|
|
if (!(pOpenForkEntry->ofe_Flags & OPEN_FORK_CLOSING))
|
|
{
|
|
pOpenForkEntry->ofe_RefCount ++;
|
|
pOFEntry = pOpenForkEntry;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkEntry->ofe_Lock);
|
|
}
|
|
|
|
return pOFEntry;
|
|
}
|
|
|
|
|
|
/*** AfpForkReferenceByPointer
|
|
*
|
|
* Reference the Open Fork Entry. This is used by the admin APIs.
|
|
*
|
|
* LOCKS: ofe_Lock
|
|
*/
|
|
POPENFORKENTRY FASTCALL
|
|
AfpForkReferenceByPointer(
|
|
IN POPENFORKENTRY pOpenForkEntry
|
|
)
|
|
{
|
|
POPENFORKENTRY pOFEntry = NULL;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT (VALID_OPENFORKENTRY(pOpenForkEntry));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, &OldIrql);
|
|
|
|
if (!(pOpenForkEntry->ofe_Flags & OPEN_FORK_CLOSING))
|
|
{
|
|
pOpenForkEntry->ofe_RefCount ++;
|
|
pOFEntry = pOpenForkEntry;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, OldIrql);
|
|
|
|
return pOFEntry;
|
|
}
|
|
|
|
|
|
/*** AfpForkReferenceById
|
|
*
|
|
* Reference the Open Fork Entry. This is used by the admin APIs.
|
|
*
|
|
* LOCKS: ofe_Lock, AfpForksLock
|
|
* LOCK_ORDER: ofe_Lock after AfpForksLock
|
|
*/
|
|
POPENFORKENTRY FASTCALL
|
|
AfpForkReferenceById(
|
|
IN DWORD ForkId
|
|
)
|
|
{
|
|
POPENFORKENTRY pOpenForkEntry;
|
|
POPENFORKENTRY pOFEntry = NULL;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT (ForkId != 0);
|
|
|
|
ACQUIRE_SPIN_LOCK(&AfpForksLock, &OldIrql);
|
|
|
|
for (pOpenForkEntry = AfpOpenForksList;
|
|
(pOpenForkEntry != NULL) && (pOpenForkEntry->ofe_ForkId >= ForkId);
|
|
pOpenForkEntry = pOpenForkEntry->ofe_Next)
|
|
{
|
|
if (pOpenForkEntry->ofe_ForkId == ForkId)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pOpenForkEntry->ofe_Lock);
|
|
|
|
if (!(pOpenForkEntry->ofe_Flags & OPEN_FORK_CLOSING))
|
|
{
|
|
pOFEntry = pOpenForkEntry;
|
|
pOpenForkEntry->ofe_RefCount ++;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkEntry->ofe_Lock);
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&AfpForksLock, OldIrql);
|
|
|
|
return pOFEntry;
|
|
}
|
|
|
|
|
|
/*** AfpForkClose
|
|
*
|
|
* Close an open fork. Simply set the close flag on the open fork and update
|
|
* connection counts, if any.
|
|
*
|
|
* LOCKS: ofd_EntryLock, cds_ConnLock
|
|
*/
|
|
VOID
|
|
AfpForkClose(
|
|
IN POPENFORKENTRY pOpenForkEntry
|
|
)
|
|
{
|
|
PCONNDESC pConnDesc;
|
|
PVOLDESC pVolDesc = pOpenForkEntry->ofe_pOpenForkDesc->ofd_pVolDesc;
|
|
KIRQL OldIrql;
|
|
BOOLEAN fAlreadyClosing=FALSE;
|
|
|
|
|
|
ASSERT(VALID_CONNDESC(pOpenForkEntry->ofe_pConnDesc));
|
|
|
|
pConnDesc = pOpenForkEntry->ofe_pConnDesc;
|
|
|
|
if ((pConnDesc != NULL) &&
|
|
(AfpConnectionReferenceByPointer(pConnDesc) != NULL))
|
|
{
|
|
ASSERT (pConnDesc->cds_pVolDesc == pVolDesc);
|
|
INTERLOCKED_DECREMENT_LONG(&pConnDesc->cds_cOpenForks);
|
|
|
|
// update the disk quota for this user
|
|
if (pVolDesc->vds_Flags & VOLUME_DISKQUOTA_ENABLED)
|
|
{
|
|
// reference again: afpUpdateDiskQuotaInfo will remove this refcount
|
|
if (AfpConnectionReferenceByPointer(pConnDesc) != NULL)
|
|
{
|
|
afpUpdateDiskQuotaInfo(pConnDesc);
|
|
}
|
|
}
|
|
|
|
AfpConnectionDereference(pConnDesc);
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, &OldIrql);
|
|
|
|
if (!(pOpenForkEntry->ofe_Flags & OPEN_FORK_CLOSING))
|
|
{
|
|
pOpenForkEntry->ofe_Flags |= OPEN_FORK_CLOSING;
|
|
}
|
|
else
|
|
{
|
|
fAlreadyClosing = TRUE;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, OldIrql);
|
|
|
|
if (!fAlreadyClosing)
|
|
{
|
|
// Take away the creation reference
|
|
AfpForkDereference(pOpenForkEntry);
|
|
}
|
|
}
|
|
|
|
|
|
/*** AfpForkDereference
|
|
*
|
|
* Dereference an open fork entry. If it is marked for deletion and this is
|
|
* the last reference, then it is cleaned up.
|
|
*
|
|
* LOCKS: AfpForksLock (SPIN), vds_VolLock (SPIN), ofd_Lock (SPIN),
|
|
* LOCKS: ofe_Lock (SPIN), AfpStatisticsLock (SPIN), sda_Lock (SPIN)
|
|
*
|
|
* LOCK_ORDER: AfpStatisticsLock after ofd_Lock after vds_VolLock
|
|
*
|
|
*/
|
|
VOID FASTCALL
|
|
AfpForkDereference(
|
|
IN POPENFORKENTRY pOpenForkEntry
|
|
)
|
|
{
|
|
POPENFORKSESS pOpenForkSess;
|
|
POPENFORKDESC pOpenForkDesc;
|
|
PVOLDESC pVolDesc;
|
|
PFORKLOCK pForkLock, *ppForkLock;
|
|
DWORD OForkRefNum, FileNum, NumLocks;
|
|
PSDA pSda;
|
|
KIRQL OldIrql;
|
|
BOOLEAN Resource, LastClose = False;
|
|
BOOLEAN Cleanup;
|
|
PDFENTRY pDfEntry = NULL;
|
|
FORKSIZE forklen;
|
|
DWORD Status;
|
|
|
|
ASSERT(VALID_OPENFORKENTRY(pOpenForkEntry));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, &OldIrql);
|
|
|
|
pOpenForkEntry->ofe_RefCount --;
|
|
|
|
ASSERT(pOpenForkEntry->ofe_RefCount >= 0);
|
|
|
|
Cleanup = (pOpenForkEntry->ofe_RefCount == 0);
|
|
|
|
RELEASE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, OldIrql);
|
|
|
|
if (!Cleanup)
|
|
return;
|
|
|
|
// Unlink this from the global list
|
|
ACQUIRE_SPIN_LOCK(&AfpForksLock, &OldIrql);
|
|
|
|
AfpUnlinkDouble(pOpenForkEntry, ofe_Next, ofe_Prev);
|
|
AfpNumOpenForks --;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpForksLock, OldIrql);
|
|
|
|
// Clean up the rest
|
|
pOpenForkDesc = pOpenForkEntry->ofe_pOpenForkDesc;
|
|
pVolDesc = pOpenForkDesc->ofd_pVolDesc;
|
|
|
|
ASSERT(VALID_CONNDESC(pOpenForkEntry->ofe_pConnDesc));
|
|
pSda = pOpenForkEntry->ofe_pSda;
|
|
|
|
ASSERT(VALID_OPENFORKDESC(pOpenForkDesc));
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkDereference: Closing Fork %ld for Session %ld\n",
|
|
pOpenForkEntry->ofe_ForkId, pSda->sda_SessionId));
|
|
|
|
// Save OForkRefNum for clearing up the Sda entry later on
|
|
OForkRefNum = pOpenForkEntry->ofe_OForkRefNum;
|
|
Resource = RESCFORK(pOpenForkEntry);
|
|
|
|
// We are not relying on
|
|
// change notifies to update our cached DFE Fork lengths and
|
|
// modified times from Writes and SetForkParms, so we must do it
|
|
// ourselves at the time when the fork handle is closed by mac.
|
|
// Note the order that the locks are taken here and for
|
|
// FpExchangeFiles/AfpExchangeForkAfpIds to prevent a FileId stored
|
|
// in the OpenForkDesc from changing out from under us due to
|
|
// an FpExchangeFiles call.
|
|
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_ExchangeFilesLock);
|
|
|
|
// Save file number so we can clear the ALREADY_OPEN flag in the DFEntry.
|
|
FileNum = pOpenForkDesc->ofd_FileNumber;
|
|
|
|
// Get rid of locks for this fork entry and reduce the use count
|
|
// We do not actually have to unlock the ranges as the close will
|
|
// get rid of them for us. If use count goes to zero, also unlink
|
|
// this fork desc from the volume list.
|
|
|
|
ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pOpenForkDesc->ofd_Lock);
|
|
|
|
pOpenForkDesc->ofd_UseCount --;
|
|
|
|
for (NumLocks = 0, ppForkLock = &pOpenForkDesc->ofd_pForkLock;
|
|
(pForkLock = *ppForkLock) != NULL;
|
|
NOTHING)
|
|
{
|
|
if (pForkLock->flo_pOpenForkEntry == pOpenForkEntry)
|
|
{
|
|
ASSERT(pOpenForkDesc->ofd_NumLocks > 0);
|
|
pOpenForkDesc->ofd_NumLocks --;
|
|
ASSERT(pOpenForkEntry->ofe_cLocks > 0);
|
|
#if DBG
|
|
pOpenForkEntry->ofe_cLocks --;
|
|
#endif
|
|
NumLocks ++;
|
|
*ppForkLock = pForkLock->flo_Next;
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkDereference: Freeing lock %lx\n", pForkLock));
|
|
AfpIOFreeBuffer(pForkLock);
|
|
}
|
|
else ppForkLock = &pForkLock->flo_Next;
|
|
}
|
|
|
|
INTERLOCKED_ADD_ULONG_DPC(&AfpServerStatistics.stat_CurrentFileLocks,
|
|
(ULONG)(-(LONG)NumLocks),
|
|
&(AfpStatisticsLock.SpinLock));
|
|
|
|
ASSERT (pOpenForkEntry->ofe_cLocks == 0);
|
|
|
|
if (pOpenForkDesc->ofd_UseCount == 0)
|
|
{
|
|
ASSERT (pOpenForkDesc->ofd_NumLocks == 0);
|
|
ASSERT (pOpenForkDesc->ofd_cOpenR <= FORK_OPEN_READ);
|
|
ASSERT (pOpenForkDesc->ofd_cOpenW <= FORK_OPEN_WRITE);
|
|
ASSERT (pOpenForkDesc->ofd_cDenyR <= FORK_DENY_READ);
|
|
ASSERT (pOpenForkDesc->ofd_cDenyW <= FORK_DENY_WRITE);
|
|
|
|
LastClose = True;
|
|
|
|
// Unlink the OpenForkDesc from the Volume Descriptor
|
|
AfpUnlinkDouble(pOpenForkDesc, ofd_Next, ofd_Prev);
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkDesc->ofd_Lock);
|
|
RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
|
|
|
|
// Free the memory for the OpenFork descriptor and path buffer
|
|
if (pOpenForkDesc->ofd_FilePath.Length > 0)
|
|
{
|
|
AfpFreeMemory(pOpenForkDesc->ofd_FilePath.Buffer);
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkDereference: Freeing path to file %lx\n",
|
|
pOpenForkDesc->ofd_FilePath.Buffer));
|
|
}
|
|
|
|
// Dereference the volume descriptor now
|
|
AfpVolumeDereference(pVolDesc);
|
|
|
|
// Finally free the open fork descriptor
|
|
AfpFreeMemory(pOpenForkDesc);
|
|
}
|
|
else
|
|
{
|
|
// Update the open & deny modes
|
|
pOpenForkDesc->ofd_cOpenR -= (pOpenForkEntry->ofe_OpenMode & FORK_OPEN_READ);
|
|
pOpenForkDesc->ofd_cOpenW -= (pOpenForkEntry->ofe_OpenMode & FORK_OPEN_WRITE);
|
|
pOpenForkDesc->ofd_cDenyR -= (pOpenForkEntry->ofe_DenyMode & FORK_OPEN_READ);
|
|
pOpenForkDesc->ofd_cDenyW -= (pOpenForkEntry->ofe_DenyMode & FORK_OPEN_WRITE);
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkDesc->ofd_Lock);
|
|
RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
|
|
}
|
|
|
|
// Lookup the DFE entry by ID, query for the fork length and
|
|
// for the appropriate time (LastWriteTime for DATA$ fork,
|
|
// ChangeTime for Resource) and set the LastWriteTime
|
|
// to last ChangeTime if its resource fork. If its the last close
|
|
// for this fork, since we already have the DFE pointer and hold
|
|
// the IdDb SWMR, update the DFE_FLAGS_x_ALREADYOPEN flag. Then
|
|
// release the SWMR.
|
|
|
|
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
|
|
|
|
pDfEntry = AfpFindDfEntryById(pVolDesc,
|
|
FileNum,
|
|
DFE_FILE);
|
|
|
|
Status = AfpIoQuerySize(&pOpenForkEntry->ofe_FileSysHandle,
|
|
&forklen);
|
|
|
|
if (NT_SUCCESS(Status) && (pDfEntry != NULL))
|
|
{
|
|
if (IS_VOLUME_NTFS(pVolDesc))
|
|
{
|
|
AfpIoChangeNTModTime(&pOpenForkEntry->ofe_FileSysHandle,
|
|
&pDfEntry->dfe_LastModTime);
|
|
}
|
|
|
|
if (Resource)
|
|
{
|
|
pDfEntry->dfe_RescLen = forklen.LowPart;
|
|
if (LastClose)
|
|
{
|
|
pDfEntry->dfe_Flags &= ~DFE_FLAGS_R_ALREADYOPEN;
|
|
#ifdef AGE_DFES
|
|
if (IS_VOLUME_AGING_DFES(pVolDesc))
|
|
{
|
|
pDfEntry->dfe_Parent->dfe_pDirEntry->de_ChildForkOpenCount --;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pDfEntry->dfe_DataLen = forklen.LowPart;
|
|
if (LastClose)
|
|
{
|
|
pDfEntry->dfe_Flags &= ~DFE_FLAGS_D_ALREADYOPEN;
|
|
#ifdef AGE_DFES
|
|
if (IS_VOLUME_AGING_DFES(pVolDesc))
|
|
{
|
|
pDfEntry->dfe_Parent->dfe_pDirEntry->de_ChildForkOpenCount --;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
|
|
AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);
|
|
|
|
// Now clear up the entry in the Sda
|
|
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
|
|
|
|
pSda->sda_cOpenForks--;
|
|
pOpenForkSess = &pSda->sda_OpenForkSess;
|
|
while (OForkRefNum > FORK_OPEN_CHUNKS)
|
|
{
|
|
OForkRefNum -= FORK_OPEN_CHUNKS;
|
|
pOpenForkSess = pOpenForkSess->ofs_Link;
|
|
ASSERT (pOpenForkSess != NULL);
|
|
}
|
|
|
|
ASSERT (pOpenForkEntry == pOpenForkSess->ofs_pOpenForkEntry[OForkRefNum-1]);
|
|
pOpenForkSess->ofs_pOpenForkEntry[OForkRefNum-1] = NULL;
|
|
|
|
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
|
|
|
|
AfpSdaDereferenceSession(pSda); // Remove the reference for this fork here
|
|
|
|
ASSERT(VALID_CONNDESC(pOpenForkEntry->ofe_pConnDesc));
|
|
AfpConnectionDereference(pOpenForkEntry->ofe_pConnDesc);
|
|
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
// All done, close the fork handle and free the OFE
|
|
if (pOpenForkEntry->ofe_ForkHandle != NULL)
|
|
{
|
|
AfpIoClose(&pOpenForkEntry->ofe_FileSysHandle);
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkDereference: Fork %ld is history\n", pOpenForkEntry->ofe_ForkId));
|
|
|
|
AfpFreeMemory(pOpenForkEntry);
|
|
}
|
|
|
|
|
|
/*** AfpCheckDenyConflict
|
|
*
|
|
* Check if the requested Open & Deny Modes clash with current open & deny modes.
|
|
*
|
|
* LOCKS: ofd_Lock, vds_VolLock (SPIN) IFF ppOpenForkDesc is NULL ELSE
|
|
* LOCKS_ASSUMED: vds_VolLock (SPIN)
|
|
*
|
|
* LOCK_ORDER: ofd_Lock after vds_VolLock
|
|
*/
|
|
AFPSTATUS
|
|
AfpCheckDenyConflict(
|
|
IN PVOLDESC pVolDesc,
|
|
IN DWORD AfpId,
|
|
IN BOOLEAN Resource,
|
|
IN BYTE OpenMode,
|
|
IN BYTE DenyMode,
|
|
IN POPENFORKDESC * ppOpenForkDesc OPTIONAL
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
POPENFORKDESC pOpenForkDesc;
|
|
AFPSTATUS Status = AFP_ERR_NONE;
|
|
BOOLEAN Foundit = False;
|
|
|
|
if (ARGUMENT_PRESENT(ppOpenForkDesc))
|
|
*ppOpenForkDesc = NULL;
|
|
else ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
|
|
|
|
// check the list of open forks in this volume for any deny conflicts
|
|
for (pOpenForkDesc = pVolDesc->vds_pOpenForkDesc;
|
|
pOpenForkDesc != NULL;
|
|
pOpenForkDesc = pOpenForkDesc->ofd_Next)
|
|
{
|
|
BOOLEAN DescRes;
|
|
|
|
// Take the DescLock before looking at AfpId since FpExchangeFiles
|
|
// can change the ID
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pOpenForkDesc->ofd_Lock);
|
|
|
|
DescRes = (pOpenForkDesc->ofd_Flags & OPEN_FORK_RESOURCE) ? True : False;
|
|
if ((pOpenForkDesc->ofd_FileNumber == AfpId) &&
|
|
!(DescRes ^ Resource))
|
|
{
|
|
Foundit = True;
|
|
// Check that the open & deny modes do not clash with existing
|
|
// settings
|
|
if (((OpenMode & FORK_OPEN_READ) && (pOpenForkDesc->ofd_cDenyR > 0)) ||
|
|
((OpenMode & FORK_OPEN_WRITE) && (pOpenForkDesc->ofd_cDenyW > 0)) ||
|
|
((DenyMode & FORK_DENY_READ) && (pOpenForkDesc->ofd_cOpenR > 0)) ||
|
|
((DenyMode & FORK_DENY_WRITE) && (pOpenForkDesc->ofd_cOpenW > 0)))
|
|
{
|
|
Status = AFP_ERR_DENY_CONFLICT;
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkDesc->ofd_Lock);
|
|
if (Foundit)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(ppOpenForkDesc))
|
|
*ppOpenForkDesc = pOpenForkDesc;
|
|
else RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** AfpForkOpen
|
|
*
|
|
* This is called after the fork has been successfully opened. Deny-mode conflicts are
|
|
* checked and if no conflicts are found, appropriate data structures are created and
|
|
* linked. If a deny conflict results, return NULL for pOpenForkEntry.
|
|
*
|
|
* LOCKS: AfpForksLock (SPIN), vds_VolLock (SPIN), cds_VolLock (SPIN), ofd_Lock (SPIN), ofe_Lock (SPIN)
|
|
* LOCK_ORDER: vds_ExchangeFilesLock, then ofd_Lock after vds_VolLock
|
|
* LOCKS_ASSUMED: vds_ExchangeFilesLock
|
|
*/
|
|
AFPSTATUS
|
|
AfpForkOpen(
|
|
IN PSDA pSda,
|
|
IN PCONNDESC pConnDesc,
|
|
IN PPATHMAPENTITY pPME,
|
|
IN PFILEDIRPARM pFDParm,
|
|
IN DWORD AccessMode,
|
|
IN BOOLEAN Resource,
|
|
OUT POPENFORKENTRY * ppOpenForkEntry,
|
|
OUT PBOOLEAN pCleanupExchgLock
|
|
)
|
|
{
|
|
|
|
POPENFORKENTRY pOpenForkEntry;
|
|
POPENFORKDESC pOpenForkDesc;
|
|
PVOLDESC pVolDesc;
|
|
AFPSTATUS Status = AFP_ERR_NONE;
|
|
KIRQL OldIrql;
|
|
BYTE OpenMode, DenyMode;
|
|
BOOLEAN NewForkDesc = False;
|
|
|
|
ASSERT(VALID_CONNDESC(pConnDesc));
|
|
|
|
pVolDesc = pConnDesc->cds_pVolDesc;
|
|
|
|
ASSERT(VALID_VOLDESC(pVolDesc));
|
|
|
|
OpenMode = (BYTE)(AccessMode & FORK_OPEN_MASK);
|
|
DenyMode = (BYTE)((AccessMode >> FORK_DENY_SHIFT) & FORK_DENY_MASK);
|
|
*ppOpenForkEntry = NULL;
|
|
|
|
if ((pOpenForkEntry = (POPENFORKENTRY)AfpAllocZeroedNonPagedMemory(sizeof(OPENFORKENTRY))) == NULL)
|
|
return AFP_ERR_MISC;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
|
|
|
|
Status = AfpCheckDenyConflict(pVolDesc,
|
|
pFDParm->_fdp_AfpId,
|
|
Resource,
|
|
OpenMode,
|
|
DenyMode,
|
|
&pOpenForkDesc);
|
|
|
|
if (pOpenForkDesc == NULL)
|
|
{
|
|
// This fork has not been opened. We can party.
|
|
if ((pOpenForkDesc = (POPENFORKDESC)AfpAllocZeroedNonPagedMemory(sizeof(OPENFORKDESC))) == NULL)
|
|
Status = AFP_ERR_MISC;
|
|
else
|
|
{
|
|
NewForkDesc = True;
|
|
INITIALIZE_SPIN_LOCK(&pOpenForkDesc->ofd_Lock);
|
|
#if DBG
|
|
pOpenForkDesc->Signature = OPENFORKDESC_SIGNATURE;
|
|
#endif
|
|
pOpenForkDesc->ofd_pVolDesc = pVolDesc;
|
|
|
|
pOpenForkDesc->ofd_FileNumber = pFDParm->_fdp_AfpId;
|
|
pOpenForkDesc->ofd_Flags = Resource ?
|
|
OPEN_FORK_RESOURCE : OPEN_FORK_DATA;
|
|
}
|
|
}
|
|
|
|
if ((pOpenForkDesc != NULL) && (Status == AFP_ERR_NONE))
|
|
{
|
|
// A lock is not needed if this is a new fork desc
|
|
if (!NewForkDesc)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pOpenForkDesc->ofd_Lock);
|
|
}
|
|
|
|
pOpenForkDesc->ofd_UseCount ++;
|
|
pOpenForkDesc->ofd_cOpenR += (OpenMode & FORK_OPEN_READ);
|
|
pOpenForkDesc->ofd_cOpenW += (OpenMode & FORK_OPEN_WRITE);
|
|
pOpenForkDesc->ofd_cDenyR += (DenyMode & FORK_DENY_READ);
|
|
pOpenForkDesc->ofd_cDenyW += (DenyMode & FORK_DENY_WRITE);
|
|
|
|
if (NewForkDesc)
|
|
{
|
|
// Now link this into the volume descriptor but only if it is a
|
|
// new forkdesc. Explicitly reference the volume descriptor. We
|
|
// cannot call AfpVolumeReference here since we already own the
|
|
// volume lock Moreover since the connection is owning it, the
|
|
// volume is OK. Initialize the volume relative path of the file
|
|
// being opened from the PME.
|
|
pOpenForkDesc->ofd_FilePath = pPME->pme_FullPath;
|
|
pOpenForkDesc->ofd_FileName = pPME->pme_UTail;
|
|
|
|
// Set the pme_FullPath to NULL so that it does not get freed up
|
|
pPME->pme_FullPath.Buffer = NULL;
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForksOpen: Initializing forkdesc with path %Z(%lx), name %Z(%lx)\n",
|
|
&pOpenForkDesc->ofd_FilePath, pOpenForkDesc->ofd_FilePath.Buffer,
|
|
&pOpenForkDesc->ofd_FileName, pOpenForkDesc->ofd_FileName.Buffer));
|
|
|
|
pVolDesc->vds_RefCount ++;
|
|
AfpLinkDoubleAtHead(pVolDesc->vds_pOpenForkDesc,
|
|
pOpenForkDesc,
|
|
ofd_Next,
|
|
ofd_Prev);
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkDesc->ofd_Lock);
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
|
|
|
|
if ((pOpenForkDesc == NULL) || (Status != AFP_ERR_NONE))
|
|
{
|
|
AfpFreeMemory(pOpenForkEntry);
|
|
return Status;
|
|
}
|
|
|
|
ASSERT (Status == AFP_ERR_NONE);
|
|
|
|
// All seems to be fine, so far. We'll go ahead and create the appropriate
|
|
// data structures and link them in. In case of errors we'll back out.
|
|
do
|
|
{
|
|
#if DBG
|
|
pOpenForkEntry->Signature = OPENFORKENTRY_SIGNATURE;
|
|
#endif
|
|
INITIALIZE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock);
|
|
pOpenForkEntry->ofe_pSda = pSda;
|
|
|
|
if (AfpConnectionReferenceByPointer(pConnDesc) == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_ERR,
|
|
("AfpForkOpen: couldn't reference pConnDesc\n"));
|
|
AfpFreeMemory(pOpenForkEntry);
|
|
return AFP_ERR_MISC;
|
|
}
|
|
pOpenForkEntry->ofe_pConnDesc = pConnDesc;
|
|
pOpenForkEntry->ofe_pOpenForkDesc = pOpenForkDesc;
|
|
pOpenForkEntry->ofe_OpenMode = OpenMode;
|
|
pOpenForkEntry->ofe_DenyMode = DenyMode;
|
|
pOpenForkEntry->ofe_FileSysHandle = pPME->pme_Handle;
|
|
pOpenForkEntry->ofe_Flags = Resource ?
|
|
OPEN_FORK_RESOURCE : OPEN_FORK_DATA;
|
|
// One reference for creation and the other for the api to dereference.
|
|
pOpenForkEntry->ofe_RefCount = 2;
|
|
if (!afpForkGetNewForkRefNumAndLinkInSda(pSda, pOpenForkEntry))
|
|
{
|
|
AfpFreeMemory(pOpenForkEntry);
|
|
AfpConnectionDereference(pConnDesc);
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_ERR,("AfpForkOpen: ...LinkInSda failed\n"));
|
|
return AFP_ERR_MISC;
|
|
}
|
|
|
|
// Now link this in global list
|
|
ACQUIRE_SPIN_LOCK(&AfpForksLock, &OldIrql);
|
|
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pSda->sda_Lock);
|
|
pSda->sda_cOpenForks++;
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
|
|
|
|
pOpenForkEntry->ofe_ForkId = afpNextForkId ++;
|
|
AfpLinkDoubleAtHead(AfpOpenForksList,
|
|
pOpenForkEntry,
|
|
ofe_Next,
|
|
ofe_Prev);
|
|
AfpNumOpenForks ++;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpForksLock, OldIrql);
|
|
|
|
if (NewForkDesc)
|
|
{
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
if ((Status = AfpSetDFFileFlags(pVolDesc,
|
|
pFDParm->_fdp_AfpId,
|
|
Resource ?
|
|
DFE_FLAGS_R_ALREADYOPEN :
|
|
DFE_FLAGS_D_ALREADYOPEN,
|
|
False,
|
|
False)) != AFP_ERR_NONE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*ppOpenForkEntry = pOpenForkEntry;
|
|
Status = AFP_ERR_NONE;
|
|
}
|
|
else
|
|
{
|
|
Status = AfpIoConvertNTStatusToAfpStatus(Status);
|
|
}
|
|
} while (False);
|
|
|
|
// Perform cleanup here, if we failed for any reason
|
|
if (Status != AFP_ERR_NONE)
|
|
{
|
|
ASSERT (pOpenForkEntry != NULL);
|
|
ACQUIRE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, &OldIrql);
|
|
|
|
pOpenForkEntry->ofe_Flags |= OPEN_FORK_CLOSING;
|
|
|
|
RELEASE_SPIN_LOCK(&pOpenForkEntry->ofe_Lock, OldIrql);
|
|
|
|
// We must free this lock on behalf of AfpFspDispOpenFork because
|
|
// the act of dereferencing it will end up calling ForkClose which
|
|
// also must take this lock. Indicate to caller that he should not
|
|
// attempt to release this lock again.
|
|
AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);
|
|
*pCleanupExchgLock = False;
|
|
|
|
// remove the refcount for the api (which won't see this pOpenForkEntry)
|
|
AfpForkDereference(pOpenForkEntry);
|
|
|
|
// remove the creation refcount
|
|
AfpForkDereference(pOpenForkEntry);
|
|
|
|
//
|
|
// Dereferencing the open fork entry will close the handle passed
|
|
// to us. NULL out the callers handle value so the caller does
|
|
// not try to close it again.
|
|
//
|
|
|
|
pPME->pme_Handle.fsh_FileHandle = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** AfpForkLockOperation
|
|
*
|
|
* Called for both ByteRangeLock/Unlock as well as Read/Write.
|
|
* For Lock, a check is made to ensure the requested range does not
|
|
* overlap an existing range FROM ANY OPEN FORK.
|
|
* For Unlock, the requested range MUST MATCH exactly with an existing
|
|
* locked range.
|
|
* For IO, return the effective range where IO is possible - could be
|
|
* potentially be an empty range. Note in this case that the start of
|
|
* the range must be free to get a non-empty range. For an empty range
|
|
* AFP_ERR_LOCK is returned. For a non-empty range AFP_ERR_NONE.
|
|
*
|
|
* Locks are maintained in a sorted (descending) order from the OpenForkDesc.
|
|
* The search can be abandoned if the start of the requested range is
|
|
* larger than the end of the encountered range.
|
|
*
|
|
* LOCKS: ofd_Lock (SPIN)
|
|
*/
|
|
AFPSTATUS
|
|
AfpForkLockOperation(
|
|
IN PSDA pSda,
|
|
IN POPENFORKENTRY pOpenForkEntry,
|
|
IN OUT PFORKOFFST pOffset,
|
|
IN OUT PFORKSIZE pSize,
|
|
IN LOCKOP Operation, // LOCK, UNLOCK or IOCHECK
|
|
IN BOOLEAN EndFlag // If True range is from end, else start
|
|
)
|
|
{
|
|
POPENFORKDESC pOpenForkDesc;
|
|
PFORKLOCK pForkLock, pForkLockNew, *ppForkLock;
|
|
IO_STATUS_BLOCK IoStsBlk;
|
|
PFAST_IO_DISPATCH pFastIoDisp;
|
|
KIRQL OldIrql;
|
|
AFPSTATUS Status;
|
|
LONG Offset, Size;
|
|
DWORD EndOff;
|
|
BOOLEAN UnlockForkDesc = True;
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: (%s) - (%ld,%ld,%ld,%ld)\n",
|
|
(Operation == LOCK) ? "Lock" : ((Operation == UNLOCK) ? "Unlock" : "Io"),
|
|
pOffset->LowPart, pSize->LowPart,
|
|
pOpenForkEntry->ofe_ForkId, pSda->sda_SessionId));
|
|
|
|
if (EndFlag)
|
|
{
|
|
LONG Off;
|
|
|
|
Size = pSize->LowPart;
|
|
if (pSize->QuadPart < 0)
|
|
{
|
|
FORKSIZE FSize;
|
|
|
|
FSize.QuadPart = -(pSize->QuadPart);
|
|
Size = -(LONG)(FSize.LowPart);
|
|
}
|
|
Off = pOffset->LowPart;
|
|
if (pOffset->QuadPart < 0)
|
|
{
|
|
FORKSIZE FOffset;
|
|
|
|
FOffset.QuadPart = -(pOffset->QuadPart);
|
|
Off = -(LONG)(FOffset.LowPart);
|
|
}
|
|
|
|
if ((Status = afpForkConvertToAbsOffSize(pOpenForkEntry,
|
|
Off,
|
|
&Size,
|
|
pOffset)) != AFP_ERR_NONE)
|
|
return Status;
|
|
pSize->QuadPart = Size;
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: Effective (%s) - (%ld,%ld,%ld,%ld)\n",
|
|
(Operation == LOCK) ? "Lock" : ((Operation == UNLOCK) ? "Unlock" : "Io"),
|
|
pOffset->LowPart, Size,
|
|
pOpenForkEntry->ofe_ForkId,
|
|
pSda->sda_SessionId));
|
|
}
|
|
|
|
Offset = pOffset->LowPart;
|
|
Size = pSize->LowPart;
|
|
|
|
// Walk down the list and check. If the option is to lock, then no locks
|
|
// should conflict. If the option is to unlock, then the lock should
|
|
// exist and owned. If the option is to check for Io, then either the
|
|
// overlapped range be 'owned' or the start of the range must not overap
|
|
// OPTIMIZATION - if there is only one instance of this fork open, then
|
|
// all locks belong to this fork and hence there can be no conflicts.
|
|
pOpenForkDesc = pOpenForkEntry->ofe_pOpenForkDesc;
|
|
|
|
ASSERT (pOpenForkDesc->ofd_UseCount > 0);
|
|
|
|
if ((Operation == IOCHECK) &&
|
|
(pOpenForkDesc->ofd_UseCount == 1))
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: Skipping for IOCHECK - UseCount %ld ,Locks %ld\n",
|
|
pOpenForkDesc->ofd_UseCount, pOpenForkDesc->ofd_NumLocks));
|
|
return AFP_ERR_NONE;
|
|
}
|
|
|
|
// Set default error code.
|
|
Status = (Operation == UNLOCK) ? AFP_ERR_RANGE_NOT_LOCKED : AFP_ERR_NONE;
|
|
|
|
EndOff = (DWORD)Offset + (DWORD)Size - 1;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pOpenForkDesc->ofd_Lock, &OldIrql);
|
|
|
|
for (ppForkLock = &pOpenForkDesc->ofd_pForkLock;
|
|
(pForkLock = *ppForkLock) != NULL;
|
|
ppForkLock = &pForkLock->flo_Next)
|
|
{
|
|
DWORD LEndOff;
|
|
|
|
// There are 4 possible ways locks can overlap
|
|
//
|
|
// 1 2
|
|
// +-----------+ +-----------+
|
|
// | | | |
|
|
// | |
|
|
// +-- LockRange --+
|
|
// | |
|
|
// | 3 |
|
|
// +-------+
|
|
// | 4 |
|
|
// +-----------------------+
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: (%s) - Found (%ld,%ld,%ld,%ld)\n",
|
|
(Operation == LOCK) ? "Lock" : ((Operation == UNLOCK) ? "Unlock" : "Io"),
|
|
pForkLock->flo_Offset, pForkLock->flo_Size,
|
|
pForkLock->flo_pOpenForkEntry->ofe_ForkId,
|
|
pForkLock->flo_Key));
|
|
|
|
// Calculate the end point of the current locked range
|
|
LEndOff = (DWORD)(pForkLock->flo_Offset) + (DWORD)(pForkLock->flo_Size) - 1;
|
|
|
|
// The list is ordered by descending flo_Offset. We can stop scanning
|
|
// if the start of the requested range is more than the end of the
|
|
// current locked range.
|
|
if ((DWORD)Offset > LEndOff)
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: %s Request (%ld, %ld) - Current (%ld,%ld), %s\n",
|
|
(Operation == LOCK) ? "Lock" : ((Operation == UNLOCK) ? "Unlock" : "Io"),
|
|
Offset, Size, pForkLock->flo_Offset, pForkLock->flo_Size,
|
|
(Operation == UNLOCK) ? "failing" : "success"));
|
|
break;
|
|
}
|
|
|
|
// The end of the requested range is beyond the locked range ?
|
|
// continue scanning.
|
|
if (EndOff < (DWORD)(pForkLock->flo_Offset))
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: %s Request (%ld, %ld) - Current (%ld,%ld), skipping\n",
|
|
(Operation == LOCK) ? "Lock" : ((Operation == UNLOCK) ? "Unlock" : "Io"),
|
|
Offset, Size, pForkLock->flo_Offset,
|
|
pForkLock->flo_Size,
|
|
(Operation == UNLOCK) ? "failing" : "success"));
|
|
continue;
|
|
}
|
|
|
|
// We have either a match or an overlap.
|
|
if (Operation == LOCK)
|
|
{
|
|
// For a lock request it is a failure.
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_WARN,
|
|
("AfpForkLockOperation: Lock Request (%ld, %ld) - Current (%ld,%ld), failing\n",
|
|
Offset, Size, pForkLock->flo_Offset, pForkLock->flo_Size));
|
|
Status = (pForkLock->flo_pOpenForkEntry == pOpenForkEntry) ?
|
|
AFP_ERR_RANGE_OVERLAP : AFP_ERR_LOCK;
|
|
}
|
|
else if (Operation == UNLOCK)
|
|
{
|
|
// For an unlock request, we must have an exact match. Also the session key
|
|
// and the OpenForkEntry must match
|
|
if ((Offset == pForkLock->flo_Offset) &&
|
|
(Size == pForkLock->flo_Size) &&
|
|
(pForkLock->flo_Key == pSda->sda_SessionId) &&
|
|
(pForkLock->flo_pOpenForkEntry == pOpenForkEntry))
|
|
{
|
|
// Unlink this lock from the list
|
|
*ppForkLock = pForkLock->flo_Next;
|
|
pOpenForkDesc->ofd_NumLocks --;
|
|
pOpenForkEntry->ofe_cLocks --;
|
|
RELEASE_SPIN_LOCK(&pOpenForkDesc->ofd_Lock, OldIrql);
|
|
UnlockForkDesc = False;
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: (Unlock) Deleting Range,Key (%ld,%ld,%ld,%ld)\n",
|
|
Offset, Size, pOpenForkEntry->ofe_ForkId, pSda->sda_SessionId));
|
|
|
|
// Try the fast I/O path first. If that fails, call AfpIoForkUnlock
|
|
// to use the normal build-an-IRP path.
|
|
pFastIoDisp = pOpenForkEntry->ofe_pDeviceObject->DriverObject->FastIoDispatch;
|
|
if ((pFastIoDisp != NULL) &&
|
|
(pFastIoDisp->FastIoUnlockSingle != NULL) &&
|
|
pFastIoDisp->FastIoUnlockSingle(AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject),
|
|
pOffset,
|
|
pSize,
|
|
AfpProcessObject,
|
|
pSda->sda_SessionId,
|
|
&IoStsBlk,
|
|
pOpenForkEntry->ofe_pDeviceObject))
|
|
{
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: Fast Unlock Succeeded\n"));
|
|
#ifdef PROFILING
|
|
// The fast I/O path worked. Update profile
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoSucceeded));
|
|
#endif
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_CurrentFileLocks,
|
|
(ULONG)-1,
|
|
&AfpStatisticsLock);
|
|
Status = AFP_ERR_NONE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoFailed));
|
|
#endif
|
|
Status = AfpIoForkLockUnlock(pSda, pForkLock, pOffset, pSize, FUNC_UNLOCK);
|
|
}
|
|
AfpIOFreeBuffer(pForkLock);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_WARN,
|
|
("AfpForkLockOperation: UnLock Request (%ld, %ld) - Current (%ld,%ld), failing\n",
|
|
Offset, Size, pForkLock->flo_Offset, pForkLock->flo_Size));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT (Operation == IOCHECK);
|
|
|
|
// Check if this is a conflict
|
|
if (pForkLock->flo_Key != pSda->sda_SessionId)
|
|
{
|
|
if ((Offset < pForkLock->flo_Offset) &&
|
|
(EndOff >= (DWORD)(pForkLock->flo_Offset)))
|
|
{
|
|
pSize->LowPart = (pForkLock->flo_Offset - Offset);
|
|
}
|
|
else Status = AFP_ERR_LOCK;
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: Conflict found\n"));
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: Our own lock found, ignoring\n"));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// We have the right status code. Do the needful
|
|
if (Operation == LOCK)
|
|
{
|
|
if (Status == AFP_ERR_NONE)
|
|
{
|
|
Status = AFP_ERR_MISC;
|
|
// Allocate the locks out of the pool.
|
|
if ((pForkLockNew = (PFORKLOCK)AfpIOAllocBuffer(sizeof(FORKLOCK))) != NULL)
|
|
{
|
|
#if DBG
|
|
pForkLockNew->Signature = FORKLOCK_SIGNATURE;
|
|
#endif
|
|
// Link this in such that the list is sorted in ascending order.
|
|
// ppForkLock points to the place where the new lock will be
|
|
// added, pForkLock is the next in the list.
|
|
pForkLockNew->flo_Next = pForkLock;
|
|
*ppForkLock = pForkLockNew;
|
|
|
|
pForkLockNew->flo_Key = pSda->sda_SessionId;
|
|
pForkLockNew->flo_pOpenForkEntry = pOpenForkEntry;
|
|
pForkLockNew->flo_Offset = Offset;
|
|
pForkLockNew->flo_Size = Size;
|
|
pOpenForkDesc->ofd_NumLocks ++;
|
|
pOpenForkEntry->ofe_cLocks ++;
|
|
RELEASE_SPIN_LOCK(&pOpenForkDesc->ofd_Lock, OldIrql);
|
|
UnlockForkDesc = False;
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("AfpForkLockOperation: Adding Range,Key (%ld,%ld,%ld,%ld)\n",
|
|
Offset, Size,
|
|
pOpenForkEntry->ofe_ForkId, pSda->sda_SessionId));
|
|
|
|
// Try the fast I/O path first. If that fails, fall through to the
|
|
// normal build-an-IRP path.
|
|
pFastIoDisp = pOpenForkEntry->ofe_pDeviceObject->DriverObject->FastIoDispatch;
|
|
if ((pFastIoDisp != NULL) &&
|
|
(pFastIoDisp->FastIoLock != NULL) &&
|
|
pFastIoDisp->FastIoLock(AfpGetRealFileObject(pOpenForkEntry->ofe_pFileObject),
|
|
pOffset,
|
|
pSize,
|
|
AfpProcessObject,
|
|
pSda->sda_SessionId,
|
|
True, // Fail immediately
|
|
True, // Exclusive
|
|
&IoStsBlk,
|
|
pOpenForkEntry->ofe_pDeviceObject))
|
|
{
|
|
if (NT_SUCCESS(IoStsBlk.Status) ||
|
|
(IoStsBlk.Status == STATUS_LOCK_NOT_GRANTED))
|
|
{
|
|
DBGPRINT(DBG_COMP_AFPAPI_FORK, DBG_LEVEL_INFO,
|
|
("AfpIoForkLock: Fast Lock Succeeded\n"));
|
|
|
|
#ifdef PROFILING
|
|
// The fast I/O path worked. Update profile
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoSucceeded));
|
|
#endif
|
|
if (IoStsBlk.Status == STATUS_LOCK_NOT_GRANTED)
|
|
{
|
|
Status = AFP_ERR_LOCK;
|
|
}
|
|
else
|
|
{
|
|
Status = AFP_ERR_NONE;
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_CurrentFileLocks,
|
|
1,
|
|
&AfpStatisticsLock);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)(&AfpServerProfile->perf_NumFastIoFailed));
|
|
#endif
|
|
|
|
Status = AfpIoForkLockUnlock(pSda, pForkLockNew, pOffset, pSize, FUNC_LOCK);
|
|
}
|
|
|
|
if ((Status != AFP_ERR_NONE) &&
|
|
(Status != AFP_ERR_EXTENDED) &&
|
|
(Status != AFP_ERR_QUEUE))
|
|
{
|
|
// Undo the above work
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_ERR,
|
|
("AfpForkLockOperation: AfpIoForkLock failed %lx, aborting for range %ld,%ld\n",
|
|
Status, Offset, EndOff));
|
|
AfpForkLockUnlink(pForkLockNew);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (UnlockForkDesc)
|
|
RELEASE_SPIN_LOCK(&pOpenForkDesc->ofd_Lock, OldIrql);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** AfpForkLockUnlink
|
|
*
|
|
* Unlink this lock from its open file descriptor and free it.
|
|
*/
|
|
VOID
|
|
AfpForkLockUnlink(
|
|
IN PFORKLOCK pForkLock
|
|
)
|
|
{
|
|
POPENFORKDESC pOpenForkDesc = pForkLock->flo_pOpenForkEntry->ofe_pOpenForkDesc;
|
|
PFORKLOCK * ppForkLock;
|
|
PFORKLOCK pTmpForkLock;
|
|
KIRQL OldIrql;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pOpenForkDesc->ofd_Lock, &OldIrql);
|
|
|
|
pOpenForkDesc->ofd_NumLocks --;
|
|
pForkLock->flo_pOpenForkEntry->ofe_cLocks --;
|
|
|
|
for (ppForkLock = &pOpenForkDesc->ofd_pForkLock;
|
|
(pTmpForkLock = *ppForkLock) != NULL;
|
|
ppForkLock = &pTmpForkLock->flo_Next)
|
|
{
|
|
if (*ppForkLock == pForkLock)
|
|
{
|
|
*ppForkLock = pForkLock->flo_Next;
|
|
break;
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&pOpenForkDesc->ofd_Lock, OldIrql);
|
|
AfpIOFreeBuffer(pForkLock);
|
|
}
|
|
|
|
|
|
/*** afpForkConvertToAbsOffSize
|
|
*
|
|
* Convert the offset,size pair as supplied by the client to their absolute
|
|
* values.
|
|
*/
|
|
LOCAL AFPSTATUS
|
|
afpForkConvertToAbsOffSize(
|
|
IN POPENFORKENTRY pOpenForkEntry,
|
|
IN LONG Offset,
|
|
IN OUT PLONG pSize,
|
|
OUT PFORKOFFST pAbsOffset
|
|
)
|
|
{
|
|
AFPSTATUS Status;
|
|
|
|
PAGED_CODE ();
|
|
|
|
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("afpForkConvertToAbsOffSize: Converting %ld, %ld\n", Offset, *pSize));
|
|
|
|
// We are relative to the end, then convert it to absolute
|
|
if ((Status = AfpIoQuerySize(&pOpenForkEntry->ofe_FileSysHandle,
|
|
pAbsOffset)) == AFP_ERR_NONE)
|
|
{
|
|
FORKOFFST EndRange, MaxOffset;
|
|
|
|
MaxOffset.QuadPart = Offset;
|
|
pAbsOffset->QuadPart += MaxOffset.QuadPart;
|
|
MaxOffset.QuadPart = MAXLONG;
|
|
|
|
// Now we have the *pAbsOffset and Size. Normalize the size.
|
|
// if the *pAbsOffset is > MAXLONG, refuse this.
|
|
if ((pAbsOffset->QuadPart > MaxOffset.QuadPart) ||
|
|
(pAbsOffset->QuadPart < 0))
|
|
Status = AFP_ERR_PARAM;
|
|
|
|
else
|
|
{
|
|
EndRange.QuadPart = pAbsOffset->QuadPart + *pSize;
|
|
if (EndRange.QuadPart >= MaxOffset.QuadPart)
|
|
*pSize = (MAXLONG - pAbsOffset->LowPart);
|
|
|
|
DBGPRINT(DBG_COMP_FORKS, DBG_LEVEL_INFO,
|
|
("afpForkConvertToAbsOffSize: Converted to %ld, %ld\n",
|
|
pAbsOffset->LowPart, *pSize));
|
|
Status = AFP_ERR_NONE;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** AfpAdmWForkClose
|
|
*
|
|
* Close a fork forcibly. This is an admin operation and must be queued
|
|
* up since this can potentially cause filesystem operations that are valid
|
|
* only in the system process context.
|
|
*/
|
|
AFPSTATUS
|
|
AfpAdmWForkClose(
|
|
IN OUT PVOID InBuf OPTIONAL,
|
|
IN LONG OutBufLen OPTIONAL,
|
|
OUT PVOID OutBuf OPTIONAL
|
|
)
|
|
{
|
|
PAFP_FILE_INFO pFileInfo = (PAFP_FILE_INFO)InBuf;
|
|
POPENFORKENTRY pOpenForkEntry;
|
|
DWORD ForkId;
|
|
AFPSTATUS Status = AFPERR_InvalidId;
|
|
|
|
if ((ForkId = pFileInfo->afpfile_id) != 0)
|
|
{
|
|
if ((pOpenForkEntry = AfpForkReferenceById(ForkId)) != NULL)
|
|
{
|
|
AfpForkClose(pOpenForkEntry);
|
|
|
|
AfpForkDereference(pOpenForkEntry);
|
|
|
|
Status = AFP_ERR_NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BOOLEAN Shoot;
|
|
DWORD ForkId = MAXULONG;
|
|
KIRQL OldIrql;
|
|
|
|
Status = AFP_ERR_NONE;
|
|
while (True)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&AfpForksLock, &OldIrql);
|
|
|
|
for (pOpenForkEntry = AfpOpenForksList;
|
|
pOpenForkEntry != NULL;
|
|
pOpenForkEntry = pOpenForkEntry->ofe_Next)
|
|
{
|
|
if (pOpenForkEntry->ofe_ForkId > ForkId)
|
|
continue;
|
|
|
|
ForkId = pOpenForkEntry->ofe_ForkId;
|
|
|
|
Shoot = False;
|
|
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pOpenForkEntry->ofe_Lock);
|
|
|
|
if (!(pOpenForkEntry->ofe_Flags & OPEN_FORK_CLOSING))
|
|
{
|
|
pOpenForkEntry->ofe_RefCount ++;
|
|
Shoot = True;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkEntry->ofe_Lock);
|
|
|
|
if (Shoot)
|
|
{
|
|
RELEASE_SPIN_LOCK(&AfpForksLock, OldIrql);
|
|
|
|
AfpForkClose(pOpenForkEntry);
|
|
|
|
AfpForkDereference(pOpenForkEntry);
|
|
|
|
break;
|
|
}
|
|
}
|
|
if (pOpenForkEntry == NULL)
|
|
{
|
|
RELEASE_SPIN_LOCK(&AfpForksLock, OldIrql);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*** afpForkGetNewForkRefNumAndLinkInSda
|
|
*
|
|
* Assign a new OForkRefNum to a fork that is being opened. The smallest one
|
|
* is always allocated. Make the right entry in the SDA point to the
|
|
* OpenForkEntry.
|
|
*
|
|
* LOCKS: sda_Lock (SPIN)
|
|
*/
|
|
LOCAL BOOLEAN
|
|
afpForkGetNewForkRefNumAndLinkInSda(
|
|
IN PSDA pSda,
|
|
IN POPENFORKENTRY pOpenForkEntry
|
|
)
|
|
{
|
|
POPENFORKSESS pOpenForkSess;
|
|
KIRQL OldIrql;
|
|
USHORT i;
|
|
USHORT OForkRefNum = 1;
|
|
BOOLEAN Found = False;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
|
|
|
|
pOpenForkSess = &pSda->sda_OpenForkSess;
|
|
pOpenForkEntry->ofe_OForkRefNum = 0;
|
|
while (!Found)
|
|
{
|
|
for (i = 0; i < FORK_OPEN_CHUNKS; i++, OForkRefNum++)
|
|
{
|
|
if (pOpenForkSess->ofs_pOpenForkEntry[i] == NULL)
|
|
{
|
|
pOpenForkSess->ofs_pOpenForkEntry[i] = pOpenForkEntry;
|
|
pOpenForkEntry->ofe_OForkRefNum = OForkRefNum;
|
|
Found = True;
|
|
break;
|
|
}
|
|
}
|
|
if (!Found)
|
|
{
|
|
if (pOpenForkSess->ofs_Link != NULL)
|
|
{
|
|
pOpenForkSess = pOpenForkSess->ofs_Link;
|
|
continue;
|
|
}
|
|
if ((pOpenForkSess->ofs_Link = (POPENFORKSESS)AfpAllocZeroedNonPagedMemory(sizeof(OPENFORKSESS))) != NULL)
|
|
{
|
|
pOpenForkSess->ofs_Link->ofs_pOpenForkEntry[0] = pOpenForkEntry;
|
|
pOpenForkEntry->ofe_OForkRefNum = OForkRefNum;
|
|
Found = True;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Found)
|
|
{
|
|
// Reference sda for this fork and up the MaxOForkRefNum, if needed
|
|
pSda->sda_RefCount ++;
|
|
if (OForkRefNum > pSda->sda_MaxOForkRefNum)
|
|
pSda->sda_MaxOForkRefNum = OForkRefNum;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
|
|
return Found;
|
|
}
|
|
|
|
/*** AfpExchangeForkAfpIds
|
|
*
|
|
* When an FpExchangeFiles occurs, if the data or resource fork of either
|
|
* of the 2 files being exchanged is open, we must fix up the AfpId kept
|
|
* in the OpenForkDesc structure. This is because when the final close
|
|
* is done on the fork, the cleanup code must clear the DFE_X_ALREADYOPEN
|
|
* flag in the corresponding DFEntry of the Idindex database.
|
|
*
|
|
* LOCKS: ofd_Lock (SPIN), vds_VolLock (SPIN)
|
|
* LOCK_ORDER: ofd_Lock after vds_VolLock
|
|
* LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive)
|
|
*/
|
|
VOID
|
|
AfpExchangeForkAfpIds(
|
|
IN PVOLDESC pVolDesc,
|
|
IN DWORD AfpId1,
|
|
IN DWORD AfpId2
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
POPENFORKDESC pOpenForkDesc;
|
|
AFPSTATUS Status = AFP_ERR_NONE;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
|
|
|
|
// check the list of open forks in this volume for the IDs specified
|
|
for (pOpenForkDesc = pVolDesc->vds_pOpenForkDesc;
|
|
pOpenForkDesc != NULL;
|
|
pOpenForkDesc = pOpenForkDesc->ofd_Next)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&pOpenForkDesc->ofd_Lock);
|
|
if (pOpenForkDesc->ofd_FileNumber == AfpId1)
|
|
{
|
|
pOpenForkDesc->ofd_FileNumber = AfpId2;
|
|
}
|
|
else if (pOpenForkDesc->ofd_FileNumber == AfpId2)
|
|
{
|
|
pOpenForkDesc->ofd_FileNumber = AfpId1;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&pOpenForkDesc->ofd_Lock);
|
|
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock, OldIrql);
|
|
}
|
|
|