2008-03-09 14:11:42 +00:00
|
|
|
/*
|
2001-10-10 21:49:15 +00:00
|
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
|
|
* PROJECT: ReactOS kernel
|
|
|
|
* FILE: ntoskrnl/cc/pin.c
|
|
|
|
* PURPOSE: Implements cache managers pinning interface
|
2005-01-26 13:58:37 +00:00
|
|
|
*
|
2007-10-19 23:21:45 +00:00
|
|
|
* PROGRAMMERS:
|
2001-10-10 21:49:15 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
|
2004-08-15 16:39:12 +00:00
|
|
|
#include <ntoskrnl.h>
|
2001-10-10 21:49:15 +00:00
|
|
|
#define NDEBUG
|
2008-08-30 16:31:06 +00:00
|
|
|
#include <debug.h>
|
2001-10-10 21:49:15 +00:00
|
|
|
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
|
2003-01-11 15:22:31 +00:00
|
|
|
extern NPAGED_LOOKASIDE_LIST iBcbLookasideList;
|
2001-10-10 21:49:15 +00:00
|
|
|
|
2003-01-11 15:22:31 +00:00
|
|
|
/* FUNCTIONS *****************************************************************/
|
2001-10-10 21:49:15 +00:00
|
|
|
|
2003-07-10 06:27:13 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2008-11-29 20:47:48 +00:00
|
|
|
BOOLEAN NTAPI
|
2002-07-17 21:04:57 +00:00
|
|
|
CcMapData (IN PFILE_OBJECT FileObject,
|
|
|
|
IN PLARGE_INTEGER FileOffset,
|
|
|
|
IN ULONG Length,
|
2006-10-25 18:23:45 +00:00
|
|
|
IN ULONG Flags,
|
2002-07-17 21:04:57 +00:00
|
|
|
OUT PVOID *pBcb,
|
|
|
|
OUT PVOID *pBuffer)
|
2001-10-10 21:49:15 +00:00
|
|
|
{
|
2002-07-17 21:04:57 +00:00
|
|
|
ULONG ReadOffset;
|
|
|
|
BOOLEAN Valid;
|
2002-09-08 10:23:54 +00:00
|
|
|
PBCB Bcb;
|
|
|
|
PCACHE_SEGMENT CacheSeg;
|
2002-07-17 21:04:57 +00:00
|
|
|
NTSTATUS Status;
|
|
|
|
PINTERNAL_BCB iBcb;
|
|
|
|
ULONG ROffset;
|
2005-05-09 01:38:29 +00:00
|
|
|
|
2006-10-25 18:23:45 +00:00
|
|
|
DPRINT("CcMapData(FileObject 0x%p, FileOffset %I64x, Length %d, Flags %d,"
|
2005-07-04 22:11:00 +00:00
|
|
|
" pBcb 0x%p, pBuffer 0x%p)\n", FileObject, FileOffset->QuadPart,
|
2006-10-25 18:23:45 +00:00
|
|
|
Length, Flags, pBcb, pBuffer);
|
2005-05-09 01:38:29 +00:00
|
|
|
|
2003-12-30 18:52:06 +00:00
|
|
|
ReadOffset = (ULONG)FileOffset->QuadPart;
|
2008-01-19 17:24:52 +00:00
|
|
|
|
|
|
|
ASSERT(FileObject);
|
|
|
|
ASSERT(FileObject->SectionObjectPointer);
|
|
|
|
ASSERT(FileObject->SectionObjectPointer->SharedCacheMap);
|
|
|
|
|
2003-06-07 Casper S. Hornstrup <chorns@users.sourceforge.net>
Changes for compiling with w32api
* include/ddk/cctypes.h (PREACTOS_COMMON_FCB_HEADER): Remove.
(FSRTL_COMMON_FCB_HEADER): Add.
* include/ddk/iotypes.h (FILE_OBJECT): Rename field
SectionObjectPointers to SectionObjectPointer.
* ntoskrnl/cc/copy.c, ntoskrnl/cc/misc.c, ntoskrnl/cc/pin.c,
ntoskrnl/cc/view.c, ntoskrnl/io/rawfs.c, ntoskrnl/mm/section.c,
drivers/fs/cdfs/cleanup.c, drivers/fs/cdfs/fcb.c,
drivers/fs/cdfs/fsctl.c, drivers/fs/ntfs/fcb.c, drivers/fs/ntfs/fsctl.c,
drivers/fs/vfat/close.c, drivers/fs/vfat/create.c,
drivers/fs/vfat/finfo.c, drivers/fs/vfat/fcb.c, drivers/fs/vfat/fsctl.c:
Use new FILE_OBJECT structure.
* drivers/fs/cdfs/cdfs.h, drivers/fs/ntfs/ntfs.h, drivers/fs/vfat/vfat.h:
Use new FSRTL_COMMON_FCB_HEADER structure.
* drivers/net/afd/include/afd.h (FSRTL_COMMON_FCB_HEADER): Remove.
* include/ddk/ketypes.h (KQUEUE): Match w32api structure.
* ntoskrnl/ke/queue.c, ntoskrnl/ke/wait.c: Use new structure.
* ntoskrnl/ke/spinlock.c (KeAcquireSpinLockAtDpcLevel,
KeReleaseSpinLockFromDpcLevel): Undefine before declaring.
svn path=/trunk/; revision=4865
2003-06-07 11:34:36 +00:00
|
|
|
Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
|
2004-10-22 20:19:58 +00:00
|
|
|
ASSERT(Bcb);
|
2002-12-15 Casper S. Hornstrup <chorns@users.sourceforge.net>
* drivers/fs/vfat/cleanup.c (VfatCleanupFile): Only uninitialize caching
when initialized.
* drivers/fs/vfat/fcb.c (vfatReleaseFCB): Ditto.
* lib/kernel32/mem/section.c (CreateFileMappingW): Pass NULL as
MaximumSize to NtCreateSection if dwMaximumSizeHigh and dwMaximumSizeLow
are both 0.
* ntoskrnl/cc/pin.c (CcMapData): Assert if Bcb is NULL.
* ntoskrnl/cc/view.c (CcRosReleaseCacheSegment, CcRosLookupCacheSegment,
CcRosMarkDirtyCacheSegment, CcRosUnmapCacheSegment,
CcRosCreateCacheSegment, CcRosGetCacheSegmentChain,
CcRosGetCacheSegment, CcRosRequestCacheSegment, CcFlushCache,
CcRosDeleteFileCache, CcRosReferenceCache, CcRosDereferenceCache,
CcRosReleaseFileCache, CcGetFileObjectFromSectionPtrs): Ditto.
* ntoskrnl/mm/section.c (MiReadPage): Assert if Fcb->Bcb is NULL.
(MmCreateDataFileSection): Make sure caching is initialized for the file
stream.
svn path=/trunk/; revision=3879
2002-12-15 17:01:52 +00:00
|
|
|
|
2005-07-04 22:11:00 +00:00
|
|
|
DPRINT("AllocationSize %I64x, FileSize %I64x\n",
|
|
|
|
Bcb->AllocationSize.QuadPart,
|
|
|
|
Bcb->FileSize.QuadPart);
|
2005-05-09 01:38:29 +00:00
|
|
|
|
2002-07-17 21:04:57 +00:00
|
|
|
if (ReadOffset % Bcb->CacheSegmentSize + Length > Bcb->CacheSegmentSize)
|
|
|
|
{
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
ROffset = ROUND_DOWN (ReadOffset, Bcb->CacheSegmentSize);
|
|
|
|
Status = CcRosRequestCacheSegment(Bcb,
|
|
|
|
ROffset,
|
|
|
|
pBuffer,
|
|
|
|
&Valid,
|
|
|
|
&CacheSeg);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
if (!Valid)
|
|
|
|
{
|
2006-10-25 18:23:45 +00:00
|
|
|
if (!(Flags & MAP_WAIT))
|
2002-07-17 21:04:57 +00:00
|
|
|
{
|
2001-12-29 14:32:22 +00:00
|
|
|
CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
|
2002-07-17 21:04:57 +00:00
|
|
|
return(FALSE);
|
|
|
|
}
|
2001-10-10 21:49:15 +00:00
|
|
|
if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
|
2002-07-17 21:04:57 +00:00
|
|
|
{
|
2002-10-02 19:23:42 +00:00
|
|
|
CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
|
2002-07-17 21:04:57 +00:00
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
}
|
2005-07-05 00:57:47 +00:00
|
|
|
|
|
|
|
*pBuffer = (PVOID)((ULONG_PTR)(*pBuffer) + (ReadOffset % Bcb->CacheSegmentSize));
|
2003-01-11 15:22:31 +00:00
|
|
|
iBcb = ExAllocateFromNPagedLookasideList(&iBcbLookasideList);
|
2002-07-17 21:04:57 +00:00
|
|
|
if (iBcb == NULL)
|
|
|
|
{
|
|
|
|
CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2003-01-11 15:22:31 +00:00
|
|
|
memset(iBcb, 0, sizeof(INTERNAL_BCB));
|
2004-08-25 15:08:29 +00:00
|
|
|
iBcb->PFCB.NodeTypeCode = 0xDE45; /* Undocumented (CAPTIVE_PUBLIC_BCB_NODETYPECODE) */
|
|
|
|
iBcb->PFCB.NodeByteSize = sizeof(PUBLIC_BCB);
|
|
|
|
iBcb->PFCB.MappedLength = Length;
|
|
|
|
iBcb->PFCB.MappedFileOffset = *FileOffset;
|
2002-07-17 21:04:57 +00:00
|
|
|
iBcb->CacheSegment = CacheSeg;
|
2002-08-14 20:58:39 +00:00
|
|
|
iBcb->Dirty = FALSE;
|
2004-08-25 15:08:29 +00:00
|
|
|
iBcb->RefCount = 1;
|
2002-07-17 21:04:57 +00:00
|
|
|
*pBcb = (PVOID)iBcb;
|
|
|
|
return(TRUE);
|
2001-10-10 21:49:15 +00:00
|
|
|
}
|
|
|
|
|
2003-07-10 06:27:13 +00:00
|
|
|
/*
|
2004-06-21 04:11:44 +00:00
|
|
|
* @unimplemented
|
2003-07-10 06:27:13 +00:00
|
|
|
*/
|
2004-06-21 04:11:44 +00:00
|
|
|
BOOLEAN
|
2008-11-29 20:47:48 +00:00
|
|
|
NTAPI
|
2004-06-21 04:11:44 +00:00
|
|
|
CcPinMappedData (
|
|
|
|
IN PFILE_OBJECT FileObject,
|
|
|
|
IN PLARGE_INTEGER FileOffset,
|
|
|
|
IN ULONG Length,
|
2005-01-01 01:51:05 +00:00
|
|
|
IN ULONG Flags,
|
2004-06-21 04:11:44 +00:00
|
|
|
OUT PVOID * Bcb
|
|
|
|
)
|
2001-10-10 21:49:15 +00:00
|
|
|
{
|
2004-08-25 15:08:29 +00:00
|
|
|
/* no-op for current implementation. */
|
|
|
|
return TRUE;
|
2004-06-21 04:11:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
BOOLEAN
|
2008-11-29 20:47:48 +00:00
|
|
|
NTAPI
|
2004-06-21 04:11:44 +00:00
|
|
|
CcPinRead (
|
|
|
|
IN PFILE_OBJECT FileObject,
|
|
|
|
IN PLARGE_INTEGER FileOffset,
|
|
|
|
IN ULONG Length,
|
2005-01-01 01:51:05 +00:00
|
|
|
IN ULONG Flags,
|
2004-06-21 04:11:44 +00:00
|
|
|
OUT PVOID * Bcb,
|
|
|
|
OUT PVOID * Buffer
|
|
|
|
)
|
|
|
|
{
|
2005-01-01 01:51:05 +00:00
|
|
|
if (CcMapData(FileObject, FileOffset, Length, Flags, Bcb, Buffer))
|
2004-08-25 15:08:29 +00:00
|
|
|
{
|
2005-01-01 01:51:05 +00:00
|
|
|
if (CcPinMappedData(FileObject, FileOffset, Length, Flags, Bcb))
|
2004-08-25 15:08:29 +00:00
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
CcUnpinData(Bcb);
|
|
|
|
}
|
|
|
|
return FALSE;
|
2004-06-21 04:11:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
BOOLEAN
|
2008-11-29 20:47:48 +00:00
|
|
|
NTAPI
|
2004-06-21 04:11:44 +00:00
|
|
|
CcPreparePinWrite (
|
|
|
|
IN PFILE_OBJECT FileObject,
|
|
|
|
IN PLARGE_INTEGER FileOffset,
|
|
|
|
IN ULONG Length,
|
|
|
|
IN BOOLEAN Zero,
|
2005-01-01 01:51:05 +00:00
|
|
|
IN ULONG Flags,
|
2004-06-21 04:11:44 +00:00
|
|
|
OUT PVOID * Bcb,
|
|
|
|
OUT PVOID * Buffer
|
|
|
|
)
|
|
|
|
{
|
2004-08-25 15:08:29 +00:00
|
|
|
/*
|
|
|
|
* FIXME: This is function is similar to CcPinRead, but doesn't
|
|
|
|
* read the data if they're not present. Instead it should just
|
|
|
|
* prepare the cache segments and zero them out if Zero == TRUE.
|
|
|
|
*
|
|
|
|
* For now calling CcPinRead is better than returning error or
|
|
|
|
* just having UNIMPLEMENTED here.
|
|
|
|
*/
|
2005-01-01 01:51:05 +00:00
|
|
|
return CcPinRead(FileObject, FileOffset, Length, Flags, Bcb, Buffer);
|
2001-10-10 21:49:15 +00:00
|
|
|
}
|
|
|
|
|
2003-07-10 06:27:13 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2008-11-29 20:47:48 +00:00
|
|
|
VOID NTAPI
|
2002-07-17 21:04:57 +00:00
|
|
|
CcSetDirtyPinnedData (IN PVOID Bcb,
|
|
|
|
IN PLARGE_INTEGER Lsn)
|
2001-10-10 21:49:15 +00:00
|
|
|
{
|
|
|
|
PINTERNAL_BCB iBcb = Bcb;
|
2002-08-14 20:58:39 +00:00
|
|
|
iBcb->Dirty = TRUE;
|
2001-10-10 21:49:15 +00:00
|
|
|
}
|
|
|
|
|
2004-06-21 04:11:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2008-11-29 20:47:48 +00:00
|
|
|
VOID NTAPI
|
2004-06-21 04:11:44 +00:00
|
|
|
CcUnpinData (IN PVOID Bcb)
|
|
|
|
{
|
|
|
|
PINTERNAL_BCB iBcb = Bcb;
|
2005-05-09 01:38:29 +00:00
|
|
|
CcRosReleaseCacheSegment(iBcb->CacheSegment->Bcb, iBcb->CacheSegment, TRUE,
|
2004-08-25 15:08:29 +00:00
|
|
|
iBcb->Dirty, FALSE);
|
|
|
|
if (--iBcb->RefCount == 0)
|
|
|
|
{
|
|
|
|
ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
|
|
|
|
}
|
2004-06-21 04:11:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
VOID
|
2008-11-29 20:47:48 +00:00
|
|
|
NTAPI
|
2004-06-21 04:11:44 +00:00
|
|
|
CcUnpinDataForThread (
|
|
|
|
IN PVOID Bcb,
|
|
|
|
IN ERESOURCE_THREAD ResourceThreadId
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UNIMPLEMENTED;
|
|
|
|
}
|
2004-08-25 15:08:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
VOID
|
2008-11-29 20:47:48 +00:00
|
|
|
NTAPI
|
2004-08-25 15:08:29 +00:00
|
|
|
CcRepinBcb (
|
|
|
|
IN PVOID Bcb
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PINTERNAL_BCB iBcb = Bcb;
|
|
|
|
iBcb->RefCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @unimplemented
|
|
|
|
*/
|
|
|
|
VOID
|
2008-11-29 20:47:48 +00:00
|
|
|
NTAPI
|
2004-08-25 15:08:29 +00:00
|
|
|
CcUnpinRepinnedBcb (
|
|
|
|
IN PVOID Bcb,
|
|
|
|
IN BOOLEAN WriteThrough,
|
|
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
|
|
)
|
|
|
|
{
|
|
|
|
PINTERNAL_BCB iBcb = Bcb;
|
|
|
|
|
|
|
|
if (--iBcb->RefCount == 0)
|
|
|
|
{
|
|
|
|
IoStatus->Information = 0;
|
|
|
|
if (WriteThrough)
|
|
|
|
{
|
- Okay so...listen up. First off: When you acquire a lock such as a fast mutex, you should never acquire it recursively.
For example, when you handle a page fault in a section, then page fault while handling that page fault (which is perfectly okay),
you shouldn't be trying to re-acquire the address space lock that you're already holding. After this fix, this scenario works
and countless others. Apps like QTInfo now work and load, and PictureViewer doesn't BSOD the system anymore. I've fixed this by changing
the lock to a pushlock. It not only increases speed inside the memory manager significantly (such as during page fault handling), but
does allow recursive acquisition without any problems.
- Now if that wasn't bad enough, here's a couple more tips. Fast Mutexes actually require APC_LEVEL to be effective. If you're going
to be using a Fast Mutex and calling it with the "Unsafe" version, then don't expect anything to work. Also, using functions like
"CcTryToAcquireBrokenMutex" where correct code is duplicated then hacked to work isn't a big help either. And that's not all. Fast Mutex
disables kernel APCs by setting the KernelApcDisable flag on, and it's expected that the count inside the fast mutex will match the count
inside the thread. In other words, LOCK ACQUISITION AND RELEASE MUST BE ORDERED. You can't acquire LOCK A and B, and then release lock A
and B, because that leads to deadlocks and other issues. So of course, the Cache Manager acquired a view lock, then acquired a segment lock,
then released the view lock, then released the segment lock, then re-acquired the view lock. Uh, no, that won't work. You know what else
doesn't work so well? Disabling APCs about 6-9 times to acquire a single lock, and using spinlocks in the same code path as well. Just how
paranoid are you about thread safety, but still manage to get it wrong? Okay, so we've got recursion, out-of-order lock acquision and
release, made-up "broken" acquire functions, and using a lock that depends on APC_LEVEL at PASSIVE_LEVEL. The best part is when Cc builds
an array of cache segments, and locks each of them... then during release, the list gets parsed head-first, so the first acquired locks
get released first. So locks a, b, c, d get acquired, then a, b, c, d get released. Great! Sounds about right for ReactOS's Cache Manager
design. I've changed the view lock to a guarded mutex -- which actually properly disables APCs and works at PASSIVE_LEVEL, and changed the
segment locks to be push locks. First it'll be 10 times faster then acquiring a bazillion fast mutexes, especially since APCs have already
been disabled at this point, and it also allows you to do most of the stupid things the Cache Manager does. Out-of-order release is still
not going to work well, so eventually on a multi-processor machine the code will completely die -- but at least it'll work on UP for now.
In the end, this makes things like the Inkscape installer and Quicktime Installer to work, and probably countless other things that generated
ASSERTS in the fast mutex code.
-- Alex Ionescu
svn path=/trunk/; revision=30401
2007-11-12 19:00:26 +00:00
|
|
|
ExAcquirePushLockExclusive(&iBcb->CacheSegment->Lock);
|
2004-08-25 15:08:29 +00:00
|
|
|
if (iBcb->CacheSegment->Dirty)
|
|
|
|
{
|
|
|
|
IoStatus->Status = CcRosFlushCacheSegment(iBcb->CacheSegment);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
}
|
- Okay so...listen up. First off: When you acquire a lock such as a fast mutex, you should never acquire it recursively.
For example, when you handle a page fault in a section, then page fault while handling that page fault (which is perfectly okay),
you shouldn't be trying to re-acquire the address space lock that you're already holding. After this fix, this scenario works
and countless others. Apps like QTInfo now work and load, and PictureViewer doesn't BSOD the system anymore. I've fixed this by changing
the lock to a pushlock. It not only increases speed inside the memory manager significantly (such as during page fault handling), but
does allow recursive acquisition without any problems.
- Now if that wasn't bad enough, here's a couple more tips. Fast Mutexes actually require APC_LEVEL to be effective. If you're going
to be using a Fast Mutex and calling it with the "Unsafe" version, then don't expect anything to work. Also, using functions like
"CcTryToAcquireBrokenMutex" where correct code is duplicated then hacked to work isn't a big help either. And that's not all. Fast Mutex
disables kernel APCs by setting the KernelApcDisable flag on, and it's expected that the count inside the fast mutex will match the count
inside the thread. In other words, LOCK ACQUISITION AND RELEASE MUST BE ORDERED. You can't acquire LOCK A and B, and then release lock A
and B, because that leads to deadlocks and other issues. So of course, the Cache Manager acquired a view lock, then acquired a segment lock,
then released the view lock, then released the segment lock, then re-acquired the view lock. Uh, no, that won't work. You know what else
doesn't work so well? Disabling APCs about 6-9 times to acquire a single lock, and using spinlocks in the same code path as well. Just how
paranoid are you about thread safety, but still manage to get it wrong? Okay, so we've got recursion, out-of-order lock acquision and
release, made-up "broken" acquire functions, and using a lock that depends on APC_LEVEL at PASSIVE_LEVEL. The best part is when Cc builds
an array of cache segments, and locks each of them... then during release, the list gets parsed head-first, so the first acquired locks
get released first. So locks a, b, c, d get acquired, then a, b, c, d get released. Great! Sounds about right for ReactOS's Cache Manager
design. I've changed the view lock to a guarded mutex -- which actually properly disables APCs and works at PASSIVE_LEVEL, and changed the
segment locks to be push locks. First it'll be 10 times faster then acquiring a bazillion fast mutexes, especially since APCs have already
been disabled at this point, and it also allows you to do most of the stupid things the Cache Manager does. Out-of-order release is still
not going to work well, so eventually on a multi-processor machine the code will completely die -- but at least it'll work on UP for now.
In the end, this makes things like the Inkscape installer and Quicktime Installer to work, and probably countless other things that generated
ASSERTS in the fast mutex code.
-- Alex Ionescu
svn path=/trunk/; revision=30401
2007-11-12 19:00:26 +00:00
|
|
|
ExReleasePushLockExclusive(&iBcb->CacheSegment->Lock);
|
2004-08-25 15:08:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IoStatus->Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ExFreeToNPagedLookasideList(&iBcbLookasideList, iBcb);
|
|
|
|
}
|
|
|
|
}
|