Something went wrong. Try again.
Reactos
Something went wrong. Try again.
21 kB · 632 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633////////////////////////////////////////////////////////////////////// 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**************************************************************************/NTSTATUSNTAPIUDFFlush( 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**************************************************************************/NTSTATUSUDFCommonFlush( 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**************************************************************************/ULONGUDFFlushAFile( 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**************************************************************************/ULONGUDFFlushADirectory( 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**************************************************************************/ULONGUDFFlushLogicalVolume( 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**************************************************************************/NTSTATUSNTAPIUDFFlushCompletion( 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 */BOOLEANUDFFlushIsBreaking( 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 */VOIDUDFFlushTryBreak( IN PVCB Vcb ){ UDFAcquireResourceExclusive(&(Vcb->FlushResource),TRUE); Vcb->VCBFlags |= UDF_VCB_FLAGS_FLUSH_BREAK_REQ; UDFReleaseResource(&(Vcb->FlushResource));} // end UDFFlushTryBreak()