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