mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 16:02:56 +00:00

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
1190 lines
41 KiB
C++
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()
|
|
|