/* 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()
/************************************************************************* * * Function: UDFQueryDirectory() * * Description: * Query directory request. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS NTAPI UDFQueryDirectory( PtrUDFIrpContext PtrIrpContext, PIRP Irp, PIO_STACK_LOCATION IrpSp, PFILE_OBJECT FileObject, PtrUDFFCB Fcb, PtrUDFCCB Ccb ) { NTSTATUS RC = STATUS_SUCCESS; BOOLEAN PostRequest = FALSE; PtrUDFNTRequiredFCB NtReqFcb = NULL; BOOLEAN CanWait = FALSE; PVCB Vcb = NULL; BOOLEAN AcquiredFCB = FALSE; unsigned long BufferLength = 0; UNICODE_STRING SearchPattern; PUNICODE_STRING PtrSearchPattern; FILE_INFORMATION_CLASS FileInformationClass; BOOLEAN ReturnSingleEntry = FALSE; PUCHAR Buffer = NULL; BOOLEAN FirstTimeQuery = FALSE; LONG NextMatch; LONG PrevMatch = -1; ULONG CurrentOffset; ULONG BaseLength; ULONG FileNameBytes; ULONG Information = 0; ULONG LastOffset = 0; BOOLEAN AtLeastOneFound = FALSE; PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp; PUDF_FILE_INFO DirFileInfo = NULL; PDIR_INDEX_HDR hDirIndex = NULL; PFILE_BOTH_DIR_INFORMATION DirInformation = NULL; // Returned from udf_info module PFILE_BOTH_DIR_INFORMATION BothDirInformation = NULL; // Pointer in callers buffer PFILE_NAMES_INFORMATION NamesInfo; ULONG BytesRemainingInBuffer; UCHAR FNM_Flags = 0; PHASH_ENTRY cur_hashes = NULL; PDIR_INDEX_ITEM DirNdx; // do some pre-init... SearchPattern.Buffer = NULL; UDFPrint(("UDFQueryDirectory: @=%#x\n", &PtrIrpContext)); #define CanBe8dot3 (FNM_Flags & UDF_FNM_FLAG_CAN_BE_8D3) #define IgnoreCase (FNM_Flags & UDF_FNM_FLAG_IGNORE_CASE) #define ContainsWC (FNM_Flags & UDF_FNM_FLAG_CONTAINS_WC) _SEH2_TRY { // Validate the sent-in FCB if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { // We will only allow notify requests on directories. try_return(RC = STATUS_INVALID_PARAMETER); } // Obtain the callers parameters NtReqFcb = Fcb->NTRequiredFCB; CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; Vcb = Fcb->Vcb; //Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? 0 : UDF_FNM_FLAG_IGNORE_CASE; DirFileInfo = Fcb->FileInfo; BufferLength = pStackLocation->Parameters.QueryDirectory.Length; // If the caller does not want to block, it would be easier to // simply post the request now. if (!CanWait) { PostRequest = TRUE; try_return(RC = STATUS_PENDING); } // Continue obtaining the callers parameters... if(IgnoreCase && pStackLocation->Parameters.QueryDirectory.FileName) { PtrSearchPattern = &SearchPattern; if(!NT_SUCCESS(RC = RtlUpcaseUnicodeString(PtrSearchPattern, (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName), TRUE))) try_return(RC); } else { PtrSearchPattern = (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName); } FileInformationClass = pStackLocation->Parameters.QueryDirectory.FileInformationClass; // Calculate baselength (without name) for each InfoClass switch (FileInformationClass) { case FileDirectoryInformation: BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] ); break; case FileFullDirectoryInformation: BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] ); break; case FileNamesInformation: BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] ); break; case FileBothDirectoryInformation: BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] ); break; default: try_return(RC = STATUS_INVALID_INFO_CLASS); } // Some additional arguments that affect the FSD behavior ReturnSingleEntry = (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) ? TRUE : FALSE; UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFAcquireResourceShared(&(NtReqFcb->MainResource), TRUE); AcquiredFCB = TRUE; // We must determine the buffer pointer to be used. Since this // routine could either be invoked directly in the context of the // calling thread, or in the context of a worker thread, here is // a general way of determining what we should use. if(Irp->MdlAddress) { Buffer = (PUCHAR) MmGetSystemAddressForMdlSafer(Irp->MdlAddress); if(!Buffer) try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } else { Buffer = (PUCHAR) Irp->UserBuffer; if(!Buffer) try_return(RC = STATUS_INVALID_USER_BUFFER); } // The method of determining where to look from and what to look for is // unfortunately extremely confusing. However, here is a methodology // we broadly adopt: // (a) We have to maintain a search buffer per CCB structure. // (b) This search buffer is initialized the very first time // a query directory operation is performed using the file object. // (For the UDF FSD, the search buffer is stored in the // DirectorySearchPattern field) // However, the caller still has the option of "overriding" this stored // search pattern by supplying a new one in a query directory operation. if(PtrSearchPattern && PtrSearchPattern->Buffer && !(PtrSearchPattern->Buffer[PtrSearchPattern->Length/sizeof(WCHAR) - 1])) { PtrSearchPattern->Length -= sizeof(WCHAR); } if(IrpSp->Flags & SL_INDEX_SPECIFIED) { // Good idea from M$: we should continue search from NEXT item // when FileIndex specified... // Strange idea from M$: we should do it with EMPTY pattern... PtrSearchPattern = NULL; Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; } else if(PtrSearchPattern && PtrSearchPattern->Buffer && !UDFIsMatchAllMask(PtrSearchPattern, NULL) ) { Ccb->CCBFlags &= ~(UDF_CCB_MATCH_ALL | UDF_CCB_WILDCARD_PRESENT | UDF_CCB_CAN_BE_8_DOT_3); // Once we have validated the search pattern, we must // check whether we need to store this search pattern in // the CCB. if(Ccb->DirectorySearchPattern) { MyFreePool__(Ccb->DirectorySearchPattern->Buffer); MyFreePool__(Ccb->DirectorySearchPattern); Ccb->DirectorySearchPattern = NULL; } // This must be the very first query request. FirstTimeQuery = TRUE; // Now, allocate enough memory to contain the caller // supplied search pattern and fill in the DirectorySearchPattern // field in the CCB Ccb->DirectorySearchPattern = (PUNICODE_STRING)MyAllocatePool__(NonPagedPool,sizeof(UNICODE_STRING)); if(!(Ccb->DirectorySearchPattern)) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } Ccb->DirectorySearchPattern->Length = PtrSearchPattern->Length; Ccb->DirectorySearchPattern->MaximumLength = PtrSearchPattern->MaximumLength; Ccb->DirectorySearchPattern->Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool,PtrSearchPattern->MaximumLength); if(!(Ccb->DirectorySearchPattern->Buffer)) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } RtlCopyMemory(Ccb->DirectorySearchPattern->Buffer,PtrSearchPattern->Buffer, PtrSearchPattern->MaximumLength); if(FsRtlDoesNameContainWildCards(PtrSearchPattern)) { Ccb->CCBFlags |= UDF_CCB_WILDCARD_PRESENT; } else { UDFBuildHashEntry(Vcb, PtrSearchPattern, cur_hashes = &(Ccb->hashes), HASH_POSIX | HASH_ULFN); } if(UDFCanNameBeA8dot3(PtrSearchPattern)) Ccb->CCBFlags |= UDF_CCB_CAN_BE_8_DOT_3; } else if(!Ccb->DirectorySearchPattern && !(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) ) { // If the filename is not specified or is a single '*' then we will // match all names. FirstTimeQuery = TRUE; PtrSearchPattern = NULL; Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; } else { // The caller has not supplied any search pattern that we are // forced to use. However, the caller had previously supplied // a pattern (or we must have invented one) and we will use it. // This is definitely not the first query operation on this // directory using this particular file object. if(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) { PtrSearchPattern = NULL; /* if(Ccb->CurrentIndex) Ccb->CurrentIndex++;*/ } else { PtrSearchPattern = Ccb->DirectorySearchPattern; if(!(Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT)) { cur_hashes = &(Ccb->hashes); } } } if(IrpSp->Flags & SL_INDEX_SPECIFIED) { // Caller has told us wherefrom to begin. // We may need to round this to an appropriate directory entry // entry alignment value. NextMatch = pStackLocation->Parameters.QueryDirectory.FileIndex + 1; } else if(IrpSp->Flags & SL_RESTART_SCAN) { NextMatch = 0; } else { // Get the starting offset from the CCB. // Remember to update this value on our way out from this function. // But, do not update the CCB CurrentByteOffset field if our reach // the end of the directory (or get an error reading the directory) // while performing the search. NextMatch = Ccb->CurrentIndex + 1; // Last good index } FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT) ? UDF_FNM_FLAG_CONTAINS_WC : 0; // this is used only when mask is supplied FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CAN_BE_8_DOT_3) ? UDF_FNM_FLAG_CAN_BE_8D3 : 0; // This is an additional verifying if(!UDFIsADirectory(DirFileInfo)) { try_return(RC = STATUS_INVALID_PARAMETER); } hDirIndex = DirFileInfo->Dloc->DirIndex; if(!hDirIndex) { try_return(RC = STATUS_INVALID_PARAMETER); } RC = STATUS_SUCCESS; // Allocate buffer enough to save both DirInformation and FileName DirInformation = (PFILE_BOTH_DIR_INFORMATION)MyAllocatePool__(NonPagedPool, sizeof(FILE_BOTH_DIR_INFORMATION)+((ULONG)UDF_NAME_LEN*sizeof(WCHAR)) ); if(!DirInformation) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } CurrentOffset=0; BytesRemainingInBuffer = pStackLocation->Parameters.QueryDirectory.Length; RtlZeroMemory(Buffer,BytesRemainingInBuffer); if((!FirstTimeQuery) && !UDFDirIndex(hDirIndex, (uint_di)NextMatch) ) { try_return( RC = STATUS_NO_MORE_FILES); } // One final note though: // If we do not find a directory entry OR while searching we reach the // end of the directory, then the return code should be set as follows: // (a) If any files have been returned (i.e. ReturnSingleEntry was FALSE // and we did find at least one match), then return STATUS_SUCCESS // (b) If no entry is being returned then: // (i) If this is the first query i.e. FirstTimeQuery is TRUE // then return STATUS_NO_SUCH_FILE // (ii) Otherwise, return STATUS_NO_MORE_FILES while(TRUE) { // If the user had requested only a single match and we have // returned that, then we stop at this point. if(ReturnSingleEntry && AtLeastOneFound) { try_return(RC); } // We call UDFFindNextMatch to look down the next matching dirent. RC = UDFFindNextMatch(Vcb, hDirIndex,&NextMatch,PtrSearchPattern, FNM_Flags, cur_hashes, &DirNdx); // If we didn't receive next match, then we are at the end of the // directory. If we have returned any files, we exit with // success, otherwise we return STATUS_NO_MORE_FILES. if(!NT_SUCCESS(RC)) { RC = AtLeastOneFound ? STATUS_SUCCESS : (FirstTimeQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES); try_return(RC); } // We found at least one matching file entry AtLeastOneFound = TRUE; if(!NT_SUCCESS(RC = UDFFileDirInfoToNT(Vcb, DirNdx, DirInformation))) { // this happends when we can't allocate tmp buffers try_return(RC); } DirInformation->FileIndex = NextMatch; FileNameBytes = DirInformation->FileNameLength; if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) { // We haven't successfully transfered current data & // later NextMatch will be incremented. Thus we should // prevent loosing information in such a way: if(NextMatch) NextMatch --; // If this won't fit and we have returned a previous entry then just // return STATUS_SUCCESS. Otherwise // use a status code of STATUS_BUFFER_OVERFLOW. if(CurrentOffset) { try_return(RC = STATUS_SUCCESS); } // strange policy... ReturnSingleEntry = TRUE; FileNameBytes = BaseLength + FileNameBytes - BytesRemainingInBuffer; RC = STATUS_BUFFER_OVERFLOW; } // Now we have an entry to return to our caller. // We'll case on the type of information requested and fill up // the user buffer if everything fits. switch (FileInformationClass) { case FileBothDirectoryInformation: case FileFullDirectoryInformation: case FileDirectoryInformation: BothDirInformation = (PFILE_BOTH_DIR_INFORMATION)(Buffer + CurrentOffset); RtlCopyMemory(BothDirInformation,DirInformation,BaseLength); BothDirInformation->FileIndex = NextMatch; BothDirInformation->FileNameLength = FileNameBytes; break; case FileNamesInformation: NamesInfo = (PFILE_NAMES_INFORMATION)(Buffer + CurrentOffset); NamesInfo->FileIndex = NextMatch; NamesInfo->FileNameLength = FileNameBytes; break; default: break; } if (FileNameBytes) { // This is a Unicode name, we can copy the bytes directly. RtlCopyMemory( (PVOID)(Buffer + CurrentOffset + BaseLength), DirInformation->FileName, FileNameBytes ); } Information = CurrentOffset + BaseLength + FileNameBytes; // ((..._INFORMATION)(PointerToPreviousEntryInBuffer))->NextEntryOffset = CurrentOffset - LastOffset; *((PULONG)(Buffer+LastOffset)) = CurrentOffset - LastOffset; // Set up our variables for the next dirent. FirstTimeQuery = FALSE; LastOffset = CurrentOffset; PrevMatch = NextMatch; NextMatch++; CurrentOffset = UDFQuadAlign(Information); BytesRemainingInBuffer = BufferLength - CurrentOffset; } try_exit: NOTHING; } _SEH2_FINALLY { if (PostRequest) { if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); } // Map the users buffer and then post the request. RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, BufferLength); ASSERT(NT_SUCCESS(RC)); RC = UDFPostRequest(PtrIrpContext, Irp); } else { #ifdef UDF_DBG if(!NT_SUCCESS(RC)) { UDFPrint((" Not found\n")); } #endif // UDF_DBG // Remember to update the CurrentByteOffset field in the CCB if required. if(Ccb) Ccb->CurrentIndex = PrevMatch; if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); } if (!_SEH2_AbnormalTermination()) { // complete the IRP Irp->IoStatus.Status = RC; Irp->IoStatus.Information = Information; IoCompleteRequest(Irp, IO_DISK_INCREMENT); // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); } } if(SearchPattern.Buffer) RtlFreeUnicodeString(&SearchPattern); if(DirInformation) MyFreePool__(DirInformation); } _SEH2_END; return(RC); } // end UDFQueryDirectory()
/************************************************************************* * * Function: UDFNotifyChangeDirectory() * * Description: * Handle the notify request. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS NTAPI UDFNotifyChangeDirectory( PtrUDFIrpContext PtrIrpContext, PIRP Irp, PIO_STACK_LOCATION IrpSp, PFILE_OBJECT FileObject, PtrUDFFCB Fcb, PtrUDFCCB Ccb ) { NTSTATUS RC = STATUS_SUCCESS; BOOLEAN CompleteRequest = FALSE; BOOLEAN PostRequest = FALSE; PtrUDFNTRequiredFCB NtReqFcb = NULL; BOOLEAN CanWait = FALSE; ULONG CompletionFilter = 0; BOOLEAN WatchTree = FALSE; PVCB Vcb = NULL; BOOLEAN AcquiredFCB = FALSE; PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp; UDFPrint(("UDFNotifyChangeDirectory\n")); _SEH2_TRY { // Validate the sent-in FCB if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { CompleteRequest = TRUE; try_return(RC = STATUS_INVALID_PARAMETER); } NtReqFcb = Fcb->NTRequiredFCB; CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; Vcb = Fcb->Vcb; // Acquire the FCB resource shared UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) { PostRequest = TRUE; try_return(RC = STATUS_PENDING); } AcquiredFCB = TRUE; // If the file is marked as DELETE_PENDING then complete this // request immediately. if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) { ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); try_return(RC = STATUS_DELETE_PENDING); } // Obtain some parameters sent by the caller CompletionFilter = pStackLocation ->Parameters.NotifyDirectory.CompletionFilter; WatchTree = (IrpSp->Flags & SL_WATCH_TREE) ? TRUE : FALSE; // If we wish to capture the subject context, we can do so as // follows: // { // PSECURITY_SUBJECT_CONTEXT SubjectContext; // SubjectContext = MyAllocatePool__(PagedPool, // sizeof(SECURITY_SUBJECT_CONTEXT)); // SeCaptureSubjectContext(SubjectContext); // } FsRtlNotifyFullChangeDirectory(Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP), (PVOID)Ccb, (Fcb->FileInfo->ParentFile) ? (PSTRING)&(Fcb->FCBName->ObjectName) : (PSTRING)&(UDFGlobalData.UnicodeStrRoot), WatchTree, FALSE, CompletionFilter, Irp, NULL, // UDFTraverseAccessCheck(...) ? NULL); // SubjectContext ? RC = STATUS_PENDING; try_exit: NOTHING; } _SEH2_FINALLY { if (PostRequest) { // Perform appropriate related post processing here if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); AcquiredFCB = FALSE; } RC = UDFPostRequest(PtrIrpContext, Irp); } else if (CompleteRequest) { if (!_SEH2_AbnormalTermination()) { Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); // complete the IRP IoCompleteRequest(Irp, IO_DISK_INCREMENT); } } else { // Simply free up the IrpContext since the IRP has been queued if (!_SEH2_AbnormalTermination()) UDFReleaseIrpContext(PtrIrpContext); } // Release the FCB resources if acquired. if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); AcquiredFCB = FALSE; } } _SEH2_END; return(RC); } // end UDFNotifyChangeDirectory()
/* 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()