/* 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()
PTHREAD_STRUCT DLDAllocFindThread(ULONG ThreadId) { ULONG i = 0; PTHREAD_STRUCT Temp = DLDThreadTable; ULONG FirstEmpty = -1; while (i<MaxThreadCount) { if (Temp->ThreadId == ThreadId) { return Temp; } else if (FirstEmpty == -1 && !Temp->ThreadId) { FirstEmpty = i; } Temp++; i++; } // Not found. Allocate new one. if (i == MaxThreadCount) { if (FirstEmpty == -1) { UDFPrint(("Not enough table entries. Try to increase MaxThrdCount on next build")); BrutePoint(); } i = FirstEmpty; } Temp = DLDThreadTable + i; RtlZeroMemory(Temp, sizeof(THREAD_STRUCT)); Temp->ThreadId = ThreadId; return Temp; }
VOID DLDpWaitForResource( IN PERESOURCE Resource, IN DISPATCHER_HEADER *DispatcherObject, IN PTHREAD_STRUCT ThrdStruct ) { KIRQL oldIrql; ULONG ResourceWaitCount = 0; Resource->ContentionCount++; while (KeWaitForSingleObject(DispatcherObject,Executive,KernelMode,FALSE,&DLDpTimeout) == STATUS_TIMEOUT) { KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); if (++ResourceWaitCount>DLDpResourceTimeoutCount) { // May be deadlock? ResourceWaitCount = 0; if (DLDProcessResource(Resource, ThrdStruct,DLD_MAX_REC_LEVEL)) { UDFPrint((" which thread %x has tried to acquire at (BugCheckId:%x:Line:%d)\n", ThrdStruct->ThreadId, ThrdStruct->BugCheckId, ThrdStruct->Line )); BrutePoint(); } } // Priority boosts // ..... // End of priority boosts KeReleaseSpinLock(&Resource->SpinLock, oldIrql); } }
/// Initialize deadlock detector VOID DLDInit(ULONG MaxThrdCount /// Maximum supported number of threads ) { if (KeNumberProcessors>1) { UDFPrint(("Deadlock Detector is designed for uniprocessor machines only!\n")); BrutePoint(); } DLDpTimeout.QuadPart = -40000000I64; MaxThreadCount = MaxThrdCount; DLDThreadTable = (PTHREAD_STRUCT) DLDAllocatePool(MaxThreadCount*sizeof(THREAD_STRUCT)); RtlZeroMemory(DLDThreadTable, sizeof(THREAD_STRUCT)*MaxThreadCount); }
/// TRUE Indicates deadlock BOOLEAN DLDProcessResource( PERESOURCE Resource, // resource to process PTHREAD_STRUCT ThrdStruct, // thread structure of caller's thread ULONG RecLevel) // current recurse level { if (RecLevel <= 0) { BrutePoint(); return FALSE; } // If resource is free, just return. Not possible, but we must check. if (!Resource->ActiveCount) { return FALSE; } PTHREAD_STRUCT ThreadOwner; if (Resource->Flag & ResourceOwnedExclusive || (Resource->OwnerThreads[1].OwnerCount == 1)) { // If only one owner // Find thread owning this resource if (Resource->Flag & ResourceOwnedExclusive) { ThreadOwner = DLDFindThread(Resource->OwnerThreads[0].OwnerThread); } else { ThreadOwner = DLDFindThread(Resource->OwnerThreads[1].OwnerThread); } BOOLEAN Result = FALSE; if (ThreadOwner) { Result = DLDProcessThread(ThreadOwner, ThrdStruct, Resource,RecLevel-1); } return Result; } else { // Many owners int i; for (i=0; i<Resource->OwnerThreads[0].TableSize; i++) { if (Resource->OwnerTable[i].OwnerThread) { ThreadOwner = DLDFindThread(Resource->OwnerTable[i].OwnerThread); if (ThreadOwner && DLDProcessThread(ThreadOwner, ThrdStruct, Resource,RecLevel-1)) { return TRUE; } } } } return FALSE; }
/// TRUE Indicates deadlock BOOLEAN DLDProcessThread(PTHREAD_STRUCT ThrdOwner, PTHREAD_STRUCT ThrdStruct, PERESOURCE Resource, ULONG RecLevel) { if (ThrdOwner == ThrdStruct) { // ERESOURCE wait cycle. Deadlock detected. UDFPrint(("DLD: *********DEADLOCK DETECTED*********\n")); UDFPrint(("Thread %x holding resource %x\n",ThrdOwner->ThreadId,Resource)); return TRUE; } for (int i=RecLevel+1;i<DLD_MAX_REC_LEVEL;i++) { if (DLDThreadAcquireChain[i].Thread->ThreadId == ThrdOwner->ThreadId) { // ERESOURCE wait cycle. Deadlock detected. UDFPrint(("DLD: *********DEADLOCK DETECTED*********\n")); UDFPrint(("Thread %x holding resource %x\n",ThrdOwner->ThreadId,Resource)); for (int j=RecLevel+1;j<=i;j++) { UDFPrint((" awaited by thread %x at (BugCheckId:%x:Line:%d) holding resource %x\n", DLDThreadAcquireChain[i].Thread->ThreadId, DLDThreadAcquireChain[i].Thread->BugCheckId, DLDThreadAcquireChain[i].Thread->Line, Resource)); } BrutePoint(); return FALSE; } } DLDThreadAcquireChain[RecLevel].Thread = ThrdOwner; DLDThreadAcquireChain[RecLevel].HoldingResource = Resource; // Find resource, awaited by thread if (ThrdOwner->WaitingResource) { if (DLDProcessResource(ThrdOwner->WaitingResource, ThrdStruct,RecLevel)) { UDFPrint((" awaited by thread %x at (BugCheckId:%x:Line:%d) holding resource %x\n", ThrdOwner->ThreadId, ThrdOwner->BugCheckId, ThrdOwner->Line, Resource)); return TRUE; } } return FALSE; }
/* This routine walks through the tree to RootDir & calls UDFCloseFile__() for each file instance imho, Useful feature */ NTSTATUS UDFCloseFileInfoChain( IN PVCB Vcb, IN PUDF_FILE_INFO fi, IN ULONG TreeLength, IN BOOLEAN VcbAcquired ) { PUDF_FILE_INFO ParentFI; PtrUDFFCB Fcb; PtrUDFFCB ParentFcb = NULL; NTSTATUS RC = STATUS_SUCCESS; NTSTATUS RC2; // we can't process Tree until we can acquire Vcb if(!VcbAcquired) UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE); AdPrint(("UDFCloseFileInfoChain\n")); for(; TreeLength && fi; TreeLength--) { // close parent chain (if any) // if we started path parsing not from RootDir on Create, // we would never get RootDir here ValidateFileInfo(fi); // acquire parent if((ParentFI = fi->ParentFile)) { ParentFcb = fi->Fcb->ParentFcb; ASSERT(ParentFcb); ASSERT(ParentFcb->NTRequiredFCB); UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB); UDFAcquireResourceExclusive(&(ParentFcb->NTRequiredFCB->MainResource),TRUE); ASSERT(ParentFcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB); ASSERT(ParentFcb->NTRequiredFCB->CommonFCBHeader.NodeTypeCode == UDF_NODE_TYPE_NT_REQ_FCB); } else { AdPrint(("Acquiring VCB...\n")); UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE); AdPrint(("Done\n")); } // acquire current file/dir // we must assure that no more threads try to reuse this object if((Fcb = fi->Fcb)) { UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB); UDFAcquireResourceExclusive(&(Fcb->NTRequiredFCB->MainResource),TRUE); ASSERT_REF(Fcb->ReferenceCount >= fi->RefCount); if(!(Fcb->FCBFlags & UDF_FCB_DELETED) && (Fcb->FCBFlags & UDF_FCB_VALID)) UDFWriteSecurity(Vcb, Fcb, &(Fcb->NTRequiredFCB->SecurityDesc)); RC2 = UDFCloseFile__(Vcb,fi); if(!NT_SUCCESS(RC2)) RC = RC2; ASSERT_REF(Fcb->ReferenceCount > fi->RefCount); UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB); UDFReleaseResource(&(Fcb->NTRequiredFCB->MainResource)); } else { BrutePoint(); RC2 = UDFCloseFile__(Vcb,fi); if(!NT_SUCCESS(RC2)) RC = RC2; } if(ParentFI) { UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB); UDFReleaseResource(&(ParentFcb->NTRequiredFCB->MainResource)); } else { UDFReleaseResource(&(Vcb->VCBResource)); } fi = ParentFI; } if(!VcbAcquired) UDFReleaseResource(&(Vcb->VCBResource)); return RC; } // end UDFCloseFileInfoChain()
/************************************************************************* * * Function: UDFCommonCleanup() * * 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: Does not matter! * *************************************************************************/ NTSTATUS UDFCommonCleanup( PtrUDFIrpContext PtrIrpContext, PIRP Irp) { IO_STATUS_BLOCK IoStatus; NTSTATUS RC = STATUS_SUCCESS; NTSTATUS RC2; PIO_STACK_LOCATION IrpSp = NULL; PFILE_OBJECT FileObject = NULL; PtrUDFFCB Fcb = NULL; PtrUDFCCB Ccb = NULL; PVCB Vcb = NULL; PtrUDFNTRequiredFCB NtReqFcb = NULL; ULONG lc = 0; BOOLEAN AcquiredVcb = FALSE; BOOLEAN AcquiredFCB = FALSE; BOOLEAN AcquiredParentFCB = FALSE; // BOOLEAN CompleteIrp = TRUE; // BOOLEAN PostRequest = FALSE; BOOLEAN ChangeTime = FALSE; #ifdef UDF_DBG BOOLEAN CanWait = FALSE; #endif // UDF_DBG BOOLEAN ForcedCleanUp = FALSE; PUDF_FILE_INFO NextFileInfo = NULL; #ifdef UDF_DBG UNICODE_STRING CurName; PDIR_INDEX_HDR DirNdx; #endif // UDF_DBG // PUDF_DATALOC_INFO Dloc; TmPrint(("UDFCommonCleanup\n")); // BrutePoint(); _SEH2_TRY { // First, get a pointer to the current I/O stack location IrpSp = IoGetCurrentIrpStackLocation(Irp); if(!IrpSp) try_return(RC = STATUS_INVALID_PARAMETER); FileObject = IrpSp->FileObject; // Get the FCB and CCB pointers Ccb = (PtrUDFCCB)(FileObject->FsContext2); ASSERT(Ccb); Fcb = Ccb->Fcb; ASSERT(Fcb); Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension); ASSERT(Vcb); ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB); // Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; #ifdef UDF_DBG CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; AdPrint((" %s\n", CanWait ? "Wt" : "nw")); ASSERT(CanWait); #endif // UDF_DBG UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; // Steps we shall take at this point are: // (a) Acquire the file (FCB) exclusively // (b) Flush file data to disk // (c) Talk to the FSRTL package (if we use it) about pending oplocks. // (d) Notify the FSRTL package for use with pending notification IRPs // (e) Unlock byte-range locks (if any were acquired by process) // (f) Update time stamp values (e.g. fast-IO had been performed) // (g) Inform the Cache Manager to uninitialize Cache Maps ... // and other similar stuff. // BrutePoint(); NtReqFcb = Fcb->NTRequiredFCB; if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) { AdPrint(("Cleaning up Volume\n")); AdPrint(("UDF: OpenHandleCount: %x\n",Fcb->OpenHandleCount)); UDFInterlockedDecrement((PLONG)&(Fcb->OpenHandleCount)); UDFInterlockedDecrement((PLONG)&(Vcb->VCBHandleCount)); if(FileObject->Flags & FO_CACHE_SUPPORTED) { // we've cached close UDFInterlockedDecrement((PLONG)&(Fcb->CachedOpenHandleCount)); } ASSERT(Fcb->OpenHandleCount <= (Fcb->ReferenceCount-1)); // If this handle had write access, and actually wrote something, // flush the device buffers, and then set the verify bit now // just to be safe (in case there is no dismount). if( FileObject->WriteAccess && (FileObject->Flags & FO_FILE_MODIFIED)) { Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME; } // User may decide to close locked volume without call to unlock proc // So, handle this situation properly & unlock it now... if (FileObject == Vcb->VolumeLockFileObject) { Vcb->VolumeLockFileObject = NULL; Vcb->VolumeLockPID = -1; Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_LOCKED; Vcb->Vpb->Flags &= ~VPB_LOCKED; UDFNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK); } MmPrint((" CcUninitializeCacheMap()\n")); CcUninitializeCacheMap(FileObject, NULL, NULL); // reset device if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) && (Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER)) { // this call doesn't modify data buffer // it just requires its presence UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, TRUE); } // We must clean up the share access at this time, since we may not // get a Close call for awhile if the file was mapped through this // File Object. IoRemoveShareAccess( FileObject, &(NtReqFcb->FCBShareAccess) ); try_return(RC = STATUS_SUCCESS); } // BrutePoint(); #ifdef UDF_DBG DirNdx = UDFGetDirIndexByFileInfo(Fcb->FileInfo); if(DirNdx) { CurName.Buffer = UDFDirIndex(DirNdx, Fcb->FileInfo->Index)->FName.Buffer; if(CurName.Buffer) { AdPrint(("Cleaning up file: %ws %8.8x\n", CurName.Buffer, FileObject)); } else { AdPrint(("Cleaning up file: ??? \n")); } } #endif //UDF_DBG AdPrint(("UDF: OpenHandleCount: %x\n",Fcb->OpenHandleCount)); // Acquire parent object if(Fcb->FileInfo->ParentFile) { UDF_CHECK_PAGING_IO_RESOURCE(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB); UDFAcquireResourceExclusive(&(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB->MainResource),TRUE); } else { UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE); } AcquiredParentFCB = TRUE; // Acquire current object UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFAcquireResourceExclusive(&(NtReqFcb->MainResource),TRUE); AcquiredFCB = TRUE; // dereference object UDFInterlockedDecrement((PLONG)&(Fcb->OpenHandleCount)); UDFInterlockedDecrement((PLONG)&(Vcb->VCBHandleCount)); if(FileObject->Flags & FO_CACHE_SUPPORTED) { // we've cached close UDFInterlockedDecrement((PLONG)&(Fcb->CachedOpenHandleCount)); } ASSERT(Fcb->OpenHandleCount <= (Fcb->ReferenceCount-1)); // check if Ccb being cleaned up has DeleteOnClose flag set #ifndef UDF_READ_ONLY_BUILD if(Ccb->CCBFlags & UDF_CCB_DELETE_ON_CLOSE) { AdPrint((" DeleteOnClose\n")); // Ok, now we'll become 'delete on close'... ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); Fcb->FCBFlags |= UDF_FCB_DELETE_ON_CLOSE; FileObject->DeletePending = TRUE; // Report this to the dir notify package for a directory. if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) { FsRtlNotifyFullChangeDirectory( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP), (PVOID)Ccb, NULL, FALSE, FALSE, 0, NULL, NULL, NULL ); } } #endif //UDF_READ_ONLY_BUILD if(!(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { // Unlock all outstanding file locks. FsRtlFastUnlockAll(&(NtReqFcb->FileLock), FileObject, IoGetRequestorProcess(Irp), NULL); } // get Link count lc = UDFGetFileLinkCount(Fcb->FileInfo); #ifndef UDF_READ_ONLY_BUILD if( (Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) && !(Fcb->OpenHandleCount)) { // This can be useful for Streams, those were brutally deleted // (together with parent object) ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); FileObject->DeletePending = TRUE; // we should mark all streams of the file being deleted // for deletion too, if there are no more Links to // main data stream if((lc <= 1) && !UDFIsSDirDeleted(Fcb->FileInfo->Dloc->SDirInfo)) { RC = UDFMarkStreamsForDeletion(Vcb, Fcb, TRUE); // Delete } // we can release these resources 'cause UDF_FCB_DELETE_ON_CLOSE // flag is already set & the file can't be opened UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); AcquiredFCB = FALSE; if(Fcb->FileInfo->ParentFile) { UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB); UDFReleaseResource(&(Fcb->ParentFcb->NTRequiredFCB->MainResource)); } else { UDFReleaseResource(&(Vcb->VCBResource)); } AcquiredParentFCB = FALSE; UDFReleaseResource(&(Vcb->VCBResource)); AcquiredVcb = FALSE; // Make system to issue last Close request // for our Target ... UDFRemoveFromSystemDelayedQueue(Fcb); #ifdef UDF_DELAYED_CLOSE // remove file from our DelayedClose queue UDFRemoveFromDelayedQueue(Fcb); ASSERT(!Fcb->IrpContextLite); #endif //UDF_DELAYED_CLOSE UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; if(Fcb->FileInfo->ParentFile) { UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB); UDFAcquireResourceExclusive(&(Fcb->ParentFcb->NTRequiredFCB->MainResource),TRUE); } else { UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE); } AcquiredParentFCB = TRUE; UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFAcquireResourceExclusive(&(NtReqFcb->MainResource),TRUE); AcquiredFCB = TRUE; // we should set file sizes to zero if there are no more // links to this file if(lc <= 1) { // Synchronize here with paging IO UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource),TRUE); // set file size to zero (for system cache manager) // NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = NtReqFcb->CommonFCBHeader.FileSize.QuadPart = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = 0; CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize)); UDFReleaseResource(&(NtReqFcb->PagingIoResource)); } } #endif //UDF_READ_ONLY_BUILD #ifdef UDF_DELAYED_CLOSE if ((Fcb->ReferenceCount == 1) && /*(Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB) &&*/ // see above (!(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE)) ) { Fcb->FCBFlags |= UDF_FCB_DELAY_CLOSE; } #endif //UDF_DELAYED_CLOSE NextFileInfo = Fcb->FileInfo; #ifndef UDF_READ_ONLY_BUILD // do we need to delete it now ? if( (Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) && !(Fcb->OpenHandleCount)) { // can we do it ? if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) { ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); if(!UDFIsDirEmpty__(NextFileInfo)) { // forget about it Fcb->FCBFlags &= ~UDF_FCB_DELETE_ON_CLOSE; goto DiscardDelete; } } else if (lc <= 1) { // Synchronize here with paging IO BOOLEAN AcquiredPagingIo; AcquiredPagingIo = UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb->PagingIoResource)); // set file size to zero (for UdfInfo package) // we should not do this for directories and linked files UDFResizeFile__(Vcb, NextFileInfo, 0); if(AcquiredPagingIo) { UDFReleaseResource(&(NtReqFcb->PagingIoResource)); } } // mark parent object for deletion if requested if((Fcb->FCBFlags & UDF_FCB_DELETE_PARENT) && Fcb->ParentFcb) { ASSERT(!(Fcb->ParentFcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); Fcb->ParentFcb->FCBFlags |= UDF_FCB_DELETE_ON_CLOSE; } // flush file. It is required by UDFUnlinkFile__() RC = UDFFlushFile__(Vcb, NextFileInfo); if(!NT_SUCCESS(RC)) { AdPrint(("Error flushing file !!!\n")); } // try to unlink if((RC = UDFUnlinkFile__(Vcb, NextFileInfo, TRUE)) == STATUS_CANNOT_DELETE) { // If we can't delete file with Streams due to references, // mark SDir & Streams // for Deletion. We shall also set DELETE_PARENT flag to // force Deletion of the current file later... when curently // opened Streams would be cleaned up. // WARNING! We should keep SDir & Streams if there is a // link to this file if(NextFileInfo->Dloc && NextFileInfo->Dloc->SDirInfo && NextFileInfo->Dloc->SDirInfo->Fcb) { BrutePoint(); if(!UDFIsSDirDeleted(NextFileInfo->Dloc->SDirInfo)) { // RC = UDFMarkStreamsForDeletion(Vcb, Fcb, TRUE); // Delete //#ifdef UDF_ALLOW_PRETEND_DELETED UDFPretendFileDeleted__(Vcb, Fcb->FileInfo); //#endif //UDF_ALLOW_PRETEND_DELETED } goto NotifyDelete; } else { // Getting here means that we can't delete file because of // References/PemissionsDenied/Smth.Else, // but not Linked+OpenedStream BrutePoint(); // RC = STATUS_SUCCESS; goto DiscardDelete_1; } } else { DiscardDelete_1: // We have got an ugly ERROR, or // file is deleted, so forget about it ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); ForcedCleanUp = TRUE; if(NT_SUCCESS(RC)) Fcb->FCBFlags &= ~UDF_FCB_DELETE_ON_CLOSE; Fcb->FCBFlags |= UDF_FCB_DELETED; RC = STATUS_SUCCESS; } NotifyDelete: // We should prevent SetEOF operations on completly // deleted data streams if(lc < 1) { NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_DELETED; } // Report that we have removed an entry. if(UDFIsAStream(NextFileInfo)) { UDFNotifyFullReportChange( Vcb, NextFileInfo, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_REMOVED_STREAM); } else { UDFNotifyFullReportChange( Vcb, NextFileInfo, UDFIsADirectory(NextFileInfo) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); } } else if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) { DiscardDelete: UDFNotifyFullReportChange( Vcb, NextFileInfo, ((Ccb->CCBFlags & UDF_CCB_ACCESS_TIME_SET) ? FILE_NOTIFY_CHANGE_LAST_ACCESS : 0) | ((Ccb->CCBFlags & UDF_CCB_WRITE_TIME_SET) ? (FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_LAST_WRITE) : 0) | 0, UDFIsAStream(NextFileInfo) ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED); } #endif //UDF_READ_ONLY_BUILD if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) { // Report to the dir notify package for a directory. FsRtlNotifyCleanup( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP), (PVOID)Ccb ); } // we can't purge Cache when more than one link exists if(lc > 1) { ForcedCleanUp = FALSE; } if ( (FileObject->Flags & FO_CACHE_SUPPORTED) && (NtReqFcb->SectionObject.DataSectionObject) ) { BOOLEAN LastNonCached = (!Fcb->CachedOpenHandleCount && Fcb->OpenHandleCount); // If this was the last cached open, and there are open // non-cached handles, attempt a flush and purge operation // to avoid cache coherency overhead from these non-cached // handles later. We ignore any I/O errors from the flush. // We shall not flush deleted files RC = STATUS_SUCCESS; if( LastNonCached || (!Fcb->OpenHandleCount && !ForcedCleanUp) ) { #ifndef UDF_READ_ONLY_BUILD LONGLONG OldFileSize, NewFileSize; if( (OldFileSize = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart) < (NewFileSize = NtReqFcb->CommonFCBHeader.FileSize.QuadPart)) { /* UDFZeroDataEx(NtReqFcb, OldFileSize, NewFileSize - OldFileSize, TRUE, Vcb, FileObject);*/ NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = NewFileSize; } #endif //UDF_READ_ONLY_BUILD MmPrint((" CcFlushCache()\n")); CcFlushCache( &(NtReqFcb->SectionObject), NULL, 0, &IoStatus ); if(!NT_SUCCESS(IoStatus.Status)) { MmPrint((" CcFlushCache() error: %x\n", IoStatus.Status)); RC = IoStatus.Status; } } // If file is deleted or it is last cached open, but there are // some non-cached handles we should purge cache section if(ForcedCleanUp || LastNonCached) { if(NtReqFcb->SectionObject.DataSectionObject) { MmPrint((" CcPurgeCacheSection()\n")); CcPurgeCacheSection( &(NtReqFcb->SectionObject), NULL, 0, FALSE ); } /* MmPrint((" CcPurgeCacheSection()\n")); CcPurgeCacheSection( &(NtReqFcb->SectionObject), NULL, 0, FALSE );*/ } // we needn't Flush here. It will be done in UDFCloseFileInfoChain() } #ifndef UDF_READ_ONLY_BUILD // Update FileTimes & Attrs if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) && !(Fcb->FCBFlags & (UDF_FCB_DELETE_ON_CLOSE | UDF_FCB_DELETED /*| UDF_FCB_DIRECTORY | UDF_FCB_READ_ONLY*/)) && !UDFIsAStreamDir(NextFileInfo)) { LONGLONG NtTime; LONGLONG ASize; KeQuerySystemTime((PLARGE_INTEGER)&NtTime); // Check if we should set ARCHIVE bit & LastWriteTime if(FileObject->Flags & FO_FILE_MODIFIED) { ULONG Attr; PDIR_INDEX_ITEM DirNdx; DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(NextFileInfo), NextFileInfo->Index); ASSERT(DirNdx); // Archive bit if(!(Ccb->CCBFlags & UDF_CCB_ATTRIBUTES_SET) && (Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT)) { Attr = UDFAttributesToNT(DirNdx, NextFileInfo->Dloc->FileEntry); if(!(Attr & FILE_ATTRIBUTE_ARCHIVE)) UDFAttributesToUDF(DirNdx, NextFileInfo->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE); } // WriteTime if(!(Ccb->CCBFlags & UDF_CCB_WRITE_TIME_SET) && (Vcb->CompatFlags & UDF_VCB_IC_UPDATE_MODIFY_TIME)) { UDFSetFileXTime(NextFileInfo, NULL, &NtTime, NULL, &NtTime); NtReqFcb->LastWriteTime.QuadPart = NtReqFcb->LastAccessTime.QuadPart = NtTime; ChangeTime = TRUE; } } if(!(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { // Update sizes in DirIndex if(!Fcb->OpenHandleCount) { ASize = UDFGetFileAllocationSize(Vcb, NextFileInfo); // NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart; UDFSetFileSizeInDirNdx(Vcb, NextFileInfo, &ASize); } else if(FileObject->Flags & FO_FILE_SIZE_CHANGED) { ASize = //UDFGetFileAllocationSize(Vcb, NextFileInfo); NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart; UDFSetFileSizeInDirNdx(Vcb, NextFileInfo, &ASize); } } // AccessTime if((FileObject->Flags & FO_FILE_FAST_IO_READ) && !(Ccb->CCBFlags & UDF_CCB_ACCESS_TIME_SET) && (Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ACCESS_TIME)) { UDFSetFileXTime(NextFileInfo, NULL, &NtTime, NULL, NULL); NtReqFcb->LastAccessTime.QuadPart = NtTime; // ChangeTime = TRUE; } // ChangeTime (AttrTime) if(!(Ccb->CCBFlags & UDF_CCB_MODIFY_TIME_SET) && (Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ATTR_TIME) && (ChangeTime || (Ccb->CCBFlags & (UDF_CCB_ATTRIBUTES_SET | UDF_CCB_CREATE_TIME_SET | UDF_CCB_ACCESS_TIME_SET | UDF_CCB_WRITE_TIME_SET))) ) { UDFSetFileXTime(NextFileInfo, NULL, NULL, &NtTime, NULL); NtReqFcb->ChangeTime.QuadPart = NtTime; } } #endif //UDF_READ_ONLY_BUILD if(!(Fcb->FCBFlags & UDF_FCB_DIRECTORY) && ForcedCleanUp) { // flush system cache MmPrint((" CcUninitializeCacheMap()\n")); CcUninitializeCacheMap(FileObject, &(UDFGlobalData.UDFLargeZero), NULL); } else { MmPrint((" CcUninitializeCacheMap()\n")); CcUninitializeCacheMap(FileObject, NULL, NULL); } // release resources now. // they'll be acquired in UDFCloseFileInfoChain() UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); AcquiredFCB = FALSE; if(Fcb->FileInfo->ParentFile) { UDF_CHECK_PAGING_IO_RESOURCE(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB); UDFReleaseResource(&(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB->MainResource)); } else { UDFReleaseResource(&(Vcb->VCBResource)); } AcquiredParentFCB = FALSE; // close the chain ASSERT(AcquiredVcb); RC2 = UDFCloseFileInfoChain(Vcb, NextFileInfo, Ccb->TreeLength, TRUE); if(NT_SUCCESS(RC)) RC = RC2; Ccb->CCBFlags |= UDF_CCB_CLEANED; // We must clean up the share access at this time, since we may not // get a Close call for awhile if the file was mapped through this // File Object. IoRemoveShareAccess( FileObject, &(NtReqFcb->FCBShareAccess) ); NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb); FileObject->Flags |= FO_CLEANUP_COMPLETE; try_exit: NOTHING; } _SEH2_FINALLY { if(AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); } if(AcquiredParentFCB) { if(Fcb->FileInfo->ParentFile) { UDF_CHECK_PAGING_IO_RESOURCE(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB); UDFReleaseResource(&(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB->MainResource)); } else { UDFReleaseResource(&(Vcb->VCBResource)); } } if(AcquiredVcb) { UDFReleaseResource(&(Vcb->VCBResource)); AcquiredVcb = FALSE; } if (!_SEH2_AbnormalTermination()) { // complete the IRP Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_DISK_INCREMENT); // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); } } _SEH2_END; // end of "__finally" processing return(RC); } // end UDFCommonCleanup()
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 {
/* 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()
/************************************************************************* * * 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()