windows-nt/Source/XPSP1/NT/net/sfm/afp/server/forks.c

1463 lines
39 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*
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);
}