Something went wrong. Try again.
Reactos
Something went wrong. Try again.
51 kB · 1227 lines
C++
at master
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228////////////////////////////////////////////////////////////////////// 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: Write.cpp** Module: UDF File System Driver (Kernel mode execution only)** Description:* Contains code to handle the "Write" dispatch entry point.**************************************************************************/
#include "udffs.h"
// define the file specific bug-check id#define UDF_BUG_CHECK_ID UDF_FILE_WRITE
#ifndef UDF_READ_ONLY_BUILD
/*************************************************************************** Function: UDFWrite()** Description:* The I/O Manager will invoke this routine to handle a write* 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**************************************************************************/NTSTATUSNTAPIUDFWrite( PDEVICE_OBJECT DeviceObject, // the logical volume device object PIRP Irp // I/O Request Packet ){ NTSTATUS RC = STATUS_SUCCESS; PtrUDFIrpContext PtrIrpContext = NULL; BOOLEAN AreWeTopLevel = FALSE;
TmPrint(("UDFWrite: , thrd:%8.8x\n",PsGetCurrentThread()));
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 = UDFCommonWrite(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 UDFWrite()
/*************************************************************************** Function: UDFCommonWrite()** 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**************************************************************************/NTSTATUSUDFCommonWrite( PtrUDFIrpContext PtrIrpContext, PIRP Irp){ NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp = NULL; LARGE_INTEGER ByteOffset; ULONG WriteLength = 0, TruncatedLength = 0; SIZE_T NumberBytesWritten = 0; PFILE_OBJECT FileObject = NULL; PtrUDFFCB Fcb = NULL; PtrUDFCCB Ccb = NULL; PVCB Vcb = NULL; PtrUDFNTRequiredFCB NtReqFcb = NULL; PERESOURCE PtrResourceAcquired = NULL; PERESOURCE PtrResourceAcquired2 = NULL; PVOID SystemBuffer = NULL;// PVOID TmpBuffer = NULL;// uint32 KeyValue = 0; PIRP TopIrp;
LONGLONG ASize; LONGLONG OldVDL;
ULONG Res1Acq = 0; ULONG Res2Acq = 0;
BOOLEAN CacheLocked = FALSE;
BOOLEAN CanWait = FALSE; BOOLEAN PagingIo = FALSE; BOOLEAN NonBufferedIo = FALSE; BOOLEAN SynchronousIo = FALSE; BOOLEAN IsThisADeferredWrite = FALSE; BOOLEAN WriteToEOF = FALSE; BOOLEAN Resized = FALSE; BOOLEAN RecursiveWriteThrough = FALSE; BOOLEAN WriteFileSizeToDirNdx = FALSE; BOOLEAN ZeroBlock = FALSE; BOOLEAN VcbAcquired = FALSE; BOOLEAN ZeroBlockDone = FALSE;
TmPrint(("UDFCommonWrite: irp %x\n", Irp));
_SEH2_TRY {
TopIrp = IoGetTopLevelIrp();
switch((ULONG_PTR)TopIrp) { case FSRTL_FSP_TOP_LEVEL_IRP: UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n")); break; case FSRTL_CACHE_TOP_LEVEL_IRP: UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n")); break; case FSRTL_MOD_WRITE_TOP_LEVEL_IRP: UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n")); break; case FSRTL_FAST_IO_TOP_LEVEL_IRP: UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n")); BrutePoint(); break; case NULL: UDFPrint((" NULL TOP_LEVEL_IRP\n")); break; default: if(TopIrp == Irp) { UDFPrint((" TOP_LEVEL_IRP\n")); } else { UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp)); } break; }
// First, get a pointer to the current I/O stack location IrpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(IrpSp); MmPrint((" Enter Irp, MDL=%x\n", Irp->MdlAddress)); if(Irp->MdlAddress) { UDFTouch(Irp->MdlAddress); }
FileObject = IrpSp->FileObject; ASSERT(FileObject);
// If this happens to be a MDL write complete request, then // allocated MDL can be freed. This may cause a recursive write // back into the FSD. if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { // Caller wants to tell the Cache Manager that a previously // allocated MDL can be freed. UDFMdlComplete(PtrIrpContext, Irp, IrpSp, FALSE); // The IRP has been completed. try_return(RC = STATUS_SUCCESS); }
// If this is a request at IRQL DISPATCH_LEVEL, then post the request if (IrpSp->MinorFunction & IRP_MN_DPC) { try_return(RC = STATUS_PENDING); }
// Get the FCB and CCB pointers Ccb = (PtrUDFCCB)(FileObject->FsContext2); ASSERT(Ccb); Fcb = Ccb->Fcb; ASSERT(Fcb); Vcb = Fcb->Vcb;
if(Fcb->FCBFlags & UDF_FCB_DELETED) { ASSERT(FALSE); try_return(RC = STATUS_TOO_LATE); }
// is this operation allowed ? if(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY) { try_return(RC = STATUS_ACCESS_DENIED); } Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
// Disk based file systems might decide to verify the logical volume // (if required and only if removable media are supported) at this time // As soon as Tray is locked, we needn't call UDFVerifyVcb()
ByteOffset = IrpSp->Parameters.Write.ByteOffset;
CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; PagingIo = (Irp->Flags & IRP_PAGING_IO) ? TRUE : FALSE; NonBufferedIo = (Irp->Flags & IRP_NOCACHE) ? TRUE : FALSE; SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO) ? TRUE : FALSE; UDFPrint((" Flags: %s; %s; %s; %s; Irp(W): %8.8x\n", CanWait ? "Wt" : "nw", PagingIo ? "Pg" : "np", NonBufferedIo ? "NBuf" : "buff", SynchronousIo ? "Snc" : "Asc", Irp->Flags));
NtReqFcb = Fcb->NTRequiredFCB;
Res1Acq = UDFIsResourceAcquired(&(NtReqFcb->MainResource)); if(!Res1Acq) { Res1Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES1_ACQ; } Res2Acq = UDFIsResourceAcquired(&(NtReqFcb->PagingIoResource)); if(!Res2Acq) { Res2Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES2_ACQ; }
if(!NonBufferedIo && (Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB)) { if((Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB) && UDFIsAStream(Fcb->FileInfo)) { UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, FILE_NOTIFY_CHANGE_STREAM_WRITE, FILE_ACTION_MODIFIED_STREAM); } else { UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS, FILE_ACTION_MODIFIED); } }
// Get some of the parameters supplied to us WriteLength = IrpSp->Parameters.Write.Length; if (WriteLength == 0) { // a 0 byte write can be immediately succeeded if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) { // NT expects changing CurrentByteOffset to zero in this case FileObject->CurrentByteOffset.QuadPart = 0; } try_return(RC); }
// If this is the normal file we have to check for // write access according to the current state of the file locks. if (!PagingIo && !FsRtlCheckLockForWriteAccess( &(NtReqFcb->FileLock), Irp) ) { try_return( RC = STATUS_FILE_LOCK_CONFLICT ); }
// ********** // Is this a write of the volume itself ? // ********** if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) { // Yup, we need to send this on to the disk driver after // validation of the offset and length. Vcb = (PVCB)(Fcb); if(!CanWait) try_return(RC = STATUS_PENDING); // I dislike the idea of writing to not locked media if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED)) { try_return(RC = STATUS_ACCESS_DENIED); }
if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) {
UDFPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n")); PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED;
if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) { UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); }#ifdef UDF_DELAYED_CLOSE UDFCloseAllDelayed(Vcb);#endif //UDF_DELAYED_CLOSE
}
// Acquire the volume resource exclusive UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); PtrResourceAcquired = &(Vcb->VCBResource);
// I dislike the idea of writing to mounted media too, but M$ has another point of view... if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) { // flush system cache UDFFlushLogicalVolume(NULL, NULL, Vcb, 0); }#if defined(_MSC_VER) && !defined(__clang__)/* FIXME */ if(PagingIo) { CollectStatistics(Vcb, MetaDataWrites); CollectStatisticsEx(Vcb, MetaDataWriteBytes, NumberBytesWritten); }#endif // Forward the request to the lower level driver // Lock the callers buffer if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, WriteLength))) { try_return(RC); } SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); if(!SystemBuffer) try_return(RC = STATUS_INVALID_USER_BUFFER); // Indicate, that volume contents can change after this operation // This flag will force VerifyVolume in future UDFPrint((" set UnsafeIoctl\n")); Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL; // Make sure, that volume will never be quick-remounted // It is very important for ChkUdf utility. Vcb->SerialNumber--; // Perform actual Write RC = UDFTWrite(Vcb, SystemBuffer, WriteLength, (ULONG)(ByteOffset.QuadPart >> Vcb->BlockSizeBits), &NumberBytesWritten); UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); try_return(RC); }
if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) { try_return(RC = STATUS_ACCESS_DENIED); }
// back pressure for very smart and fast system cache ;) if(!NonBufferedIo) { // cached IO if(Vcb->VerifyCtx.QueuedCount || Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) { UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT); } } else { if(Vcb->VerifyCtx.ItemCount > UDF_SYS_CACHE_STOP_THR) { UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT); } }
// The FSD (if it is a "nice" FSD) should check whether it is // convenient to allow the write to proceed by utilizing the // CcCanIWrite() function call. If it is not convenient to perform // the write at this time, we should defer the request for a while. // The check should not however be performed for non-cached write // operations. To determine whether we are retrying the operation // or now, use Flags in the IrpContext structure we have created
IsThisADeferredWrite = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_DEFERRED_WRITE) ? TRUE : FALSE;
if (!NonBufferedIo) { MmPrint((" CcCanIWrite()\n")); if (!CcCanIWrite(FileObject, WriteLength, CanWait, IsThisADeferredWrite)) { // Cache Manager and/or the VMM does not want us to perform // the write at this time. Post the request. PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_DEFERRED_WRITE; UDFPrint(("UDFCommonWrite: Defer write\n")); MmPrint((" CcDeferWrite()\n")); CcDeferWrite(FileObject, UDFDeferredWriteCallBack, PtrIrpContext, Irp, WriteLength, IsThisADeferredWrite); try_return(RC = STATUS_PENDING); } }
// If the write request is directed to a page file, // send the request directly to the disk if (Fcb->FCBFlags & UDF_FCB_PAGE_FILE) { NonBufferedIo = TRUE; }
// We can continue. Check whether this write operation is targeted // to a directory object in which case the UDF FSD will disallow // the write request. if (Fcb->FCBFlags & UDF_FCB_DIRECTORY) { RC = STATUS_INVALID_DEVICE_REQUEST; try_return(RC); }
// Validate start offset and length supplied. // Here is a special check that determines whether the caller wishes to // begin the write at current end-of-file (whatever the value of that // offset might be) if(ByteOffset.HighPart == (LONG)0xFFFFFFFF) { if(ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE) { WriteToEOF = TRUE; ByteOffset = NtReqFcb->CommonFCBHeader.FileSize; } else if(ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) { ByteOffset = FileObject->CurrentByteOffset; } }
// Check if this volume has already been shut down. If it has, fail // this write request. if (Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN) { try_return(RC = STATUS_TOO_LATE); }
// Paging I/O write operations are special. If paging i/o write // requests begin beyond end-of-file, the request should be no-oped // If paging i/o // requests extend beyond current end of file, they should be truncated // to current end-of-file. if(PagingIo && (WriteToEOF || ((ByteOffset.QuadPart + WriteLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart))) { if (ByteOffset.QuadPart > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) { TruncatedLength = 0; } else { TruncatedLength = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart); } if(!TruncatedLength) try_return(RC = STATUS_SUCCESS); } else { TruncatedLength = WriteLength; }
#if defined(_MSC_VER) && !defined(__clang__)/* FIXME */ if(PagingIo) { CollectStatistics(Vcb, UserFileWrites); CollectStatisticsEx(Vcb, UserFileWriteBytes, NumberBytesWritten); }#endif
// There are certain complications that arise when the same file stream // has been opened for cached and non-cached access. The FSD is then // responsible for maintaining a consistent view of the data seen by // the caller. // If this happens to be a non-buffered I/O, we should __try to flush the // cached data (if some other file object has already initiated caching // on the file stream). We should also __try to purge the cached // information though the purge will probably fail if the file has been // mapped into some process' virtual address space // WARNING !!! we should not flush data beyond valid data length if ( NonBufferedIo && !PagingIo && NtReqFcb->SectionObject.DataSectionObject && TruncatedLength && (ByteOffset.QuadPart < NtReqFcb->CommonFCBHeader.FileSize.QuadPart)) {
if(!Res1Acq) { // Try to acquire the FCB MainResource exclusively if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) { try_return(RC = STATUS_PENDING); } PtrResourceAcquired = &(NtReqFcb->MainResource); }
if(!Res2Acq) { // We hold PagingIo shared around the flush to fix a // cache coherency problem. UDFAcquireSharedStarveExclusive(&(NtReqFcb->PagingIoResource), TRUE ); PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); }
// Flush and then attempt to purge the cache if((ByteOffset.QuadPart + TruncatedLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) { NumberBytesWritten = TruncatedLength; } else { NumberBytesWritten = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart); }
MmPrint((" CcFlushCache()\n")); CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, NumberBytesWritten, &(Irp->IoStatus));
if(PtrResourceAcquired2) { UDFReleaseResource(&(NtReqFcb->PagingIoResource)); PtrResourceAcquired2 = NULL; } // If the flush failed, return error to the caller if (!NT_SUCCESS(RC = Irp->IoStatus.Status)) { NumberBytesWritten = 0; try_return(RC); }
if(!Res2Acq) { // Acquiring and immediately dropping the resource serializes // us behind any other writes taking place (either from the // lazy writer or modified page writer). UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE ); UDFReleaseResource(&(NtReqFcb->PagingIoResource)); }
// Attempt the purge and ignore the return code MmPrint((" CcPurgeCacheSection()\n")); CcPurgeCacheSection(&(NtReqFcb->SectionObject), &ByteOffset, NumberBytesWritten, FALSE); NumberBytesWritten = 0; // We are finished with our flushing and purging if(PtrResourceAcquired) { UDFReleaseResource(PtrResourceAcquired); PtrResourceAcquired = NULL; } }
// Determine if we were called by the lazywriter. // We reuse 'IsThisADeferredWrite' here to decrease stack usage IsThisADeferredWrite = (NtReqFcb->LazyWriterThreadID == HandleToUlong(PsGetCurrentThreadId()));
// Acquire the appropriate FCB resource if(PagingIo) { // PagingIoResource is already acquired exclusive // on LazyWrite condition (see UDFAcqLazyWrite()) ASSERT(NonBufferedIo); if(!IsThisADeferredWrite) { if(!Res2Acq) { // Try to acquire the FCB PagingIoResource exclusive if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) { try_return(RC = STATUS_PENDING); } // Remember the resource that was acquired PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); } } } else { // Try to acquire the FCB MainResource shared if(NonBufferedIo) { if(!Res2Acq) { if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) { //if(!UDFAcquireSharedWaitForExclusive(&(NtReqFcb->PagingIoResource), CanWait)) { try_return(RC = STATUS_PENDING); } PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); } } else { if(!Res1Acq) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) { //if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) { try_return(RC = STATUS_PENDING); } PtrResourceAcquired = &(NtReqFcb->MainResource); } } // Remember the resource that was acquired }
// Set the flag indicating if Fast I/O is possible NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);/* if(NtReqFcb->CommonFCBHeader.IsFastIoPossible == FastIoIsPossible) { NtReqFcb->CommonFCBHeader.IsFastIoPossible = FastIoIsQuestionable; }*/
if ( (Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) && (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_TOP_LEVEL)) {
// This clause determines if the top level request was // in the FastIo path. if ((ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG) {
PIO_STACK_LOCATION IrpStack; ASSERT( TopIrp->Type == IO_TYPE_IRP ); IrpStack = IoGetCurrentIrpStackLocation(TopIrp);
// Finally this routine detects if the Top irp was a // write to this file and thus we are the writethrough. if ((IrpStack->MajorFunction == IRP_MJ_WRITE) && (IrpStack->FileObject->FsContext == FileObject->FsContext)) {
RecursiveWriteThrough = TRUE; PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_WRITE_THROUGH; } } }
// Here is the deal with ValidDataLength and FileSize: // // Rule 1: PagingIo is never allowed to extend file size. // // Rule 2: Only the top level requestor may extend Valid // Data Length. This may be paging IO, as when a // a user maps a file, but will never be as a result // of cache lazy writer writes since they are not the // top level request. // // Rule 3: If, using Rules 1 and 2, we decide we must extend // file size or valid data, we take the Fcb exclusive.
// Check whether the current request will extend the file size, // or the valid data length (if the FSD supports the concept of a // valid data length associated with the file stream). In either case, // inform the Cache Manager at this time using CcSetFileSizes() about // the new file length. Note that real FSD implementations will have to // first allocate enough on-disk space at this point (before they // inform the Cache Manager about the new size) to ensure that the write // will subsequently not fail due to lack of disk space.
OldVDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart; ZeroBlock = (ByteOffset.QuadPart > OldVDL);
if (!PagingIo && !RecursiveWriteThrough && !IsThisADeferredWrite) {
BOOLEAN ExtendFS;
ExtendFS = (ByteOffset.QuadPart + TruncatedLength > NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
if( WriteToEOF || ZeroBlock || ExtendFS) { // we are extending the file;
if(!CanWait) try_return(RC = STATUS_PENDING);// CanWait = TRUE; // Release any resources acquired above ... if (PtrResourceAcquired2) { UDFReleaseResource(PtrResourceAcquired2); PtrResourceAcquired2 = NULL; } if (PtrResourceAcquired) { UDFReleaseResource(PtrResourceAcquired); PtrResourceAcquired = NULL; } if(!UDFAcquireResourceShared(&(Vcb->VCBResource), CanWait)) { try_return(RC = STATUS_PENDING); } VcbAcquired = TRUE; if(!Res1Acq) { // Try to acquire the FCB MainResource exclusively UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) { try_return(RC = STATUS_PENDING); } // Remember the resource that was acquired PtrResourceAcquired = &(NtReqFcb->MainResource); }
if(!Res2Acq) { // allocate space... AdPrint((" Try to acquire PagingIoRes\n")); UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE ); PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); } AdPrint((" PagingIoRes Ok, Resizing...\n"));
if(ExtendFS) { RC = UDFResizeFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart + TruncatedLength);
if(!NT_SUCCESS(RC)) { if(PtrResourceAcquired2) { UDFReleaseResource(&(NtReqFcb->PagingIoResource)); PtrResourceAcquired2 = NULL; } try_return(RC); } Resized = TRUE; // ... and inform the Cache Manager about it NtReqFcb->CommonFCBHeader.FileSize.QuadPart = ByteOffset.QuadPart + TruncatedLength; NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo); if(!Vcb->LowFreeSpace) { NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart += (PAGE_SIZE*9-1); } else { NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart += (PAGE_SIZE-1); } NtReqFcb->CommonFCBHeader.AllocationSize.LowPart &= ~(PAGE_SIZE-1); }
UDFPrint(("UDFCommonWrite: Set size %x (alloc size %x)\n", ByteOffset.LowPart + TruncatedLength, NtReqFcb->CommonFCBHeader.AllocationSize.LowPart)); if (CcIsFileCached(FileObject)) { if(ExtendFS) { MmPrint((" CcSetFileSizes()\n")); CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize)); NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED; } // Attempt to Zero newly added fragment // and ignore the return code // This should be done to inform cache manager // that given extent has no cached data // (Otherwise, CM sometimes thinks that it has) if(ZeroBlock) { NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED; ThPrint((" UDFZeroDataEx(1)\n")); UDFZeroDataEx(NtReqFcb, OldVDL, /*ByteOffset.QuadPart*/ NtReqFcb->CommonFCBHeader.FileSize.QuadPart - OldVDL, CanWait, Vcb, FileObject);#ifdef UDF_DBG ZeroBlockDone = TRUE;#endif //UDF_DBG } } if (PtrResourceAcquired2) { UDFReleaseResource(PtrResourceAcquired2); PtrResourceAcquired2 = NULL; }
// Inform any pending IRPs (notify change directory). if(UDFIsAStream(Fcb->FileInfo)) { UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, FILE_NOTIFY_CHANGE_STREAM_SIZE, FILE_ACTION_MODIFIED_STREAM); } else { UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); } }
}
#ifdef UDF_DISABLE_SYSTEM_CACHE_MANAGER NonBufferedIo = TRUE;#endif if(Fcb && Fcb->FileInfo && Fcb->FileInfo->Dloc) { AdPrint(("UDFCommonWrite: DataLoc %x, Mapping %x\n", Fcb->FileInfo->Dloc->DataLoc, Fcb->FileInfo->Dloc->DataLoc.Mapping)); }
// Branch here for cached vs non-cached I/O if (!NonBufferedIo) {
// The caller wishes to perform cached I/O. Initiate caching if // this is the first cached I/O operation using this file object if (!FileObject->PrivateCacheMap) { // This is the first cached I/O operation. You must ensure // that the FCB Common FCB Header contains valid sizes at this time UDFPrint(("UDFCommonWrite: Init system cache\n")); MmPrint((" CcInitializeCacheMap()\n")); CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&(NtReqFcb->CommonFCBHeader.AllocationSize)), FALSE, // We will not utilize pin access for this file &(UDFGlobalData.CacheMgrCallBacks), // callbacks NtReqFcb); // The context used in callbacks MmPrint((" CcSetReadAheadGranularity()\n")); CcSetReadAheadGranularity(FileObject, Vcb->SystemCacheGran);
}
if(ZeroBlock && !ZeroBlockDone) { ThPrint((" UDFZeroDataEx(2)\n")); UDFZeroDataEx(NtReqFcb, OldVDL, /*ByteOffset.QuadPart*/ ByteOffset.QuadPart + TruncatedLength - OldVDL, CanWait, Vcb, FileObject); if(ByteOffset.LowPart & (PAGE_SIZE-1)) { } }
WriteFileSizeToDirNdx = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_WRITE_THROUGH) ? TRUE : FALSE; // Check and see if this request requires a MDL returned to the caller if (IrpSp->MinorFunction & IRP_MN_MDL) { // Caller does want a MDL returned. Note that this mode // implies that the caller is prepared to block MmPrint((" CcPrepareMdlWrite()\n"));// CcPrepareMdlWrite(FileObject, &ByteOffset, TruncatedLength, &(Irp->MdlAddress), &(Irp->IoStatus));// NumberBytesWritten = Irp->IoStatus.Information;// RC = Irp->IoStatus.Status;
NumberBytesWritten = 0; RC = STATUS_INVALID_PARAMETER;
try_return(RC); }
if(NtReqFcb->SectionObject.DataSectionObject && TruncatedLength >= 0x10000 && ByteOffset.LowPart && !(ByteOffset.LowPart & 0x00ffffff)) {
//if(WinVer_Id() < WinVer_2k) { //LARGE_INTEGER flush_offs; //flush_offs.QuadPart = ByteOffset.QuadPart - 0x100*0x10000; MmPrint((" CcFlushCache() 16Mb\n")); //CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, 0x100*0x10000, &(Irp->IoStatus));
// there was a nice idea: flush just previous part. But it doesn't work CcFlushCache(&(NtReqFcb->SectionObject), NULL, 0, &(Irp->IoStatus)); //} }
// This is a regular run-of-the-mill cached I/O request. Let the // Cache Manager worry about it! // First though, we need a buffer pointer (address) that is valid
// We needn't call CcZeroData 'cause udf_info.cpp will care about it SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); if(!SystemBuffer) try_return(RC = STATUS_INVALID_USER_BUFFER); ASSERT(SystemBuffer); NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED; PerfPrint(("UDFCommonWrite: CcCopyWrite %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart)); MmPrint((" CcCopyWrite()\n")); if(!CcCopyWrite(FileObject, &(ByteOffset), TruncatedLength, CanWait, SystemBuffer)) { // The caller was not prepared to block and data is not immediately // available in the system cache // Mark Irp Pending ... try_return(RC = STATUS_PENDING); }
UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); // We have the data RC = STATUS_SUCCESS; NumberBytesWritten = TruncatedLength;
try_return(RC);
} else {
MmPrint((" Write NonBufferedIo\n"));
// We needn't call CcZeroData here (like in Fat driver) // 'cause we've already done it above // (see call to UDFZeroDataEx() ) if (!RecursiveWriteThrough && !IsThisADeferredWrite && (OldVDL < ByteOffset.QuadPart)) {#ifdef UDF_DBG ASSERT(!ZeroBlockDone);#endif //UDF_DBG UDFZeroDataEx(NtReqFcb, OldVDL, /*ByteOffset.QuadPart*/ ByteOffset.QuadPart - OldVDL, CanWait, Vcb, FileObject); } if(OldVDL < (ByteOffset.QuadPart + TruncatedLength)) { NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = ByteOffset.QuadPart + TruncatedLength; }
#if 1 if((ULONG_PTR)TopIrp == FSRTL_MOD_WRITE_TOP_LEVEL_IRP) { UDFPrint(("FSRTL_MOD_WRITE_TOP_LEVEL_IRP => CanWait\n")); CanWait = TRUE; } else if((ULONG_PTR)TopIrp == FSRTL_CACHE_TOP_LEVEL_IRP) { UDFPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n")); CanWait = TRUE; }
if(NtReqFcb->AcqSectionCount || NtReqFcb->AcqFlushCount) { MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb->AcqSectionCount, NtReqFcb->AcqFlushCount)); CanWait = TRUE; } else {}/* if((TopIrp != Irp)) { UDFPrint(("(TopIrp != Irp) => CanWait\n")); CanWait = TRUE; } else*/#endif if(KeGetCurrentIrql() > PASSIVE_LEVEL) { MmPrint((" !PASSIVE_LEVEL\n")); CanWait = FALSE; PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FORCED_POST; } // Successful check will cause WCache lock if(!CanWait && UDFIsFileCached__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, TRUE)) { UDFPrint(("UDFCommonWrite: Cached => CanWait\n")); CacheLocked = TRUE; CanWait = TRUE; } // Send the request to lower level drivers if(!CanWait) { UDFPrint(("UDFCommonWrite: Post physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
try_return(RC = STATUS_PENDING); }
if(!Res2Acq) { if(UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb->PagingIoResource))) { PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); } }
PerfPrint(("UDFCommonWrite: Physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
// Lock the callers buffer if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, TruncatedLength))) { try_return(RC); }
SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); if(!SystemBuffer) { try_return(RC = STATUS_INVALID_USER_BUFFER); } NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED; RC = UDFWriteFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, CacheLocked, (PCHAR)SystemBuffer, &NumberBytesWritten);
UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
#if defined(_MSC_VER) && !defined(__clang__)/* FIXME */ if(PagingIo) { CollectStatistics(Vcb, UserDiskWrites); } else { CollectStatistics2(Vcb, NonCachedDiskWrites); }#endif WriteFileSizeToDirNdx = TRUE;
try_return(RC); }
try_exit: NOTHING;
} _SEH2_FINALLY {
if(CacheLocked) { WCacheEODirect__(&(Vcb->FastCache), Vcb); }
// Release any resources acquired here ... if(PtrResourceAcquired2) { UDFReleaseResource(PtrResourceAcquired2); } if(PtrResourceAcquired) { if(NtReqFcb && (PtrResourceAcquired == &(NtReqFcb->MainResource))) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); } UDFReleaseResource(PtrResourceAcquired); } if(VcbAcquired) { UDFReleaseResource(&(Vcb->VCBResource)); }
// Post IRP if required if(RC == STATUS_PENDING) {
// Lock the callers buffer here. Then invoke a common routine to // perform the post operation. if (!(IrpSp->MinorFunction & IRP_MN_MDL)) { RC = UDFLockCallersBuffer(PtrIrpContext, Irp, FALSE, WriteLength); ASSERT(NT_SUCCESS(RC)); } if(PagingIo) { if(Res1Acq) { PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES1_ACQ; } if(Res2Acq) { PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES2_ACQ; } }
// Perform the post operation which will mark the IRP pending // and will return STATUS_PENDING back to us RC = UDFPostRequest(PtrIrpContext, Irp);
} else { // For synchronous I/O, the FSD must maintain the current byte offset // Do not do this however, if I/O is marked as paging-io if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) { FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + NumberBytesWritten; } // If the write completed successfully and this was not a paging-io // operation, set a flag in the CCB that indicates that a write was // performed and that the file time should be updated at cleanup if (NT_SUCCESS(RC) && !PagingIo) { Ccb->CCBFlags |= UDF_CCB_MODIFIED; // If the file size was changed, set a flag in the FCB indicating that // this occurred. FileObject->Flags |= FO_FILE_MODIFIED; if(Resized) { if(!WriteFileSizeToDirNdx) { FileObject->Flags |= FO_FILE_SIZE_CHANGED; } else { ASize = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo); UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &ASize); } } // Update ValidDataLength if(!IsThisADeferredWrite && NtReqFcb) { if(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart < (ByteOffset.QuadPart + NumberBytesWritten)) {
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = min(NtReqFcb->CommonFCBHeader.FileSize.QuadPart, ByteOffset.QuadPart + NumberBytesWritten); } } }
// If the request failed, and we had done some nasty stuff like // extending the file size (including informing the Cache Manager // about the new file size), and allocating on-disk space etc., undo // it at this time.
// Can complete the IRP here if no exception was encountered if(!_SEH2_AbnormalTermination() && Irp) { Irp->IoStatus.Status = RC; Irp->IoStatus.Information = NumberBytesWritten; // complete the IRP MmPrint((" Complete Irp, MDL=%x\n", Irp->MdlAddress)); if(Irp->MdlAddress) { UDFTouch(Irp->MdlAddress); } IoCompleteRequest(Irp, IO_DISK_INCREMENT); } // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext);
} // can we complete the IRP ? } _SEH2_END; // end of "__finally" processing
UDFPrint(("\n")); return(RC);} // end UDFCommonWrite()
/*************************************************************************** Function: UDFDeferredWriteCallBack()** Description:* Invoked by the cache manager in the context of a worker thread.* Typically, you can simply post the request at this point (just* as you would have if the original request could not block) to* perform the write in the context of a system worker thread.** Expected Interrupt Level (for execution) :** IRQL_PASSIVE_LEVEL** Return Value: None**************************************************************************/VOIDNTAPIUDFDeferredWriteCallBack( IN PVOID Context1, // Should be PtrIrpContext IN PVOID Context2 // Should be Irp ){ UDFPrint(("UDFDeferredWriteCallBack\n")); // We should typically simply post the request to our internal // queue of posted requests (just as we would if the original write // could not be completed because the caller could not block). // Once we post the request, return from this routine. The write // will then be retried in the context of a system worker thread UDFPostRequest((PtrUDFIrpContext)Context1, (PIRP)Context2);
} // end UDFDeferredWriteCallBack()
/***************************************************************************************************************************************************/
#define USE_CcCopyWrite_TO_ZERO
VOIDUDFPurgeCacheEx_( PtrUDFNTRequiredFCB NtReqFcb, LONGLONG Offset, LONGLONG Length,//#ifndef ALLOW_SPARSE BOOLEAN CanWait,//#endif //ALLOW_SPARSE PVCB Vcb, PFILE_OBJECT FileObject ){ ULONG Off_l;#ifdef USE_CcCopyWrite_TO_ZERO ULONG PgLen;#endif //USE_CcCopyWrite_TO_ZERO
// We'll just purge cache section here, // without call to CcZeroData() // 'cause udf_info.cpp will care about it
#define PURGE_BLOCK_SZ 0x10000000
// NOTE: if FS engine doesn't suport // sparse/unrecorded areas, CcZeroData must be called // In this case we'll see some recursive WRITE requests
_SEH2_TRY { MmPrint((" UDFPurgeCacheEx_(): Offs: %I64x, ", Offset)); MmPrint((" Len: %lx\n", Length)); SECTION_OBJECT_POINTERS* SectionObject = &(NtReqFcb->SectionObject); if(Length) { LONGLONG Offset0, OffsetX, VDL;
Offset0 = Offset; if((Off_l = ((ULONG)Offset0 & (PAGE_SIZE-1)))) { // Offset, Offset0 // v // ...|dddddddddddd00000|.... // |<- Off_l ->|#ifndef USE_CcCopyWrite_TO_ZERO *((PULONG)&Offset0) &= ~(PAGE_SIZE-1); MmPrint((" CcFlushCache(s) Offs %I64x, Len %x\n", Offset0, Off_l)); CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset0, Off_l, NULL );#else //USE_CcCopyWrite_TO_ZERO // ...|ddddd000000000000|.... // |<- PgLen ->| PgLen = PAGE_SIZE - Off_l; /*(*((PULONG)&Offset) & (PAGE_SIZE-1))*/ // if(PgLen > Length) PgLen = (ULONG)Length;
MmPrint((" ZeroCache (CcWrite) Offs %I64x, Len %x\n", Offset, PgLen));#ifdef DBG if(FileObject && Vcb) {
ASSERT(CanWait);#endif //DBG if (PgLen) { if (SectionObject->SharedCacheMap) { CcCopyWrite(FileObject, (PLARGE_INTEGER)&Offset, PgLen, TRUE || CanWait, Vcb->ZBuffer); } Offset += PgLen; Length -= PgLen; }#ifdef DBG } else { MmPrint((" Can't use CcWrite to zero cache\n")); }#endif //DBG#endif //USE_CcCopyWrite_TO_ZERO } VDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart; OffsetX = Offset+Length; if((Off_l = ((ULONG)OffsetX & (PAGE_SIZE-1)))) {
if(OffsetX < VDL) {#ifndef USE_CcCopyWrite_TO_ZERO Off_l = ( (ULONG)(VDL-OffsetX) > PAGE_SIZE ) ? (PAGE_SIZE - Off_l) : ((ULONG)(VDL-OffsetX)); *((PULONG)&OffsetX) &= ~(PAGE_SIZE-1); MmPrint((" CcFlushCache(e) Offs %I64x, Len %x\n", OffsetX, Off_l)); CcFlushCache( SectionObject, (PLARGE_INTEGER)&OffsetX, Off_l, NULL );#else //USE_CcCopyWrite_TO_ZERO if(VDL - OffsetX > PAGE_SIZE) { PgLen = (ULONG)OffsetX & ~(PAGE_SIZE-1); } else { PgLen = (ULONG)(VDL - OffsetX) & ~(PAGE_SIZE-1); } // ...|000000000000ddddd|.... // |<- PgLen ->| MmPrint((" ZeroCache (CcWrite - 2) Offs %I64x, Len %x\n", OffsetX, PgLen));#ifdef DBG if(FileObject && Vcb) { ASSERT(CanWait);#endif //DBG if (SectionObject->SharedCacheMap) { CcCopyWrite(FileObject, (PLARGE_INTEGER)&OffsetX, PgLen, TRUE || CanWait, Vcb->ZBuffer); } Length -= PgLen;#ifdef DBG } else { MmPrint((" Can't use CcWrite to zero cache (2)\n")); }#endif //DBG#endif //USE_CcCopyWrite_TO_ZERO } }#ifndef USE_CcCopyWrite_TO_ZERO do#else //USE_CcCopyWrite_TO_ZERO while(Length)#endif //USE_CcCopyWrite_TO_ZERO { MmPrint((" CcPurgeCacheSection()\n")); if(PURGE_BLOCK_SZ > Length) { CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, FALSE); /* NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += Length; ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <= NtReqFcb->CommonFCBHeader.FileSize.QuadPart); MmPrint((" CcFlushCache()\n")); CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL ); */#ifndef ALLOW_SPARSE // UDFZeroFile__(#endif //ALLOW_SPARSE break; } else { CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset, PURGE_BLOCK_SZ, FALSE); /* NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += PURGE_BLOCK_SZ; ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <= NtReqFcb->CommonFCBHeader.FileSize.QuadPart); MmPrint((" CcFlushCache()\n")); CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL ); */#ifndef ALLOW_SPARSE // UDFZeroFile__(#endif //ALLOW_SPARSE Length -= PURGE_BLOCK_SZ; Offset += PURGE_BLOCK_SZ; } }#ifndef USE_CcCopyWrite_TO_ZERO while(Length);#endif //USE_CcCopyWrite_TO_ZERO if(VDL < Offset) NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = Offset; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BrutePoint(); } _SEH2_END;} // end UDFPurgeCacheEx_()
#endif //UDF_READ_ONLY_BUILD