reactos/drivers/filesystems/udfs/flush.cpp

632 lines
20 KiB
C++

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