/* 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()
/************************************************************************* * * 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()
/************************************************************************* * * 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()
/************************************************************************* * * 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()