+++ /dev/null
-////////////////////////////////////////////////////////////////////
-// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
-// All rights reserved
-// This file was released under the GPLv2 on June 2015.
-////////////////////////////////////////////////////////////////////
-/*************************************************************************
-*
-* File: Flush.cpp
-*
-* Module: UDF File System Driver (Kernel mode execution only)
-*
-* Description:
-* Contains code to handle the "Flush Buffers" dispatch entry point.
-*
-*************************************************************************/
-
-#include "udffs.h"
-
-// define the file specific bug-check id
-#define UDF_BUG_CHECK_ID UDF_FILE_FLUSH
-
-
-
-/*************************************************************************
-*
-* Function: UDFFlush()
-*
-* Description:
-* The I/O Manager will invoke this routine to handle a flush buffers
-* request
-*
-* Expected Interrupt Level (for execution) :
-*
-* IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
-* to be deferred to a worker thread context)
-*
-* Return Value: STATUS_SUCCESS/Error
-*
-*************************************************************************/
-NTSTATUS
-NTAPI
-UDFFlush(
- PDEVICE_OBJECT DeviceObject, // the logical volume device object
- PIRP Irp) // I/O Request Packet
-{
- NTSTATUS RC = STATUS_SUCCESS;
- PtrUDFIrpContext PtrIrpContext = NULL;
- BOOLEAN AreWeTopLevel = FALSE;
-
- UDFPrint(("UDFFlush: \n"));
-
- FsRtlEnterFileSystem();
- ASSERT(DeviceObject);
- ASSERT(Irp);
-
- // set the top level context
- AreWeTopLevel = UDFIsIrpTopLevel(Irp);
- ASSERT(!UDFIsFSDevObj(DeviceObject));
-
- _SEH2_TRY {
-
- // get an IRP context structure and issue the request
- PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
- if(PtrIrpContext) {
- RC = UDFCommonFlush(PtrIrpContext, Irp);
- } else {
- RC = STATUS_INSUFFICIENT_RESOURCES;
- Irp->IoStatus.Status = RC;
- Irp->IoStatus.Information = 0;
- // complete the IRP
- IoCompleteRequest(Irp, IO_DISK_INCREMENT);
- }
-
- } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
-
- RC = UDFExceptionHandler(PtrIrpContext, Irp);
-
- UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
- } _SEH2_END;
-
- if (AreWeTopLevel) {
- IoSetTopLevelIrp(NULL);
- }
-
- FsRtlExitFileSystem();
-
- return(RC);
-} // end UDFFlush()
-
-
-
-/*************************************************************************
-*
-* Function: UDFCommonFlush()
-*
-* 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
-UDFCommonFlush(
- 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;
- PtrUDFNTRequiredFCB NtReqFcb = NULL;
- BOOLEAN AcquiredVCB = FALSE;
- BOOLEAN AcquiredFCB = FALSE;
- BOOLEAN PostRequest = FALSE;
- BOOLEAN CanWait = TRUE;
-
- UDFPrint(("UDFCommonFlush: \n"));
-
- _SEH2_TRY {
-
- // Get some of the parameters supplied to us
- CanWait = ((PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE);
- // If we cannot wait, post the request immediately since a flush is inherently blocking/synchronous.
- if (!CanWait) {
- PostRequest = TRUE;
- try_return(RC);
- }
-
- // 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);
- NtReqFcb = Fcb->NTRequiredFCB;
-
- // Check the type of object passed-in. That will determine the course of
- // action we take.
- if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || (Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)) {
-
- if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) {
- Vcb = (PVCB)(Fcb);
- } else {
- Vcb = Fcb->Vcb;
- }
- Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
-
-#ifdef UDF_DELAYED_CLOSE
- UDFCloseAllDelayed(Vcb);
-#endif //UDF_DELAYED_CLOSE
-
- UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
- AcquiredVCB = TRUE;
- // The caller wishes to flush all files for the mounted
- // logical volume. The flush volume routine below should simply
- // walk through all of the open file streams, acquire the
- // VCB resource, and request the flush operation from the Cache
- // Manager. Basically, the sequence of operations listed below
- // for a single file should be executed on all open files.
-
- UDFFlushLogicalVolume(PtrIrpContext, Irp, Vcb, 0);
-
- UDFReleaseResource(&(Vcb->VCBResource));
- AcquiredVCB = FALSE;
-
- try_return(RC);
- } else
- if (!(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {
- // This is a regular file.
- Vcb = Fcb->Vcb;
- ASSERT(Vcb);
- if(!ExIsResourceAcquiredExclusiveLite(&(Vcb->VCBResource)) &&
- !ExIsResourceAcquiredSharedLite(&(Vcb->VCBResource))) {
- UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
- AcquiredVCB = TRUE;
- }
- UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
- UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), TRUE);
- AcquiredFCB = TRUE;
-
- // Request the Cache Manager to perform a flush operation.
- // Further, instruct the Cache Manager that we wish to flush the
- // entire file stream.
- UDFFlushAFile(Fcb, Ccb, &(Irp->IoStatus), 0);
- RC = Irp->IoStatus.Status;
-
- // Some log-based FSD implementations may wish to flush their
- // log files at this time. Finally, we should update the time-stamp
- // values for the file stream appropriately. This would involve
- // obtaining the current time and modifying the appropriate directory
- // entry fields.
- } else {
- Vcb = Fcb->Vcb;
- }
-
-try_exit: NOTHING;
-
- } _SEH2_FINALLY {
-
- if (AcquiredFCB) {
- UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
- UDFReleaseResource(&(NtReqFcb->MainResource));
- AcquiredFCB = FALSE;
- }
- if (AcquiredVCB) {
- UDFReleaseResource(&(Vcb->VCBResource));
- AcquiredVCB = FALSE;
- }
-
- if(!_SEH2_AbnormalTermination()) {
- if (PostRequest) {
- // Nothing to lock now.
- BrutePoint();
- RC = UDFPostRequest(PtrIrpContext, Irp);
- } else {
- // Some applications like this request very much
- // (ex. WinWord). But it's not a good idea for CD-R/RW media
- if(Vcb->FlushMedia) {
- PIO_STACK_LOCATION PtrNextIoStackLocation = NULL;
- NTSTATUS RC1 = STATUS_SUCCESS;
-
- // Send the request down at this point.
- // To do this, we must set the next IRP stack location, and
- // maybe set a completion routine.
- // Be careful about marking the IRP pending if the lower level
- // driver returned pending and we do have a completion routine!
- PtrNextIoStackLocation = IoGetNextIrpStackLocation(Irp);
- *PtrNextIoStackLocation = *IrpSp;
-
- // Set the completion routine to "eat-up" any
- // STATUS_INVALID_DEVICE_REQUEST error code returned by the lower
- // level driver.
- IoSetCompletionRoutine(Irp, UDFFlushCompletion, NULL, TRUE, TRUE, TRUE);
-
- RC1 = IoCallDriver(Vcb->TargetDeviceObject, Irp);
-
- RC = ((RC1 == STATUS_INVALID_DEVICE_REQUEST) ? RC : RC1);
-
- // Release the IRP context at this time.
- UDFReleaseIrpContext(PtrIrpContext);
- } else {
- 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;
-
- return(RC);
-} // end UDFCommonFlush()
-
-
-/*************************************************************************
-*
-* Function: UDFFlushAFile()
-*
-* Description:
-* Tell the Cache Manager to perform a flush.
-*
-* Expected Interrupt Level (for execution) :
-*
-* IRQL_PASSIVE_LEVEL
-*
-* Return Value: None
-*
-*************************************************************************/
-ULONG
-UDFFlushAFile(
- IN PtrUDFFCB Fcb,
- IN PtrUDFCCB Ccb,
- OUT PIO_STATUS_BLOCK PtrIoStatus,
- IN ULONG FlushFlags
- )
-{
- BOOLEAN SetArchive = FALSE;
-// BOOLEAN PurgeCache = FALSE;
- ULONG ret_val = 0;
-
- UDFPrint(("UDFFlushAFile: \n"));
- if(!Fcb)
- return 0;
-
- _SEH2_TRY {
- if(Fcb->Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)
- return 0;
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
-#ifndef UDF_READ_ONLY_BUILD
- // Flush Security if required
- _SEH2_TRY {
- UDFWriteSecurity(Fcb->Vcb, Fcb, &(Fcb->NTRequiredFCB->SecurityDesc));
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
-#endif //UDF_READ_ONLY_BUILD
- // Flush SDir if any
- _SEH2_TRY {
- if(UDFHasAStreamDir(Fcb->FileInfo) &&
- Fcb->FileInfo->Dloc->SDirInfo &&
- !UDFIsSDirDeleted(Fcb->FileInfo->Dloc->SDirInfo) ) {
- ret_val |=
- UDFFlushADirectory(Fcb->Vcb, Fcb->FileInfo->Dloc->SDirInfo, PtrIoStatus, FlushFlags);
- }
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
- // Flush File
- _SEH2_TRY {
- if((Fcb->CachedOpenHandleCount || !Fcb->OpenHandleCount) &&
- Fcb->NTRequiredFCB->SectionObject.DataSectionObject) {
- if(!(Fcb->NTRequiredFCB->NtReqFCBFlags & UDF_NTREQ_FCB_DELETED)
- &&
- ((Fcb->NTRequiredFCB->NtReqFCBFlags & UDF_NTREQ_FCB_MODIFIED) ||
- (Ccb && !(Ccb->CCBFlags & UDF_CCB_FLUSHED)) )) {
- MmPrint((" CcFlushCache()\n"));
- CcFlushCache(&(Fcb->NTRequiredFCB->SectionObject), NULL, 0, PtrIoStatus);
- }
- // notice, that we should purge cache
- // we can't do it now, because it may cause last Close
- // request & thus, structure deallocation
-// PurgeCache = TRUE;
-
-#ifndef UDF_READ_ONLY_BUILD
- if(Ccb) {
- if( (Ccb->FileObject->Flags & FO_FILE_MODIFIED) &&
- !(Ccb->CCBFlags & UDF_CCB_WRITE_TIME_SET)) {
- if(Fcb->Vcb->CompatFlags & UDF_VCB_IC_UPDATE_MODIFY_TIME) {
- LONGLONG NtTime;
- KeQuerySystemTime((PLARGE_INTEGER)&NtTime);
- UDFSetFileXTime(Fcb->FileInfo, NULL, NULL, NULL, &NtTime);
- Fcb->NTRequiredFCB->LastWriteTime.QuadPart = NtTime;
- }
- SetArchive = TRUE;
- Ccb->FileObject->Flags &= ~FO_FILE_MODIFIED;
- }
- if(Ccb->FileObject->Flags & FO_FILE_SIZE_CHANGED) {
- LONGLONG ASize = UDFGetFileAllocationSize(Fcb->Vcb, Fcb->FileInfo);
- UDFSetFileSizeInDirNdx(Fcb->Vcb, Fcb->FileInfo, &ASize);
- Ccb->FileObject->Flags &= ~FO_FILE_SIZE_CHANGED;
- }
- }
-#endif //UDF_READ_ONLY_BUILD
- }
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
-
- _SEH2_TRY {
-#ifndef UDF_READ_ONLY_BUILD
- if(SetArchive &&
- (Fcb->Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT)) {
- ULONG Attr;
- PDIR_INDEX_ITEM DirNdx;
- DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index);
- // Archive bit
- Attr = UDFAttributesToNT(DirNdx, Fcb->FileInfo->Dloc->FileEntry);
- if(!(Attr & FILE_ATTRIBUTE_ARCHIVE))
- UDFAttributesToUDF(DirNdx, Fcb->FileInfo->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE);
- }
-#endif //UDF_READ_ONLY_BUILD
- UDFFlushFile__( Fcb->Vcb, Fcb->FileInfo, FlushFlags);
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
-
-/* if(PurgeCache) {
- _SEH2_TRY {
- MmPrint((" CcPurgeCacheSection()\n"));
- CcPurgeCacheSection( &(Fcb->NTRequiredFCB->SectionObject), NULL, 0, FALSE );
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
- }*/
-
- return ret_val;
-} // end UDFFlushAFile()
-
-/*************************************************************************
-*
-* Function: UDFFlushADirectory()
-*
-* Description:
-* Tell the Cache Manager to perform a flush for all files
-* in current directory & all subdirectories and flush all metadata
-*
-* Expected Interrupt Level (for execution) :
-*
-* IRQL_PASSIVE_LEVEL
-*
-* Return Value: None
-*
-*************************************************************************/
-ULONG
-UDFFlushADirectory(
- IN PVCB Vcb,
- IN PUDF_FILE_INFO FI,
- OUT PIO_STATUS_BLOCK PtrIoStatus,
- IN ULONG FlushFlags
- )
-{
- UDFPrint(("UDFFlushADirectory: \n"));
-// PDIR_INDEX_HDR hDI;
- PDIR_INDEX_ITEM DI;
-// BOOLEAN Referenced = FALSE;
- ULONG ret_val = 0;
-
- if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)
- return 0;
-
- if(!FI || !FI->Dloc || !FI->Dloc->DirIndex) goto SkipFlushDir;
-// hDI = FI->Dloc->DirIndex;
-
- // Flush Security if required
- _SEH2_TRY {
- UDFWriteSecurity(Vcb, FI->Fcb, &(FI->Fcb->NTRequiredFCB->SecurityDesc));
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
- // Flush SDir if any
- _SEH2_TRY {
- if(UDFHasAStreamDir(FI) &&
- FI->Dloc->SDirInfo &&
- !UDFIsSDirDeleted(FI->Dloc->SDirInfo) ) {
- ret_val |=
- UDFFlushADirectory(Vcb, FI->Dloc->SDirInfo, PtrIoStatus, FlushFlags);
- }
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
-
- // Flush Dir Tree
- _SEH2_TRY {
- UDF_DIR_SCAN_CONTEXT ScanContext;
- PUDF_FILE_INFO tempFI;
-
- if(UDFDirIndexInitScan(FI, &ScanContext, 2)) {
- while((DI = UDFDirIndexScan(&ScanContext, &tempFI))) {
- // Flush Dir entry
- _SEH2_TRY {
- if(!tempFI) continue;
- if(UDFIsADirectory(tempFI)) {
- UDFFlushADirectory(Vcb, tempFI, PtrIoStatus, FlushFlags);
- } else {
- UDFFlushAFile(tempFI->Fcb, NULL, PtrIoStatus, FlushFlags);
- }
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
- if(UDFFlushIsBreaking(Vcb, FlushFlags)) {
- ret_val |= UDF_FLUSH_FLAGS_INTERRUPTED;
- break;
- }
- }
- }
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
-SkipFlushDir:
- // Flush Dir
- _SEH2_TRY {
- UDFFlushFile__( Vcb, FI, FlushFlags );
- } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
- BrutePoint();
- } _SEH2_END;
-
- return ret_val;
-} // end UDFFlushADirectory()
-
-/*************************************************************************
-*
-* Function: UDFFlushLogicalVolume()
-*
-* Description:
-* Flush everything beginning from root directory.
-* Vcb must be previously acquired exclusively.
-*
-* Expected Interrupt Level (for execution) :
-*
-* IRQL_PASSIVE_LEVEL
-*
-* Return Value: None
-*
-*************************************************************************/
-ULONG
-UDFFlushLogicalVolume(
- IN PtrUDFIrpContext PtrIrpContext,
- IN PIRP Irp,
- IN PVCB Vcb,
- IN ULONG FlushFlags
- )
-{
- ULONG ret_val = 0;
-#ifndef UDF_READ_ONLY_BUILD
- IO_STATUS_BLOCK IoStatus;
-
- UDFPrint(("UDFFlushLogicalVolume: \n"));
-
- _SEH2_TRY {
- if(Vcb->VCBFlags & (UDF_VCB_FLAGS_RAW_DISK/* |
- UDF_VCB_FLAGS_MEDIA_READ_ONLY*/))
- return 0;
- if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY)
- return 0;
- if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED))
- return 0;
-
- // NOTE: This function may also be invoked internally as part of
- // processing a shutdown request.
- ASSERT(Vcb->RootDirFCB);
- ret_val |= UDFFlushADirectory(Vcb, Vcb->RootDirFCB->FileInfo, &IoStatus, FlushFlags);
-
-// if(UDFFlushIsBreaking(Vcb, FlushFlags))
-// return;
- // flush internal cache
- if(FlushFlags & UDF_FLUSH_FLAGS_LITE) {
- UDFPrint((" Lite flush, keep Modified=%d.\n", Vcb->Modified));
- } else {
- if(Vcb->VerifyOnWrite) {
- UDFPrint(("UDF: Flushing cache for verify\n"));
- //WCacheFlushAll__(&(Vcb->FastCache), Vcb);
- WCacheFlushBlocks__(&(Vcb->FastCache), Vcb, 0, Vcb->LastLBA);
- UDFVFlush(Vcb);
- }
- // umount (this is internal operation, NT will "dismount" volume later)
- UDFUmount__(Vcb);
-
- UDFPreClrModified(Vcb);
- WCacheFlushAll__(&(Vcb->FastCache), Vcb);
- UDFClrModified(Vcb);
- }
-
- } _SEH2_FINALLY {
- ;
- } _SEH2_END;
-#endif //UDF_READ_ONLY_BUILD
-
- return ret_val;
-} // end UDFFlushLogicalVolume()
-
-
-/*************************************************************************
-*
-* Function: UDFFlushCompletion()
-*
-* Description:
-* Eat up any bad errors.
-*
-* Expected Interrupt Level (for execution) :
-*
-* IRQL_PASSIVE_LEVEL
-*
-* Return Value: None
-*
-*************************************************************************/
-NTSTATUS
-NTAPI
-UDFFlushCompletion(
- PDEVICE_OBJECT PtrDeviceObject,
- PIRP Irp,
- PVOID Context
- )
-{
-// NTSTATUS RC = STATUS_SUCCESS;
-
- UDFPrint(("UDFFlushCompletion: \n"));
-
- if (Irp->PendingReturned) {
- IoMarkIrpPending(Irp);
- }
-
- if (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST) {
- // cannot do much here, can we?
- Irp->IoStatus.Status = STATUS_SUCCESS;
- }
-
- return(STATUS_SUCCESS);
-} // end UDFFlushCompletion()
-
-
-/*
- Check if we should break FlushTree process
- */
-BOOLEAN
-UDFFlushIsBreaking(
- IN PVCB Vcb,
- IN ULONG FlushFlags
- )
-{
- BOOLEAN ret_val = FALSE;
-// if(!(FlushFlags & UDF_FLUSH_FLAGS_BREAKABLE))
- return FALSE;
- UDFAcquireResourceExclusive(&(Vcb->FlushResource),TRUE);
- ret_val = (Vcb->VCBFlags & UDF_VCB_FLAGS_FLUSH_BREAK_REQ) ? TRUE : FALSE;
- Vcb->VCBFlags &= ~UDF_VCB_FLAGS_FLUSH_BREAK_REQ;
- UDFReleaseResource(&(Vcb->FlushResource));
- return ret_val;
-} // end UDFFlushIsBreaking()
-
-/*
- Signal FlushTree break request. Note, this is
- treated as recommendation only
- */
-VOID
-UDFFlushTryBreak(
- IN PVCB Vcb
- )
-{
- UDFAcquireResourceExclusive(&(Vcb->FlushResource),TRUE);
- Vcb->VCBFlags |= UDF_VCB_FLAGS_FLUSH_BREAK_REQ;
- UDFReleaseResource(&(Vcb->FlushResource));
-} // end UDFFlushTryBreak()