reactos/reactos/drivers/filesystems/udfs/close.cpp
Pierre Schweitzer bcd5f02d0f [UDFS]
HIde a bug in UDFQueueDelayedClose() by removing the boolean storing the fact
that VCB resource was (or not) acquired.
If you debug it, you see that in finally block, value is set to false on exit
without any reason: it is always acquired!
Given it's always acquired, just drop the boolean, but this doesn't explain the
root cause of this bug....

Here it helps avoiding deadlocks on directory listing on a DVD formatted with UDF

CORE-4375

svn path=/trunk/; revision=74780
2017-06-03 18:12:25 +00:00

1190 lines
41 KiB
C++

////////////////////////////////////////////////////////////////////
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
// All rights reserved
// This file was released under the GPLv2 on June 2015.
////////////////////////////////////////////////////////////////////
/*************************************************************************
*
* File: Close.cpp
*
* Module: UDF File System Driver (Kernel mode execution only)
*
* Description:
* Contains code to handle the "Close" dispatch entry point.
*
*************************************************************************/
#include "udffs.h"
// define the file specific bug-check id
#define UDF_BUG_CHECK_ID UDF_FILE_CLOSE
typedef BOOLEAN (*PCHECK_TREE_ITEM) (IN PUDF_FILE_INFO FileInfo);
#define TREE_ITEM_LIST_GRAN 32
NTSTATUS
UDFBuildTreeItemsList(
IN PVCB Vcb,
IN PUDF_FILE_INFO FileInfo,
IN PCHECK_TREE_ITEM CheckItemProc,
IN PUDF_DATALOC_INFO** PassedList,
IN PULONG PassedListSize,
IN PUDF_DATALOC_INFO** FoundList,
IN PULONG FoundListSize);
// callbacks, can't be __fastcall
BOOLEAN
UDFIsInDelayedCloseQueue(
PUDF_FILE_INFO FileInfo);
BOOLEAN
UDFIsLastClose(
PUDF_FILE_INFO FileInfo);
/*************************************************************************
*
* Function: UDFClose()
*
* Description:
* The I/O Manager will invoke this routine to handle a close
* request
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
* to be deferred to a worker thread context)
*
* Return Value: STATUS_SUCCESS
*
*************************************************************************/
NTSTATUS
NTAPI
UDFClose(
PDEVICE_OBJECT DeviceObject, // the logical volume device object
PIRP Irp // I/O Request Packet
)
{
NTSTATUS RC = STATUS_SUCCESS;
PtrUDFIrpContext PtrIrpContext = NULL;
BOOLEAN AreWeTopLevel = FALSE;
AdPrint(("UDFClose: \n"));
FsRtlEnterFileSystem();
ASSERT(DeviceObject);
ASSERT(Irp);
// If we were called with our file system device object instead of a
// volume device object, just complete this request with STATUS_SUCCESS
if (UDFIsFSDevObj(DeviceObject)) {
// this is a close of the FSD itself
Irp->IoStatus.Status = RC;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
FsRtlExitFileSystem();
return(RC);
}
// set the top level context
AreWeTopLevel = UDFIsIrpTopLevel(Irp);
_SEH2_TRY {
// get an IRP context structure and issue the request
PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
ASSERT(PtrIrpContext);
RC = UDFCommonClose(PtrIrpContext, Irp);
} _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
RC = UDFExceptionHandler(PtrIrpContext, Irp);
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
} _SEH2_END;
if (AreWeTopLevel) {
IoSetTopLevelIrp(NULL);
}
FsRtlExitFileSystem();
return(RC);
}
/*************************************************************************
*
* Function: UDFCommonClose()
*
* Description:
* The actual work is performed here. This routine may be invoked in one'
* of the two possible contexts:
* (a) in the context of a system worker thread
* (b) in the context of the original caller
*
* Expected Interrupt Level (for execution) :
*
* IRQL_PASSIVE_LEVEL
*
* Return Value: must be STATUS_SUCCESS
*
*************************************************************************/
NTSTATUS
UDFCommonClose(
PtrUDFIrpContext PtrIrpContext,
PIRP Irp
)
{
NTSTATUS RC = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpSp = NULL;
PFILE_OBJECT FileObject = NULL;
PtrUDFFCB Fcb = NULL;
PtrUDFCCB Ccb = NULL;
PVCB Vcb = NULL;
// PERESOURCE PtrResourceAcquired = NULL;
BOOLEAN AcquiredVcb = FALSE;
BOOLEAN AcquiredGD = FALSE;
PUDF_FILE_INFO fi;
ULONG i = 0;
// ULONG clean_stat = 0;
// BOOLEAN CompleteIrp = TRUE;
BOOLEAN PostRequest = FALSE;
#ifdef UDF_DBG
UNICODE_STRING CurName;
PDIR_INDEX_HDR DirNdx;
#endif
AdPrint(("UDFCommonClose: \n"));
_SEH2_TRY {
if (Irp) {
// If this is the first (IOManager) request
// First, get a pointer to the current I/O stack location
IrpSp = IoGetCurrentIrpStackLocation(Irp);
ASSERT(IrpSp);
FileObject = IrpSp->FileObject;
ASSERT(FileObject);
// Get the FCB and CCB pointers
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
ASSERT(Ccb);
if(Ccb->CCBFlags & UDF_CCB_READ_ONLY) {
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_READ_ONLY;
}
Fcb = Ccb->Fcb;
} else {
// If this is a queued call (for our dispatch)
// Get saved Fcb address
Fcb = PtrIrpContext->Fcb;
i = PtrIrpContext->TreeLength;
}
ASSERT(Fcb);
Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
ASSERT(Vcb);
ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB);
// Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
// Steps we shall take at this point are:
// (a) Acquire the VCB shared
// (b) Acquire the FCB's CCB list exclusively
// (c) Delete the CCB structure (free memory)
// (d) If this is the last close, release the FCB structure
// (unless we keep these around for "delayed close" functionality.
// Note that it is often the case that the close dispatch entry point is invoked
// in the most inconvenient of situations (when it is not possible, for example,
// to safely acquire certain required resources without deadlocking or waiting).
// Therefore, be extremely careful in implementing this close dispatch entry point.
// Also note that we do not have the option of returning a failure code from the
// close dispatch entry point; the system expects that the close will always succeed.
UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
AcquiredVcb = TRUE;
// Is this is the first (IOManager) request ?
if (Irp) {
PtrIrpContext->TreeLength =
i = Ccb->TreeLength;
// remember the number of incomplete Close requests
InterlockedIncrement((PLONG)&(Fcb->CcbCount));
// we can release CCB in any case
UDFCleanUpCCB(Ccb);
FileObject->FsContext2 = NULL;
#ifdef DBG
/* } else {
ASSERT(Fcb->NTRequiredFCB);
if(Fcb->NTRequiredFCB) {
ASSERT(Fcb->NTRequiredFCB->FileObject);
if(Fcb->NTRequiredFCB->FileObject) {
ASSERT(!Fcb->NTRequiredFCB->FileObject->FsContext2);
}
}*/
#endif //DBG
}
#ifdef UDF_DELAYED_CLOSE
// check if this is the last Close (no more Handles)
// and try to Delay it....
if((Fcb->FCBFlags & UDF_FCB_DELAY_CLOSE) &&
(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) &&
!(Vcb->VCBFlags & UDF_VCB_FLAGS_NO_DELAYED_CLOSE) &&
!(Fcb->OpenHandleCount)) {
UDFReleaseResource(&(Vcb->VCBResource));
AcquiredVcb = FALSE;
if((RC = UDFQueueDelayedClose(PtrIrpContext,Fcb)) == STATUS_SUCCESS)
try_return(RC = STATUS_SUCCESS);
// do standard Close if we can't Delay this opeartion
AdPrint((" Cant queue Close Irp, status=%x\n", RC));
}
#endif //UDF_DELAYED_CLOSE
if(Irp) {
// We should post actual procesing if this is a recursive call
if((PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_TOP_LEVEL) ||
(Fcb->NTRequiredFCB->AcqFlushCount)) {
AdPrint((" post NOT_TOP_LEVEL Irp\n"));
PostRequest = TRUE;
try_return(RC = STATUS_SUCCESS);
}
}
// Close request is near completion, Vcb is acquired.
// Now we can safely decrease CcbCount, because no Rename
// operation can run until Vcb release.
InterlockedDecrement((PLONG)&(Fcb->CcbCount));
UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_READ_ONLY)
UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCountRO));
if(!i || (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB)) {
AdPrint(("UDF: Closing volume\n"));
AdPrint(("UDF: ReferenceCount: %x\n",Fcb->ReferenceCount));
if (Vcb->VCBOpenCount > UDF_RESIDUAL_REFERENCE) {
ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB);
UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
ASSERT(Fcb->NTRequiredFCB);
UDFInterlockedDecrement((PLONG)&(Fcb->NTRequiredFCB->CommonRefCount));
try_return(RC = STATUS_SUCCESS);
}
UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount));
if(AcquiredVcb) {
UDFReleaseResource(&(Vcb->VCBResource));
AcquiredVcb = FALSE;
} else {
BrutePoint();
}
// Acquire GlobalDataResource
UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE);
AcquiredGD = TRUE;
// // Acquire Vcb
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
AcquiredVcb = TRUE;
UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB);
UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
ASSERT(Fcb->NTRequiredFCB);
UDFInterlockedDecrement((PLONG)&(Fcb->NTRequiredFCB->CommonRefCount));
//AdPrint(("UDF: Closing volume, reset driver (e.g. stop BGF)\n"));
//UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, FALSE);
AdPrint(("UDF: Closing volume, reset write status\n"));
RC = UDFPhSendIOCTL(IOCTL_CDRW_RESET_WRITE_STATUS, Vcb->TargetDeviceObject,
NULL, 0, NULL, 0, TRUE, NULL);
if((Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED) ||
((!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) && (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE))) {
// Try to KILL dismounted volume....
// w2k requires this, NT4 - recomends
AcquiredVcb = UDFCheckForDismount(PtrIrpContext, Vcb, TRUE);
}
try_return(RC = STATUS_SUCCESS);
}
fi = Fcb->FileInfo;
#ifdef UDF_DBG
if(!fi) {
BrutePoint();
}
DirNdx = UDFGetDirIndexByFileInfo(fi);
if(DirNdx) {
CurName.Buffer = UDFDirIndex(DirNdx,fi->Index)->FName.Buffer;
if(CurName.Buffer) {
AdPrint(("Closing file: %ws %8.8x\n", CurName.Buffer, FileObject));
} else {
AdPrint(("Closing file: ??? \n"));
}
}
AdPrint(("UDF: ReferenceCount: %x\n",Fcb->ReferenceCount));
#endif // UDF_DBG
// try to clean up as long chain as it is possible
UDFCleanUpFcbChain(Vcb, fi, i, TRUE);
try_exit: NOTHING;
} _SEH2_FINALLY {
if(AcquiredVcb) {
UDFReleaseResource(&(Vcb->VCBResource));
}
if(AcquiredGD) {
UDFReleaseResource(&(UDFGlobalData.GlobalDataResource));
}
// Post IRP if required
if (PostRequest) {
// Perform the post operation & complete the IRP
// if this is first call of UDFCommonClose
// and will return STATUS_SUCCESS back to us
PtrIrpContext->Irp = NULL;
PtrIrpContext->Fcb = Fcb;
UDFPostRequest(PtrIrpContext, NULL);
}
if (!_SEH2_AbnormalTermination()) {
// If this is not async close complete the IRP
if (Irp) {
/* if( FileObject ) {
if(clean_stat & UDF_CLOSE_NTREQFCB_DELETED) {
// ASSERT(!FileObject->FsContext2);
FileObject->FsContext = NULL;
#ifdef DBG
} else {
UDFNTRequiredFCB* NtReqFcb = ((UDFNTRequiredFCB*)(FileObject->FsContext));
if(NtReqFcb->FileObject == FileObject) {
NtReqFcb->FileObject = NULL;
}
#endif //DBG
}
}*/
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
// Free up the Irp Context
if(!PostRequest)
UDFReleaseIrpContext(PtrIrpContext);
}
} _SEH2_END; // end of "__finally" processing
return STATUS_SUCCESS ;
} // end UDFCommonClose()
/*
This routine walks through the tree to RootDir & kills all unreferenced
structures....
imho, Useful feature
*/
ULONG
UDFCleanUpFcbChain(
IN PVCB Vcb,
IN PUDF_FILE_INFO fi,
IN ULONG TreeLength,
IN BOOLEAN VcbAcquired
)
{
PtrUDFFCB Fcb = NULL;
PtrUDFFCB ParentFcb = NULL;
PUDF_FILE_INFO ParentFI;
UDFNTRequiredFCB* NtReqFcb;
ULONG CleanCode;
LONG RefCount, ComRefCount;
BOOLEAN Delete = FALSE;
ULONG ret_val = 0;
ValidateFileInfo(fi);
AdPrint(("UDFCleanUpFcbChain\n"));
ASSERT(TreeLength);
// we can't process Tree until we can acquire Vcb
if(!VcbAcquired)
UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE);
// cleanup parent chain (if any & unused)
while(fi) {
// acquire parent
if((ParentFI = fi->ParentFile)) {
ASSERT(fi->Fcb);
ParentFcb = fi->Fcb->ParentFcb;
ASSERT(ParentFcb);
ASSERT(ParentFcb->NTRequiredFCB);
UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB);
UDFAcquireResourceExclusive(&(ParentFcb->NTRequiredFCB->MainResource),TRUE);
} else {
// we get to RootDir, it has no parent
if(!VcbAcquired)
UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE);
}
Fcb = fi->Fcb;
ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB);
NtReqFcb = Fcb->NTRequiredFCB;
ASSERT(NtReqFcb->CommonFCBHeader.NodeTypeCode == UDF_NODE_TYPE_NT_REQ_FCB);
// acquire current file/dir
// we must assure that no more threads try to re-use this object
#ifdef UDF_DBG
_SEH2_TRY {
#endif // UDF_DBG
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
UDFAcquireResourceExclusive(&(NtReqFcb->MainResource),TRUE);
#ifdef UDF_DBG
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
BrutePoint();
if(ParentFI) {
UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB);
UDFReleaseResource(&(ParentFcb->NTRequiredFCB->MainResource));
} else {
if(!VcbAcquired)
UDFReleaseResource(&(Vcb->VCBResource));
}
break;
} _SEH2_END;
#endif // UDF_DBG
ASSERT_REF((Fcb->ReferenceCount > fi->RefCount) || !TreeLength);
// If we haven't pass through all files opened
// in UDFCommonCreate before target file (TreeLength specfies
// the number of such files) dereference them.
// Otherwise we'll just check if the file has no references.
#ifdef UDF_DBG
if(Fcb) {
if(TreeLength) {
ASSERT(Fcb->ReferenceCount);
ASSERT(NtReqFcb->CommonRefCount);
RefCount = UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
ComRefCount = UDFInterlockedDecrement((PLONG)&(NtReqFcb->CommonRefCount));
}
} else {
BrutePoint();
}
if(TreeLength)
TreeLength--;
ASSERT(Fcb->OpenHandleCount <= Fcb->ReferenceCount);
#else
if(TreeLength) {
RefCount = UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
ComRefCount = UDFInterlockedDecrement((PLONG)&(NtReqFcb->CommonRefCount));
TreeLength--;
}
#endif
/* if(Fcb && Fcb->FCBName && Fcb->FCBName->ObjectName.Buffer) {
AdPrint((" %ws (%x)\n",
Fcb->FCBName->ObjectName.Buffer,Fcb->ReferenceCount));
} else if (Fcb) {
AdPrint((" ??? (%x)\n",Fcb->ReferenceCount));
} else {
AdPrint((" ??? (??)\n"));
}*/
// ...and delete if it has gone
if(!RefCount && !Fcb->OpenHandleCount) {
// no more references... current file/dir MUST DIE!!!
BOOLEAN AutoInherited = UDFIsAStreamDir(fi) || UDFIsAStream(fi);
if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) {
// do nothing
} else
#ifndef UDF_READ_ONLY_BUILD
if(Delete) {
/* if(!(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {
// set file size to zero (for UdfInfo package)
// we should not do this for directories
UDFResizeFile__(Vcb, fi, 0);
}*/
UDFReferenceFile__(fi);
ASSERT(Fcb->ReferenceCount < fi->RefCount);
UDFFlushFile__(Vcb, fi);
UDFUnlinkFile__(Vcb, fi, TRUE);
UDFCloseFile__(Vcb, fi);
ASSERT(Fcb->ReferenceCount == fi->RefCount);
Fcb->FCBFlags |= UDF_FCB_DELETED;
Delete = FALSE;
} else
#endif //UDF_READ_ONLY_BUILD
if(!(Fcb->FCBFlags & UDF_FCB_DELETED)) {
UDFFlushFile__(Vcb, fi);
} else {
// BrutePoint();
}
#ifndef UDF_READ_ONLY_BUILD
// check if we should try to delete Parent for the next time
if(Fcb->FCBFlags & UDF_FCB_DELETE_PARENT)
Delete = TRUE;
#endif //UDF_READ_ONLY_BUILD
// remove references to OS-specific structures
// to let UDF_INFO release FI & Co
fi->Fcb = NULL;
if(!ComRefCount) {
// CommonFcb is also completly dereferenced
// Kill it!
fi->Dloc->CommonFcb = NULL;
}
if((CleanCode = UDFCleanUpFile__(Vcb, fi))) {
// Check, if we can uninitialize & deallocate CommonFcb part
// kill some cross links
Fcb->FileInfo = NULL;
// release allocated resources
if(CleanCode & UDF_FREE_DLOC) {
// Obviously, it is a good time & place to release
// CommonFcb structure
// NtReqFcb->NtReqFCBFlags &= ~UDF_NTREQ_FCB_VALID;
// Unitialize byte-range locks support structure
FsRtlUninitializeFileLock(&(NtReqFcb->FileLock));
// Remove resources
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
UDFReleaseResource(&(NtReqFcb->MainResource));
if(NtReqFcb->CommonFCBHeader.Resource) {
UDFDeleteResource(&(NtReqFcb->MainResource));
UDFDeleteResource(&(NtReqFcb->PagingIoResource));
}
NtReqFcb->CommonFCBHeader.Resource =
NtReqFcb->CommonFCBHeader.PagingIoResource = NULL;
UDFDeassignAcl(NtReqFcb, AutoInherited);
KdPrint(("UDFReleaseNtReqFCB: %x\n", NtReqFcb));
#ifdef DBG
// NtReqFcb->FileObject->FsContext2 = NULL;
// ASSERT(NtReqFcb->FileObject);
/* if(NtReqFcb->FileObject) {
ASSERT(!NtReqFcb->FileObject->FsContext2);
NtReqFcb->FileObject->FsContext = NULL;
NtReqFcb->FileObject->SectionObjectPointer = NULL;
}*/
#endif //DBG
MyFreePool__(NtReqFcb);
ret_val |= UDF_CLOSE_NTREQFCB_DELETED;
} else {
// we usually get here when the file has some opened links
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
UDFReleaseResource(&(NtReqFcb->MainResource));
}
// remove some references & free Fcb structure
Fcb->NTRequiredFCB = NULL;
Fcb->ParentFcb = NULL;
UDFCleanUpFCB(Fcb);
MyFreePool__(fi);
ret_val |= UDF_CLOSE_FCB_DELETED;
// get pointer to parent FCB
fi = ParentFI;
// free old parent's resource...
if(fi) {
UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB);
UDFReleaseResource(&(ParentFcb->NTRequiredFCB->MainResource));
} else {
if(!VcbAcquired)
UDFReleaseResource(&(Vcb->VCBResource));
}
} else {
// Stop cleaning up
// Restore pointers
fi->Fcb = Fcb;
fi->Dloc->CommonFcb = NtReqFcb;
// free all acquired resources
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
UDFReleaseResource(&(NtReqFcb->MainResource));
fi = ParentFI;
if(fi) {
UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB);
UDFReleaseResource(&(ParentFcb->NTRequiredFCB->MainResource));
} else {
if(!VcbAcquired)
UDFReleaseResource(&(Vcb->VCBResource));
}
// If we have dereferenced all parents 'associated'
// with input file & current file is still in use
// then it isn't worth walking down the tree
// 'cause in this case all the rest files are also used
if(!TreeLength)
break;
// AdPrint(("Stop on referenced File/Dir\n"));
}
} else {
// we get to referenced file/dir. Stop search & release resource
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
UDFReleaseResource(&(NtReqFcb->MainResource));
if(ParentFI) {
UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB);
UDFReleaseResource(&(ParentFcb->NTRequiredFCB->MainResource));
} else {
if(!VcbAcquired)
UDFReleaseResource(&(Vcb->VCBResource));
}
Delete = FALSE;
if(!TreeLength)
break;
fi = ParentFI;
}
}
if(fi) {
Fcb = fi->Fcb;
for(;TreeLength && fi;TreeLength--) {
if(Fcb) {
ParentFcb = Fcb->ParentFcb;
ASSERT(Fcb->ReferenceCount);
ASSERT(Fcb->NTRequiredFCB->CommonRefCount);
ASSERT_REF(Fcb->ReferenceCount > fi->RefCount);
UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
UDFInterlockedDecrement((PLONG)&(Fcb->NTRequiredFCB->CommonRefCount));
#ifdef UDF_DBG
} else {
BrutePoint();
#endif
}
Fcb = ParentFcb;
}
}
if(!VcbAcquired)
UDFReleaseResource(&(Vcb->VCBResource));
return ret_val;
} // end UDFCleanUpFcbChain()
VOID
UDFDoDelayedClose(
IN PtrUDFIrpContextLite NextIrpContextLite
)
{
PtrUDFIrpContext IrpContext;
AdPrint((" UDFDoDelayedClose\n"));
UDFInitializeIrpContextFromLite(&IrpContext,NextIrpContextLite);
IrpContext->Fcb->IrpContextLite = NULL;
MyFreePool__(NextIrpContextLite);
IrpContext->Fcb->FCBFlags &= ~UDF_FCB_DELAY_CLOSE;
UDFCommonClose(IrpContext,NULL);
} // end UDFDoDelayedClose()
/*
This routine removes request from Delayed Close queue.
It operates until reach lower threshold
*/
VOID
UDFDelayedClose(
PVOID unused
)
{
PLIST_ENTRY Entry;
PtrUDFIrpContextLite NextIrpContextLite;
AdPrint((" UDFDelayedClose\n"));
// Acquire DelayedCloseResource
UDFAcquireResourceExclusive(&(UDFGlobalData.DelayedCloseResource), TRUE);
while (UDFGlobalData.ReduceDelayedClose &&
(UDFGlobalData.DelayedCloseCount > UDFGlobalData.MinDelayedCloseCount)) {
Entry = UDFGlobalData.DelayedCloseQueue.Flink;
if (!IsListEmpty(Entry)) {
// Extract the IrpContext.
NextIrpContextLite = CONTAINING_RECORD( Entry,
UDFIrpContextLite,
DelayedCloseLinks );
RemoveEntryList( Entry );
UDFGlobalData.DelayedCloseCount--;
UDFDoDelayedClose(NextIrpContextLite);
} else {
BrutePoint();
}
}
while (UDFGlobalData.ReduceDirDelayedClose &&
(UDFGlobalData.DirDelayedCloseCount > UDFGlobalData.MinDirDelayedCloseCount)) {
Entry = UDFGlobalData.DirDelayedCloseQueue.Flink;
if (!IsListEmpty(Entry)) {
// Extract the IrpContext.
NextIrpContextLite = CONTAINING_RECORD( Entry,
UDFIrpContextLite,
DelayedCloseLinks );
RemoveEntryList( Entry );
UDFGlobalData.DirDelayedCloseCount--;
UDFDoDelayedClose(NextIrpContextLite);
} else {
BrutePoint();
}
}
UDFGlobalData.FspCloseActive = FALSE;
UDFGlobalData.ReduceDelayedClose = FALSE;
UDFGlobalData.ReduceDirDelayedClose = FALSE;
// Release DelayedCloseResource
UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource));
return;
} // end UDFDelayedClose()
/*
This routine performs Close operation for all files from
Delayed Close queue.
*/
VOID
UDFCloseAllDelayed(
IN PVCB Vcb
)
{
PLIST_ENTRY Entry;
PtrUDFIrpContextLite NextIrpContextLite;
BOOLEAN GlobalDataAcquired = FALSE;
AdPrint((" UDFCloseAllDelayed\n"));
// Acquire DelayedCloseResource
if (!ExIsResourceAcquiredExclusive(&UDFGlobalData.GlobalDataResource)) {
UDFAcquireResourceExclusive(&(UDFGlobalData.DelayedCloseResource), TRUE);
GlobalDataAcquired = TRUE;
}
Entry = UDFGlobalData.DelayedCloseQueue.Flink;
while (Entry != &UDFGlobalData.DelayedCloseQueue) {
// Extract the IrpContext.
NextIrpContextLite = CONTAINING_RECORD( Entry,
UDFIrpContextLite,
DelayedCloseLinks );
Entry = Entry->Flink;
if (NextIrpContextLite->Fcb->Vcb == Vcb) {
RemoveEntryList( &(NextIrpContextLite->DelayedCloseLinks) );
UDFGlobalData.DelayedCloseCount--;
UDFDoDelayedClose(NextIrpContextLite);
}
}
Entry = UDFGlobalData.DirDelayedCloseQueue.Flink;
while (Entry != &UDFGlobalData.DirDelayedCloseQueue) {
// Extract the IrpContext.
NextIrpContextLite = CONTAINING_RECORD( Entry,
UDFIrpContextLite,
DelayedCloseLinks );
Entry = Entry->Flink;
if (NextIrpContextLite->Fcb->Vcb == Vcb) {
RemoveEntryList( &(NextIrpContextLite->DelayedCloseLinks) );
UDFGlobalData.DirDelayedCloseCount--;
UDFDoDelayedClose(NextIrpContextLite);
}
}
// Release DelayedCloseResource
if(GlobalDataAcquired)
UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource));
} // end UDFCloseAllDelayed()
NTSTATUS
UDFBuildTreeItemsList(
IN PVCB Vcb,
IN PUDF_FILE_INFO FileInfo,
IN PCHECK_TREE_ITEM CheckItemProc,
IN PUDF_FILE_INFO** PassedList,
IN PULONG PassedListSize,
IN PUDF_FILE_INFO** FoundList,
IN PULONG FoundListSize
)
{
PDIR_INDEX_HDR hDirNdx;
PUDF_FILE_INFO SDirInfo;
ULONG i;
KdPrint((" UDFBuildTreeItemsList():\n"));
if(!(*PassedList) || !(*FoundList)) {
(*PassedList) = (PUDF_FILE_INFO*)
MyAllocatePool__(NonPagedPool, sizeof(PUDF_FILE_INFO)*TREE_ITEM_LIST_GRAN);
if(!(*PassedList))
return STATUS_INSUFFICIENT_RESOURCES;
(*PassedListSize) = 0;
(*FoundList) = (PUDF_FILE_INFO*)
MyAllocatePool__(NonPagedPool, sizeof(PUDF_FILE_INFO)*TREE_ITEM_LIST_GRAN);
if(!(*FoundList)) {
MyFreePool__(*PassedList);
*PassedList = NULL;
return STATUS_INSUFFICIENT_RESOURCES;
}
(*FoundListSize) = 0;
}
// check if already passed
for(i=0;i<(*PassedListSize);i++) {
if( ((*PassedList)[i]) == FileInfo )
return STATUS_SUCCESS;
}
// remember passed object
// we should not proceed linked objects twice
(*PassedListSize)++;
if( !((*PassedListSize) & (TREE_ITEM_LIST_GRAN - 1)) ) {
if(!MyReallocPool__((PCHAR)(*PassedList), (*PassedListSize)*sizeof(PUDF_FILE_INFO),
(PCHAR*)PassedList, ((*PassedListSize)+TREE_ITEM_LIST_GRAN)*sizeof(PUDF_FILE_INFO))) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
(*PassedList)[(*PassedListSize)-1] = FileInfo;
// check if this object matches our conditions
if(CheckItemProc(FileInfo)) {
// remember matched object
(*FoundListSize)++;
if( !((*FoundListSize) & (TREE_ITEM_LIST_GRAN - 1)) ) {
if(!MyReallocPool__((PCHAR)(*FoundList), (*FoundListSize)*sizeof(PUDF_DATALOC_INFO),
(PCHAR*)FoundList, ((*FoundListSize)+TREE_ITEM_LIST_GRAN)*sizeof(PUDF_DATALOC_INFO))) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
(*FoundList)[(*FoundListSize)-1] = FileInfo;
}
// walk through SDir (if any)
if((SDirInfo = FileInfo->Dloc->SDirInfo))
UDFBuildTreeItemsList(Vcb, SDirInfo, CheckItemProc,
PassedList, PassedListSize, FoundList, FoundListSize);
// walk through subsequent objects (if any)
if((hDirNdx = FileInfo->Dloc->DirIndex)) {
// scan DirIndex
UDF_DIR_SCAN_CONTEXT ScanContext;
PDIR_INDEX_ITEM DirNdx;
PUDF_FILE_INFO CurFileInfo;
if(UDFDirIndexInitScan(FileInfo, &ScanContext, 2)) {
while((DirNdx = UDFDirIndexScan(&ScanContext, &CurFileInfo))) {
if(!CurFileInfo)
continue;
UDFBuildTreeItemsList(Vcb, CurFileInfo, CheckItemProc,
PassedList, PassedListSize, FoundList, FoundListSize);
}
}
}
return STATUS_SUCCESS;
} // end UDFBuildTreeItemsList()
BOOLEAN
UDFIsInDelayedCloseQueue(
PUDF_FILE_INFO FileInfo)
{
ASSERT(FileInfo);
return (FileInfo->Fcb && FileInfo->Fcb->IrpContextLite);
} // end UDFIsInDelayedCloseQueue()
BOOLEAN
UDFIsLastClose(
PUDF_FILE_INFO FileInfo)
{
ASSERT(FileInfo);
PtrUDFFCB Fcb = FileInfo->Fcb;
if( Fcb &&
!Fcb->OpenHandleCount &&
Fcb->ReferenceCount &&
Fcb->NTRequiredFCB->SectionObject.DataSectionObject) {
return TRUE;
}
return FALSE;
} // UDFIsLastClose()
NTSTATUS
UDFCloseAllXXXDelayedInDir(
IN PVCB Vcb,
IN PUDF_FILE_INFO FileInfo,
IN BOOLEAN System
)
{
PUDF_FILE_INFO* PassedList = NULL;
ULONG PassedListSize = 0;
PUDF_FILE_INFO* FoundList = NULL;
ULONG FoundListSize = 0;
NTSTATUS RC;
ULONG i;
BOOLEAN ResAcq = FALSE;
BOOLEAN AcquiredVcb = FALSE;
UDFNTRequiredFCB* NtReqFcb;
PUDF_FILE_INFO CurFileInfo;
PFE_LIST_ENTRY CurListPtr;
PFE_LIST_ENTRY* ListPtrArray = NULL;
_SEH2_TRY {
KdPrint((" UDFCloseAllXXXDelayedInDir(): Acquire DelayedCloseResource\n"));
// Acquire DelayedCloseResource
UDFAcquireResourceExclusive(&(UDFGlobalData.DelayedCloseResource), TRUE);
ResAcq = TRUE;
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
AcquiredVcb = TRUE;
RC = UDFBuildTreeItemsList(Vcb, FileInfo,
System ? UDFIsLastClose : UDFIsInDelayedCloseQueue,
&PassedList, &PassedListSize, &FoundList, &FoundListSize);
if(!NT_SUCCESS(RC)) {
KdPrint((" UDFBuildTreeItemsList(): error %x\n", RC));
try_return(RC);
}
if(!FoundList || !FoundListSize) {
try_return(RC = STATUS_SUCCESS);
}
// build array of referenced pointers
ListPtrArray = (PFE_LIST_ENTRY*)(MyAllocatePool__(NonPagedPool, FoundListSize*sizeof(PFE_LIST_ENTRY)));
if(!ListPtrArray) {
KdPrint((" Can't alloc ListPtrArray for %x items\n", FoundListSize));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
for(i=0;i<FoundListSize;i++) {
_SEH2_TRY {
CurFileInfo = FoundList[i];
if(!CurFileInfo->ListPtr) {
CurFileInfo->ListPtr = (PFE_LIST_ENTRY)(MyAllocatePool__(NonPagedPool, sizeof(FE_LIST_ENTRY)));
if(!CurFileInfo->ListPtr) {
KdPrint((" Can't alloc ListPtrEntry for items %x\n", i));
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
CurFileInfo->ListPtr->FileInfo = CurFileInfo;
CurFileInfo->ListPtr->EntryRefCount = 0;
}
CurFileInfo->ListPtr->EntryRefCount++;
ListPtrArray[i] = CurFileInfo->ListPtr;
} _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
BrutePoint();
} _SEH2_END;
}
UDFReleaseResource(&(Vcb->VCBResource));
AcquiredVcb = FALSE;
if(System) {
// Remove from system queue
PtrUDFFCB Fcb;
IO_STATUS_BLOCK IoStatus;
BOOLEAN NoDelayed = (Vcb->VCBFlags & UDF_VCB_FLAGS_NO_DELAYED_CLOSE) ?
TRUE : FALSE;
Vcb->VCBFlags |= UDF_VCB_FLAGS_NO_DELAYED_CLOSE;
for(i=FoundListSize;i>0;i--) {
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
AcquiredVcb = TRUE;
_SEH2_TRY {
CurListPtr = ListPtrArray[i-1];
CurFileInfo = CurListPtr->FileInfo;
if(CurFileInfo &&
(Fcb = CurFileInfo->Fcb)) {
NtReqFcb = Fcb->NTRequiredFCB;
ASSERT((ULONG)NtReqFcb > 0x1000);
// ASSERT((ULONG)(NtReqFcb->SectionObject) > 0x1000);
if(!(NtReqFcb->NtReqFCBFlags & UDF_NTREQ_FCB_DELETED) &&
(NtReqFcb->NtReqFCBFlags & UDF_NTREQ_FCB_MODIFIED)) {
MmPrint((" CcFlushCache()\n"));
CcFlushCache(&(NtReqFcb->SectionObject), NULL, 0, &IoStatus);
}
if(NtReqFcb->SectionObject.ImageSectionObject) {
MmPrint((" MmFlushImageSection()\n"));
MmFlushImageSection(&(NtReqFcb->SectionObject), MmFlushForWrite);
}
if(NtReqFcb->SectionObject.DataSectionObject) {
MmPrint((" CcPurgeCacheSection()\n"));
CcPurgeCacheSection( &(NtReqFcb->SectionObject), NULL, 0, FALSE );
}
} else {
MmPrint((" Skip item: deleted\n"));
}
CurListPtr->EntryRefCount--;
if(!CurListPtr->EntryRefCount) {
if(CurListPtr->FileInfo)
CurListPtr->FileInfo->ListPtr = NULL;
MyFreePool__(CurListPtr);
}
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
BrutePoint();
} _SEH2_END;
UDFReleaseResource(&(Vcb->VCBResource));
AcquiredVcb = FALSE;
}
if(!NoDelayed)
Vcb->VCBFlags &= ~UDF_VCB_FLAGS_NO_DELAYED_CLOSE;
} else {
// Remove from internal queue
PtrUDFIrpContextLite NextIrpContextLite;
for(i=FoundListSize;i>0;i--) {
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
AcquiredVcb = TRUE;
CurListPtr = ListPtrArray[i-1];
CurFileInfo = CurListPtr->FileInfo;
if(CurFileInfo &&
CurFileInfo->Fcb &&
(NextIrpContextLite = CurFileInfo->Fcb->IrpContextLite)) {
RemoveEntryList( &(NextIrpContextLite->DelayedCloseLinks) );
if (NextIrpContextLite->Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
// BrutePoint();
UDFGlobalData.DirDelayedCloseCount--;
} else {
UDFGlobalData.DelayedCloseCount--;
}
UDFDoDelayedClose(NextIrpContextLite);
}
CurListPtr->EntryRefCount--;
if(!CurListPtr->EntryRefCount) {
if(CurListPtr->FileInfo)
CurListPtr->FileInfo->ListPtr = NULL;
MyFreePool__(CurListPtr);
}
UDFReleaseResource(&(Vcb->VCBResource));
AcquiredVcb = FALSE;
}
}
RC = STATUS_SUCCESS;
try_exit: NOTHING;
} _SEH2_FINALLY {
// release Vcb
if(AcquiredVcb)
UDFReleaseResource(&(Vcb->VCBResource));
// Release DelayedCloseResource
if(ResAcq)
UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource));
if(ListPtrArray)
MyFreePool__(ListPtrArray);
if(PassedList)
MyFreePool__(PassedList);
if(FoundList)
MyFreePool__(FoundList);
} _SEH2_END;
return RC;
} // end UDFCloseAllXXXDelayedInDir(
/*
This routine adds request to Delayed Close queue.
If number of queued requests exceeds higher threshold it fires
UDFDelayedClose()
*/
NTSTATUS
UDFQueueDelayedClose(
PtrUDFIrpContext IrpContext,
PtrUDFFCB Fcb
)
{
PtrUDFIrpContextLite IrpContextLite;
BOOLEAN StartWorker = FALSE;
NTSTATUS RC;
AdPrint((" UDFQueueDelayedClose\n"));
_SEH2_TRY {
// Acquire DelayedCloseResource
UDFAcquireResourceExclusive(&(UDFGlobalData.DelayedCloseResource), TRUE);
UDFAcquireResourceShared(&(Fcb->Vcb->VCBResource), TRUE);
if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) {
try_return(RC = STATUS_DELETE_PENDING);
}
if(Fcb->IrpContextLite ||
Fcb->FCBFlags & UDF_FCB_POSTED_RENAME) {
// BrutePoint();
try_return(RC = STATUS_UNSUCCESSFUL);
}
if(!NT_SUCCESS(RC = UDFInitializeIrpContextLite(&IrpContextLite,IrpContext,Fcb))) {
try_return(RC);
}
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
InsertTailList( &UDFGlobalData.DirDelayedCloseQueue,
&IrpContextLite->DelayedCloseLinks );
UDFGlobalData.DirDelayedCloseCount++;
} else {
InsertTailList( &UDFGlobalData.DelayedCloseQueue,
&IrpContextLite->DelayedCloseLinks );
UDFGlobalData.DelayedCloseCount++;
}
Fcb->IrpContextLite = IrpContextLite;
// If we are above our threshold then start the delayed
// close operation.
if(UDFGlobalData.DelayedCloseCount > UDFGlobalData.MaxDelayedCloseCount) {
UDFGlobalData.ReduceDelayedClose = TRUE;
if(!UDFGlobalData.FspCloseActive) {
UDFGlobalData.FspCloseActive = TRUE;
StartWorker = TRUE;
}
}
// If we are above our threshold then start the delayed
// close operation.
if(UDFGlobalData.DirDelayedCloseCount > UDFGlobalData.MaxDirDelayedCloseCount) {
UDFGlobalData.ReduceDirDelayedClose = TRUE;
if(!UDFGlobalData.FspCloseActive) {
UDFGlobalData.FspCloseActive = TRUE;
StartWorker = TRUE;
}
}
// Start the FspClose thread if we need to.
if(StartWorker) {
ExQueueWorkItem( &UDFGlobalData.CloseItem, CriticalWorkQueue );
}
RC = STATUS_SUCCESS;
try_exit: NOTHING;
} _SEH2_FINALLY {
if(!NT_SUCCESS(RC)) {
Fcb->FCBFlags &= ~UDF_FCB_DELAY_CLOSE;
}
UDFReleaseResource(&(Fcb->Vcb->VCBResource));
// Release DelayedCloseResource
UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource));
} _SEH2_END;
return RC;
} // end UDFQueueDelayedClose()