/* This routine initializes DirIndex array */ PDIR_INDEX_HDR UDFDirIndexAlloc( IN uint_di i ) { uint_di j,k; PDIR_INDEX_HDR hDirNdx; PDIR_INDEX_ITEM* FrameList; if(!i) return NULL; #ifdef UDF_LIMIT_DIR_SIZE if(i>UDF_DIR_INDEX_FRAME) return NULL; #endif //UDF_LIMIT_DIR_SIZE j = i >> UDF_DIR_INDEX_FRAME_SH; i &= (UDF_DIR_INDEX_FRAME-1); hDirNdx = (PDIR_INDEX_HDR)MyAllocatePoolTag__(UDF_DIR_INDEX_MT, sizeof(DIR_INDEX_HDR)+(j+(i!=0))*sizeof(PDIR_INDEX_ITEM), MEM_DIR_HDR_TAG); if(!hDirNdx) return NULL; RtlZeroMemory(hDirNdx, sizeof(DIR_INDEX_HDR)); FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1); for(k=0; k<j; k++, FrameList++) { (*FrameList) = (PDIR_INDEX_ITEM)MyAllocatePoolTag__(UDF_DIR_INDEX_MT, UDF_DIR_INDEX_FRAME*sizeof(DIR_INDEX_ITEM), MEM_DIR_NDX_TAG); if(!(*FrameList)) { free_hdi: // item pointet by FrameList is NULL, it could not be allocated while(k) { k--; FrameList--; MyFreePool__(*FrameList); } MyFreePool__(hDirNdx); return NULL; } RtlZeroMemory((*FrameList), UDF_DIR_INDEX_FRAME*sizeof(DIR_INDEX_ITEM)); } if(i) { (*FrameList) = (PDIR_INDEX_ITEM)MyAllocatePoolTag__(UDF_DIR_INDEX_MT, AlignDirIndex(i)*sizeof(DIR_INDEX_ITEM), MEM_DIR_NDX_TAG); if(!(*FrameList)) goto free_hdi; RtlZeroMemory((*FrameList), i*sizeof(DIR_INDEX_ITEM)); } hDirNdx->FrameCount = j+(i!=0); hDirNdx->LastFrameCount = i ? i : UDF_DIR_INDEX_FRAME; return hDirNdx; } // UDFDirIndexAlloc()
/* This routine releases DirIndex array */ void UDFDirIndexFree( PDIR_INDEX_HDR hDirNdx ) { uint32 k; PDIR_INDEX_ITEM* FrameList; FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1); if(!hDirNdx) return; for(k=0; k<hDirNdx->FrameCount; k++, FrameList++) { if(*FrameList) MyFreePool__(*FrameList); } MyFreePool__(hDirNdx); } // UDFDirIndexFree();
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()
/************************************************************************* * * Function: UDFCommonShutdown() * * Description: * The actual work is performed here. Basically, all we do here is * internally invoke a flush on all mounted logical volumes. This, in * tuen, will result in all open file streams being flushed to disk. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: Irrelevant * *************************************************************************/ NTSTATUS UDFCommonShutdown( PtrUDFIrpContext PtrIrpContext, PIRP Irp ) { NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp = NULL; PVCB Vcb; PLIST_ENTRY Link; PPREVENT_MEDIA_REMOVAL_USER_IN Buf = NULL; LARGE_INTEGER delay; KdPrint(("UDFCommonShutdown\n")); _SEH2_TRY { // First, get a pointer to the current I/O stack location IrpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(IrpSp); Buf = (PPREVENT_MEDIA_REMOVAL_USER_IN)MyAllocatePool__(NonPagedPool, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN)); if(!Buf) try_return(RC = STATUS_INSUFFICIENT_RESOURCES); // (a) Block all new "mount volume" requests by acquiring an appropriate // global resource/lock. // (b) Go through your linked list of mounted logical volumes and for // each such volume, do the following: // (i) acquire the volume resource exclusively // (ii) invoke UDFFlushLogicalVolume() (internally) to flush the // open data streams belonging to the volume from the system // cache // (iii) Invoke the physical/virtual/logical target device object // on which the volume is mounted and inform this device // about the shutdown request (Use IoBuildSynchronouFsdRequest() // to create an IRP with MajorFunction = IRP_MJ_SHUTDOWN that you // will then issue to the target device object). // (iv) Wait for the completion of the shutdown processing by the target // device object // (v) Release the VCB resource we will have acquired in (i) above. // Acquire GlobalDataResource UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); // Walk through all of the Vcb's attached to the global data. Link = UDFGlobalData.VCBQueue.Flink; while (Link != &(UDFGlobalData.VCBQueue)) { // Get 'next' Vcb Vcb = CONTAINING_RECORD( Link, VCB, NextVCB ); // Move to the next link now since the current Vcb may be deleted. Link = Link->Flink; ASSERT(Link != Link->Flink); if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN)) { #ifdef UDF_DELAYED_CLOSE UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); KdPrint((" UDFCommonShutdown: set UDF_VCB_FLAGS_NO_DELAYED_CLOSE\n")); Vcb->VCBFlags |= UDF_VCB_FLAGS_NO_DELAYED_CLOSE; UDFReleaseResource(&(Vcb->VCBResource)); #endif //UDF_DELAYED_CLOSE // Note: UDFCloseAllDelayed() doesn't acquire DelayedCloseResource if // GlobalDataResource is already acquired. Thus for now we should // release GlobalDataResource and re-acquire it later. UDFReleaseResource( &(UDFGlobalData.GlobalDataResource) ); if(Vcb->RootDirFCB && Vcb->RootDirFCB->FileInfo) { KdPrint((" UDFCommonShutdown: UDFCloseAllSystemDelayedInDir\n")); RC = UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); ASSERT(OS_SUCCESS(RC)); } #ifdef UDF_DELAYED_CLOSE UDFCloseAllDelayed(Vcb); // UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource)); #endif //UDF_DELAYED_CLOSE // re-acquire GlobalDataResource UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); // disable Eject Waiter UDFStopEjectWaiter(Vcb); // Acquire Vcb resource UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); ASSERT(!Vcb->OverflowQueueCount); if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN)) { UDFDoDismountSequence(Vcb, Buf, FALSE); if(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) { // let drive flush all data before reset delay.QuadPart = -10000000; // 1 sec KeDelayExecutionThread(KernelMode, FALSE, &delay); } Vcb->VCBFlags |= (UDF_VCB_FLAGS_SHUTDOWN | UDF_VCB_FLAGS_VOLUME_READ_ONLY); } UDFReleaseResource(&(Vcb->VCBResource)); } } // Once we have processed all the mounted logical volumes, we can release // all acquired global resources and leave (in peace :-) UDFReleaseResource( &(UDFGlobalData.GlobalDataResource) ); RC = STATUS_SUCCESS; try_exit: NOTHING; } _SEH2_FINALLY { if(Buf) MyFreePool__(Buf); 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); } } _SEH2_END; // end of "__finally" processing return(RC); } // end UDFCommonShutdown()
/* Routine Description: This routine performs the verify volume operation. It is responsible for either completing of enqueuing the input Irp. Arguments: Irp - Supplies the Irp to process Return Value: NTSTATUS - The return status for the operation --*/ NTSTATUS UDFVerifyVolume( IN PIRP Irp ) { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); PVPB Vpb = IrpSp->Parameters.VerifyVolume.Vpb; PVCB Vcb = (PVCB)IrpSp->Parameters.VerifyVolume.DeviceObject->DeviceExtension; PVCB NewVcb = NULL; IO_STATUS_BLOCK Iosb; ULONG MediaChangeCount = 0; NTSTATUS RC; ULONG Mode; BOOLEAN UnsafeIoctl = (Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL) ? TRUE : FALSE; // Update the real device in the IrpContext from the Vpb. There was no available // file object when the IrpContext was created. // IrpContext->RealDevice = Vpb->RealDevice; KdPrint(("UDFVerifyVolume:\n")); // Acquire shared global access, the termination handler for the // following try statement will free the access. UDFAcquireResourceShared(&(UDFGlobalData.GlobalDataResource),TRUE); UDFAcquireResourceExclusive(&(Vcb->VCBResource),TRUE); _SEH2_TRY { KdPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified)); // Check if the real device still needs to be verified. If it doesn't // then obviously someone beat us here and already did the work // so complete the verify irp with success. Otherwise reenable // the real device and get to work. if( !(Vpb->RealDevice->Flags & DO_VERIFY_VOLUME) && ((Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_LOCKED) && !UnsafeIoctl) ) { KdPrint(("UDFVerifyVolume: STATUS_SUCCESS (1)\n")); try_return(RC = STATUS_SUCCESS); } Vcb->VCBFlags &= ~UDF_VCB_FLAGS_UNSAFE_IOCTL; // Verify that there is a disk here. RC = UDFPhSendIOCTL( IOCTL_STORAGE_CHECK_VERIFY, Vcb->TargetDeviceObject, NULL,0, &MediaChangeCount,sizeof(ULONG), TRUE,&Iosb ); if(!NT_SUCCESS( RC )) { // If we will allow a raw mount then return WRONG_VOLUME to // allow the volume to be mounted by raw. if(FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT )) { KdPrint(("UDFVerifyVolume: STATUS_WRONG_VOLUME (1)\n")); RC = STATUS_WRONG_VOLUME; } if(UDFIsRawDevice(RC)) { KdPrint(("UDFVerifyVolume: STATUS_WRONG_VOLUME (2)\n")); RC = STATUS_WRONG_VOLUME; } try_return( RC ); } if(Iosb.Information != sizeof(ULONG)) { // Be safe about the count in case the driver didn't fill it in MediaChangeCount = 0; } KdPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified)); KdPrint(("UDFVerifyVolume: MediaChangeCount=%x, Vcb->MediaChangeCount=%x, UnsafeIoctl=%x\n", MediaChangeCount, Vcb->MediaChangeCount, UnsafeIoctl)); // Verify that the device actually saw a change. If the driver does not // support the MCC, then we must verify the volume in any case. if(MediaChangeCount == 0 || (Vcb->MediaChangeCount != MediaChangeCount) || UnsafeIoctl ) { KdPrint(("UDFVerifyVolume: compare\n")); NewVcb = (PVCB)MyAllocatePool__(NonPagedPool,sizeof(VCB)); if(!NewVcb) try_return(RC=STATUS_INSUFFICIENT_RESOURCES); RtlZeroMemory(NewVcb,sizeof(VCB)); NewVcb->TargetDeviceObject = Vcb->TargetDeviceObject; NewVcb->Vpb = Vpb; // Set the removable media flag based on the real device's // characteristics if(Vpb->RealDevice->Characteristics & FILE_REMOVABLE_MEDIA) { UDFSetFlag( NewVcb->VCBFlags, UDF_VCB_FLAGS_REMOVABLE_MEDIA ); } RC = UDFGetDiskInfo(NewVcb->TargetDeviceObject,NewVcb); if(!NT_SUCCESS(RC)) try_return(RC); // Prevent modification attempts durring Verify NewVcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY | UDF_VCB_FLAGS_MEDIA_READ_ONLY; // Compare physical parameters (phase 1) KdPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified)); RC = UDFCompareVcb(Vcb,NewVcb, TRUE); if(!NT_SUCCESS(RC)) try_return(RC); if((Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) && Vcb->MountPhErrorCount > MOUNT_ERR_THRESHOLD ) { KdPrint(("UDFVerifyVolume: it was very BAD volume. Do not perform Logical check\n")); goto skip_logical_check; } // Initialize internal cache // in *** READ ONLY *** mode Mode = WCACHE_MODE_ROM; RC = WCacheInit__(&(NewVcb->FastCache), UDFGlobalData.WCacheMaxFrames, UDFGlobalData.WCacheMaxBlocks, NewVcb->WriteBlockSize, 5, NewVcb->BlockSizeBits, UDFGlobalData.WCacheBlocksPerFrameSh, 0/*NewVcb->FirstLBA*/, NewVcb->LastPossibleLBA, Mode, /*WCACHE_CACHE_WHOLE_PACKET*/ 0 | (Vcb->DoNotCompareBeforeWrite ? WCACHE_DO_NOT_COMPARE : 0) | WCACHE_MARK_BAD_BLOCKS | WCACHE_RO_BAD_BLOCKS, // speed up mount on bad disks UDFGlobalData.WCacheFramesToKeepFree, UDFTWrite, UDFTRead, #ifdef UDF_ASYNC_IO UDFTWriteAsync, UDFTReadAsync, #else //UDF_ASYNC_IO NULL, NULL, #endif //UDF_ASYNC_IO UDFIsBlockAllocated, UDFUpdateVAT, UDFWCacheErrorHandler); if(!NT_SUCCESS(RC)) try_return(RC); KdPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified)); RC = UDFGetDiskInfoAndVerify(NewVcb->TargetDeviceObject,NewVcb); KdPrint((" NewVcb->NSRDesc=%x\n", NewVcb->NSRDesc)); if(!NT_SUCCESS(RC)) { if((Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) && (NewVcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) && !(NewVcb->NSRDesc & VRS_ISO9660_FOUND)) { KdPrint(("UDFVerifyVolume: both are RAW -> remount\n", Vcb->Modified)); RC = STATUS_SUCCESS; goto skip_logical_check; } if(RC == STATUS_UNRECOGNIZED_VOLUME) { try_return(RC = STATUS_WRONG_VOLUME); } try_return(RC); } WCacheChFlags__(&(Vcb->FastCache), WCACHE_CACHE_WHOLE_PACKET, // enable cache whole packet WCACHE_MARK_BAD_BLOCKS | WCACHE_RO_BAD_BLOCKS); // let user retry request on Bad Blocks NewVcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_MOUNTED; // Compare logical parameters (phase 2) KdPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified)); RC = UDFCompareVcb(Vcb,NewVcb, FALSE); if(!NT_SUCCESS(RC)) try_return(RC); // We have unitialized WCache, so it is better to // force MOUNT_VOLUME call if(!WCacheIsInitialized__(&(Vcb->FastCache))) try_return(RC = STATUS_WRONG_VOLUME); skip_logical_check:; } KdPrint(("UDFVerifyVolume: compared\n")); KdPrint(("UDFVerifyVolume: Modified=%d\n", Vcb->Modified)); if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED)) { KdPrint(("UDFVerifyVolume: set UDF_VCB_FLAGS_VOLUME_MOUNTED\n")); Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_MOUNTED; Vcb->SoftEjectReq = FALSE; } UDFClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME ); try_exit: NOTHING; } _SEH2_FINALLY { // Update the media change count to note that we have verified the volume // at this value Vcb->MediaChangeCount = MediaChangeCount; // If we got the wrong volume, mark the Vcb as not mounted. if(RC == STATUS_WRONG_VOLUME) { KdPrint(("UDFVerifyVolume: clear UDF_VCB_FLAGS_VOLUME_MOUNTED\n")); Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED; Vcb->WriteSecurity = FALSE; // ASSERT(!(Vcb->EjectWaiter)); if(Vcb->EjectWaiter) { UDFReleaseResource(&(Vcb->VCBResource)); UDFStopEjectWaiter(Vcb); UDFAcquireResourceExclusive(&(Vcb->VCBResource),TRUE); } } else if(NT_SUCCESS(RC) && (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)){ BOOLEAN CacheInitialized = FALSE; KdPrint((" !!! VerifyVolume - QUICK REMOUNT !!!\n")); // Initialize internal cache CacheInitialized = WCacheIsInitialized__(&(Vcb->FastCache)); if(!CacheInitialized) { Mode = WCACHE_MODE_ROM; RC = WCacheInit__(&(Vcb->FastCache), Vcb->WCacheMaxFrames, Vcb->WCacheMaxBlocks, Vcb->WriteBlockSize, 5, Vcb->BlockSizeBits, Vcb->WCacheBlocksPerFrameSh, 0/*Vcb->FirstLBA*/, Vcb->LastPossibleLBA, Mode, /*WCACHE_CACHE_WHOLE_PACKET*/ 0 | (Vcb->DoNotCompareBeforeWrite ? WCACHE_DO_NOT_COMPARE : 0) | (Vcb->CacheChainedIo ? WCACHE_CHAINED_IO : 0), Vcb->WCacheFramesToKeepFree, // UDFTWrite, UDFTRead, UDFTWriteVerify, UDFTReadVerify, #ifdef UDF_ASYNC_IO UDFTWriteAsync, UDFTReadAsync, #else //UDF_ASYNC_IO NULL, NULL, #endif //UDF_ASYNC_IO UDFIsBlockAllocated, UDFUpdateVAT, UDFWCacheErrorHandler); } if(NT_SUCCESS(RC)) { if(!Vcb->VerifyCtx.VInited) { RC = UDFVInit(Vcb); } } if(NT_SUCCESS(RC)) { if(!CacheInitialized) { if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY)) { if(!Vcb->CDR_Mode) { if((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_DISK) || CdrwMediaClassEx_IsRAM(Vcb->MediaClassEx)) { KdPrint(("UDFMountVolume: RAM mode\n")); Mode = WCACHE_MODE_RAM; } else { KdPrint(("UDFMountVolume: RW mode\n")); Mode = WCACHE_MODE_RW; } /* if(FsDeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) { } else { Vcb->WriteSecurity = TRUE; }*/ } else { Mode = WCACHE_MODE_R; } } WCacheSetMode__(&(Vcb->FastCache), Mode); WCacheChFlags__(&(Vcb->FastCache), WCACHE_CACHE_WHOLE_PACKET, // enable cache whole packet WCACHE_MARK_BAD_BLOCKS | WCACHE_RO_BAD_BLOCKS); // let user retry request on Bad Blocks } // we can't record ACL on old format disks if(!UDFNtAclSupported(Vcb)) { Vcb->WriteSecurity = FALSE; Vcb->UseExtendedFE = FALSE; } KdPrint(("UDFVerifyVolume: try start EjectWaiter\n")); RC = UDFStartEjectWaiter(Vcb); if(!NT_SUCCESS(RC)) { KdPrint(("UDFVerifyVolume: start EjectWaiter failed\n")); Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED; Vcb->WriteSecurity = FALSE; } } } if(NewVcb) { // Release internal cache KdPrint(("UDFVerifyVolume: delete NewVcb\n")); WCacheFlushAll__(&(NewVcb->FastCache),NewVcb); WCacheRelease__(&(NewVcb->FastCache)); ASSERT(!(NewVcb->EjectWaiter)); // Waiter thread should be already stopped // if MediaChangeCount have changed ASSERT(!(Vcb->EjectWaiter)); UDFCleanupVCB(NewVcb); MyFreePool__(NewVcb); } UDFReleaseResource(&(Vcb->VCBResource)); UDFReleaseResource(&(UDFGlobalData.GlobalDataResource)); } _SEH2_END; // Complete the request if no exception. Irp->IoStatus.Information = 0; Irp->IoStatus.Status = RC; IoCompleteRequest(Irp,IO_DISK_INCREMENT); KdPrint(("UDFVerifyVolume: RC = %x\n", RC)); return RC; } // end UDFVerifyVolume ()
/* Thisd routine truncates DirIndex array */ OSSTATUS UDFDirIndexTrunc( IN PDIR_INDEX_HDR* _hDirNdx, IN uint_di d // decrement ) { uint_di j,k; if(d > UDF_DIR_INDEX_FRAME) { OSSTATUS status; while(d) { k = (d > UDF_DIR_INDEX_FRAME) ? UDF_DIR_INDEX_FRAME : d; if(!OS_SUCCESS(status = UDFDirIndexTrunc(_hDirNdx, k))) { return status; } d -= k; } return STATUS_SUCCESS; } PDIR_INDEX_HDR hDirNdx = *_hDirNdx; PDIR_INDEX_ITEM* FrameList; j = UDF_DIR_INDEX_FRAME+hDirNdx->LastFrameCount-d; FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1); k = hDirNdx->FrameCount-1; if(j <= UDF_DIR_INDEX_FRAME) { // free last frame if(!k && (j < 2)) { // someone tries to trunc. residual entries... return STATUS_INVALID_PARAMETER; } MyFreePool__(FrameList[k]); FrameList[k] = NULL; hDirNdx->LastFrameCount = UDF_DIR_INDEX_FRAME; hDirNdx->FrameCount--; // Truncate new last frame if(!MyReallocPool__((int8*)(FrameList[k-1]), UDF_DIR_INDEX_FRAME*sizeof(DIR_INDEX_ITEM), (int8**)(&(FrameList[k-1])), AlignDirIndex(j)*sizeof(DIR_INDEX_ITEM) ) ) return STATUS_INSUFFICIENT_RESOURCES; hDirNdx->LastFrameCount = j; // Truncate header if(!MyReallocPool__((int8*)hDirNdx, sizeof(DIR_INDEX_HDR) + (k+1)*sizeof(PDIR_INDEX_ITEM), (int8**)(&hDirNdx), sizeof(DIR_INDEX_HDR) + k*sizeof(PDIR_INDEX_ITEM) ) ) return STATUS_INSUFFICIENT_RESOURCES; (*_hDirNdx) = hDirNdx; } else { j -= UDF_DIR_INDEX_FRAME; if(!k && (j < 2)) { // someone tries to trunc. residual entries... return STATUS_INVALID_PARAMETER; } if(!MyReallocPool__((int8*)(FrameList[k]), AlignDirIndex(hDirNdx->LastFrameCount)*sizeof(DIR_INDEX_ITEM), (int8**)(&(FrameList[k])), AlignDirIndex(j)*sizeof(DIR_INDEX_ITEM) ) ) return STATUS_INSUFFICIENT_RESOURCES; hDirNdx->LastFrameCount = j; } return STATUS_SUCCESS; } // end UDFDirIndexTrunc()
/************************************************************************* * * 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()
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 {
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()
/* 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()