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