/* This routine removes request from Delayed Close queue. It operates until reach lower threshold */ VOID UDFDelayedClose( PVOID unused ) { PLIST_ENTRY Entry; PtrUDFIrpContextLite NextIrpContextLite; AdPrint((" UDFDelayedClose\n")); // Acquire DelayedCloseResource UDFAcquireResourceExclusive(&(UDFGlobalData.DelayedCloseResource), TRUE); while (UDFGlobalData.ReduceDelayedClose && (UDFGlobalData.DelayedCloseCount > UDFGlobalData.MinDelayedCloseCount)) { Entry = UDFGlobalData.DelayedCloseQueue.Flink; if (!IsListEmpty(Entry)) { // Extract the IrpContext. NextIrpContextLite = CONTAINING_RECORD( Entry, UDFIrpContextLite, DelayedCloseLinks ); RemoveEntryList( Entry ); UDFGlobalData.DelayedCloseCount--; UDFDoDelayedClose(NextIrpContextLite); } else { BrutePoint(); } } while (UDFGlobalData.ReduceDirDelayedClose && (UDFGlobalData.DirDelayedCloseCount > UDFGlobalData.MinDirDelayedCloseCount)) { Entry = UDFGlobalData.DirDelayedCloseQueue.Flink; if (!IsListEmpty(Entry)) { // Extract the IrpContext. NextIrpContextLite = CONTAINING_RECORD( Entry, UDFIrpContextLite, DelayedCloseLinks ); RemoveEntryList( Entry ); UDFGlobalData.DirDelayedCloseCount--; UDFDoDelayedClose(NextIrpContextLite); } else { BrutePoint(); } } UDFGlobalData.FspCloseActive = FALSE; UDFGlobalData.ReduceDelayedClose = FALSE; UDFGlobalData.ReduceDirDelayedClose = FALSE; // Release DelayedCloseResource UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource)); return; } // end UDFDelayedClose()
/* This routine performs Close operation for all files from Delayed Close queue. */ VOID UDFCloseAllDelayed( IN PVCB Vcb ) { PLIST_ENTRY Entry; PtrUDFIrpContextLite NextIrpContextLite; BOOLEAN GlobalDataAcquired = FALSE; AdPrint((" UDFCloseAllDelayed\n")); // Acquire DelayedCloseResource if (!ExIsResourceAcquiredExclusive(&UDFGlobalData.GlobalDataResource)) { UDFAcquireResourceExclusive(&(UDFGlobalData.DelayedCloseResource), TRUE); GlobalDataAcquired = TRUE; } Entry = UDFGlobalData.DelayedCloseQueue.Flink; while (Entry != &UDFGlobalData.DelayedCloseQueue) { // Extract the IrpContext. NextIrpContextLite = CONTAINING_RECORD( Entry, UDFIrpContextLite, DelayedCloseLinks ); Entry = Entry->Flink; if (NextIrpContextLite->Fcb->Vcb == Vcb) { RemoveEntryList( &(NextIrpContextLite->DelayedCloseLinks) ); UDFGlobalData.DelayedCloseCount--; UDFDoDelayedClose(NextIrpContextLite); } } Entry = UDFGlobalData.DirDelayedCloseQueue.Flink; while (Entry != &UDFGlobalData.DirDelayedCloseQueue) { // Extract the IrpContext. NextIrpContextLite = CONTAINING_RECORD( Entry, UDFIrpContextLite, DelayedCloseLinks ); Entry = Entry->Flink; if (NextIrpContextLite->Fcb->Vcb == Vcb) { RemoveEntryList( &(NextIrpContextLite->DelayedCloseLinks) ); UDFGlobalData.DirDelayedCloseCount--; UDFDoDelayedClose(NextIrpContextLite); } } // Release DelayedCloseResource if(GlobalDataAcquired) UDFReleaseResource(&(UDFGlobalData.DelayedCloseResource)); } // end UDFCloseAllDelayed()
/************************************************************************* * * 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 is called when all of the user references to a volume are gone. We will initiate all of the teardown any system resources. If all of the references to this volume are gone at the end of this routine then we will complete the teardown of this Vcb and mark the current Vpb as not mounted. Otherwise we will allocated a new Vpb for this device and keep the current Vpb attached to the Vcb. Arguments: Vcb - Vcb for the volume to dismount. Return Value: BOOLEAN - TRUE if we didn't delete the Vcb, FALSE otherwise. */ BOOLEAN UDFDismountVcb( IN PVCB Vcb, IN BOOLEAN VcbAcquired ) { PVPB OldVpb; PVPB NewVpb; BOOLEAN VcbPresent = TRUE; KIRQL SavedIrql; BOOLEAN FinalReference; KdPrint(("UDFDismountVcb:\n")); // We should only take this path once. ASSERT( !(Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED) ); // Mark the Vcb as DismountInProgress. Vcb->VCBFlags |= UDF_VCB_FLAGS_BEING_DISMOUNTED; // Allocate a new Vpb in case we will need it. NewVpb = (PVPB)DbgAllocatePoolWithTag( NonPagedPool, sizeof( VPB ), 'bpvU' ); if(!NewVpb) { Vcb->VCBFlags &= ~UDF_VCB_FLAGS_BEING_DISMOUNTED; return TRUE; } RtlZeroMemory( NewVpb, sizeof(VPB) ); OldVpb = Vcb->Vpb; // Remove the mount volume reference. UDFCloseResidual(Vcb); // the only residual reference is cleaned above // Acquire the Vpb spinlock to check for Vpb references. IoAcquireVpbSpinLock(&SavedIrql); // Remember if this is the last reference on this Vcb. We incremented // the count on the Vpb earlier so we get one last crack it. If our // reference has gone to zero but the vpb reference count is greater // than zero then the Io system will be responsible for deleting the // Vpb. FinalReference = (BOOLEAN)(OldVpb->ReferenceCount == 1); // There is a reference count in the Vpb and in the Vcb. We have // incremented the reference count in the Vpb to make sure that // we have last crack at it. If this is a failed mount then we // want to return the Vpb to the IO system to use for the next // mount request. if (OldVpb->RealDevice->Vpb == OldVpb) { // If not the final reference then swap out the Vpb. if (!FinalReference) { NewVpb->Type = IO_TYPE_VPB; NewVpb->Size = sizeof( VPB ); NewVpb->RealDevice = OldVpb->RealDevice; NewVpb->RealDevice->Vpb = NewVpb; NewVpb = NULL; IoReleaseVpbSpinLock(SavedIrql); // We want to leave the Vpb for the IO system. Mark it // as being not mounted. Go ahead and delete the Vcb as // well. } else { // Make sure to remove the last reference on the Vpb. OldVpb->ReferenceCount--; OldVpb->DeviceObject = NULL; Vcb->Vpb->Flags &= ~VPB_MOUNTED; // Clear the Vpb flag so we know not to delete it. Vcb->Vpb = NULL; IoReleaseVpbSpinLock(SavedIrql); if(VcbAcquired) UDFReleaseResource(&(Vcb->VCBResource)); UDFStopEjectWaiter(Vcb); UDFReleaseVCB(Vcb); VcbPresent = FALSE; } // Someone has already swapped in a new Vpb. If this is the final reference // then the file system is responsible for deleting the Vpb. } else if (FinalReference) { // Make sure to remove the last reference on the Vpb. OldVpb->ReferenceCount--; IoReleaseVpbSpinLock( SavedIrql ); if(VcbAcquired) UDFReleaseResource(&(Vcb->VCBResource)); UDFStopEjectWaiter(Vcb); UDFReleaseVCB(Vcb); VcbPresent = FALSE; // The current Vpb is no longer the Vpb for the device (the IO system // has already allocated a new one). We leave our reference in the // Vpb and will be responsible for deleting it at a later time. } else { OldVpb->DeviceObject = NULL; Vcb->Vpb->Flags &= ~VPB_MOUNTED; IoReleaseVpbSpinLock( SavedIrql ); } // Deallocate the new Vpb if we don't need it. if (NewVpb != NULL) { DbgFreePool( NewVpb ); } // Let our caller know whether the Vcb is still present. return VcbPresent; } // end UDFDismountVcb()
/* Routine Description: This routine is called to check if a volume is ready for dismount. This occurs when only file system references are left on the volume. If the dismount is not currently underway and the user reference count has gone to zero then we can begin the dismount. If the dismount is in progress and there are no references left on the volume (we check the Vpb for outstanding references as well to catch any create calls dispatched to the file system) then we can delete the Vcb. Arguments: Vcb - Vcb for the volume to try to dismount. */ BOOLEAN UDFCheckForDismount( IN PtrUDFIrpContext IrpContext, IN PVCB Vcb, IN BOOLEAN _VcbAcquired ) { BOOLEAN VcbPresent = TRUE; KIRQL SavedIrql; BOOLEAN VcbAcquired; ULONG ResidualReferenceCount; KdPrint(("UDFCheckForDismount:\n")); if(!Vcb) return FALSE; // GlobalDataResource is already acquired if(!_VcbAcquired) { VcbAcquired = UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE/*FALSE*/ ); if(!VcbAcquired) return TRUE; } else { VcbAcquired = TRUE; } if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && (IrpContext->TargetDeviceObject == Vcb->TargetDeviceObject)) { ResidualReferenceCount = 2; } else { ResidualReferenceCount = 1; } // If the dismount is not already underway then check if the // user reference count has gone to zero. If so start the teardown // on the Vcb. if (!(Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) { if (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE) { VcbPresent = UDFDismountVcb(Vcb, VcbAcquired); } VcbAcquired = VcbAcquired && VcbPresent; // If the teardown is underway and there are absolutely no references // remaining then delete the Vcb. References here include the // references in the Vcb and Vpb. } else if (!(Vcb->VCBOpenCount)) { IoAcquireVpbSpinLock( &SavedIrql ); // If there are no file objects and no reference counts in the // Vpb we can delete the Vcb. Don't forget that we have the // last reference in the Vpb. if (Vcb->Vpb->ReferenceCount <= ResidualReferenceCount) { IoReleaseVpbSpinLock( SavedIrql ); if(VcbAcquired) UDFReleaseResource(&(Vcb->VCBResource)); UDFStopEjectWaiter(Vcb); UDFReleaseVCB(Vcb); VcbAcquired = VcbPresent = FALSE; } else { IoReleaseVpbSpinLock( SavedIrql ); } } // Release any resources still acquired. if (!_VcbAcquired && VcbAcquired) { UDFReleaseResource(&(Vcb->VCBResource)); } return VcbPresent; } // end UDFCheckForDismount()
/* Routine Description: This routines performs an IoVerifyVolume operation and takes the appropriate action. If the verify is successful then we send the originating Irp off to an Ex Worker Thread. This routine is called from the exception handler. No file system resources are held when this routine is called. Arguments: Irp - The irp to send off after all is well and done. Device - The real device needing verification. */ NTSTATUS UDFPerformVerify( IN PtrUDFIrpContext IrpContext, IN PIRP Irp, IN PDEVICE_OBJECT DeviceToVerify ) { PVCB Vcb; NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; KdPrint(("UDFPerformVerify:\n")); if(!IrpContext) return STATUS_INVALID_PARAMETER; if(!Irp) return STATUS_INVALID_PARAMETER; // Check if this Irp has a status of Verify required and if it does // then call the I/O system to do a verify. // // Skip the IoVerifyVolume if this is a mount or verify request // itself. Trying a recursive mount will cause a deadlock with // the DeviceObject->DeviceLock. if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) || (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) { return UDFPostRequest(IrpContext, Irp); } // Extract a pointer to the Vcb from the VolumeDeviceObject. // Note that since we have specifically excluded mount, // requests, we know that IrpSp->DeviceObject is indeed a // volume device object. IrpSp = IoGetCurrentIrpStackLocation(Irp); Vcb = (PVCB)IrpSp->DeviceObject->DeviceExtension; KdPrint(("UDFPerformVerify: check\n")); // Check if the volume still thinks it needs to be verified, // if it doesn't then we can skip doing a verify because someone // else beat us to it. _SEH2_TRY { if (DeviceToVerify->Flags & DO_VERIFY_VOLUME) { // If the IopMount in IoVerifyVolume did something, and // this is an absolute open, force a reparse. RC = IoVerifyVolume( DeviceToVerify, FALSE ); // Bug? /* if (UDFIsRawDevice(RC)) { RC = STATUS_WRONG_VOLUME; }*/ // If the verify operation completed it will return // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. if (RC == STATUS_SUCCESS) { IrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_EXCEPTION; } // If UDFVerifyVolume encountered an error during // processing, it will return that error. If we got // STATUS_WRONG_VOLUME from the verify, and our volume // is now mounted, commute the status to STATUS_SUCCESS. if ((RC == STATUS_WRONG_VOLUME) && (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) { RC = STATUS_SUCCESS; } // Do a quick unprotected check here. The routine will do // a safe check. After here we can release the resource. // Note that if the volume really went away, we will be taking // the Reparse path. // If the device might need to go away then call our dismount routine. if ( (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) || (Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) && (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE) ) { KdPrint(("UDFPerformVerify: UDFCheckForDismount\n")); UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); UDFCheckForDismount( IrpContext, Vcb, FALSE ); UDFReleaseResource(&(UDFGlobalData.GlobalDataResource)); } // If this is a create and the verify succeeded then complete the // request with a REPARSE status. if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && (IrpSp->FileObject->RelatedFileObject == NULL) && ((RC == STATUS_SUCCESS) || (RC == STATUS_WRONG_VOLUME)) ) { KdPrint(("UDFPerformVerify: IO_REMOUNT\n")); Irp->IoStatus.Information = IO_REMOUNT; Irp->IoStatus.Status = STATUS_REPARSE; IoCompleteRequest(Irp,IO_DISK_INCREMENT); UDFReleaseIrpContext(IrpContext); RC = STATUS_REPARSE; Irp = NULL; IrpContext = NULL; // If there is still an error to process then call the Io system // for a popup. } else if ((Irp != NULL) && !NT_SUCCESS( RC )) { KdPrint(("UDFPerformVerify: check IoIsErrorUserInduced\n")); // Fill in the device object if required. if (IoIsErrorUserInduced( RC ) ) { IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify ); } KdPrint(("UDFPerformVerify: UDFNormalizeAndRaiseStatus\n")); UDFNormalizeAndRaiseStatus( IrpContext, RC ); } } // If there is still an Irp, send it off to an Ex Worker thread. if (IrpContext != NULL) { RC = UDFPostRequest( IrpContext, Irp ); } } _SEH2_EXCEPT(UDFExceptionFilter( IrpContext, _SEH2_GetExceptionInformation())) { // We had some trouble trying to perform the verify or raised // an error ourselves. So we'll abort the I/O request with // the error status that we get back from the execption code. RC = UDFExceptionHandler( IrpContext, Irp); } _SEH2_END; KdPrint(("UDFPerformVerify: RC = %x\n", RC)); return RC; } // end UDFPerformVerify()
/* 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 ()
/* 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: 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()
/************************************************************************* * * 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: UDFCommonDirControl() * * 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: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS NTAPI UDFCommonDirControl( 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; BOOLEAN AcquiredVcb = FALSE; TmPrint(("UDFCommonDirControl: \n")); // BrutePoint(); _SEH2_TRY { // 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); 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; UDFFlushTryBreak(Vcb); UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; // Get some of the parameters supplied to us switch (IrpSp->MinorFunction) { case IRP_MN_QUERY_DIRECTORY: RC = UDFQueryDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb); break; case IRP_MN_NOTIFY_CHANGE_DIRECTORY: RC = UDFNotifyChangeDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb); break; default: // This should not happen. RC = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); // complete the IRP IoCompleteRequest(Irp, IO_NO_INCREMENT); break; } //try_exit: NOTHING; } _SEH2_FINALLY { if(AcquiredVcb) { UDFReleaseResource(&(Vcb->VCBResource)); AcquiredVcb = FALSE; } } _SEH2_END; return(RC); } // end UDFCommonDirControl()
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()