1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*************************************************************************
10 * Module: UDF File System Driver (Kernel mode execution only)
13 * Contains code to handle the "Write" dispatch entry point.
15 *************************************************************************/
19 // define the file specific bug-check id
20 #define UDF_BUG_CHECK_ID UDF_FILE_WRITE
22 #ifndef UDF_READ_ONLY_BUILD
24 /*************************************************************************
26 * Function: UDFWrite()
29 * The I/O Manager will invoke this routine to handle a write
32 * Expected Interrupt Level (for execution) :
34 * IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
35 * to be deferred to a worker thread context)
37 * Return Value: STATUS_SUCCESS/Error
39 *************************************************************************/
43 PDEVICE_OBJECT DeviceObject
, // the logical volume device object
44 PIRP Irp
// I/O Request Packet
47 NTSTATUS RC
= STATUS_SUCCESS
;
48 PtrUDFIrpContext PtrIrpContext
= NULL
;
49 BOOLEAN AreWeTopLevel
= FALSE
;
51 TmPrint(("UDFWrite: , thrd:%8.8x\n",PsGetCurrentThread()));
53 FsRtlEnterFileSystem();
57 // set the top level context
58 AreWeTopLevel
= UDFIsIrpTopLevel(Irp
);
59 ASSERT(!UDFIsFSDevObj(DeviceObject
));
63 // get an IRP context structure and issue the request
64 PtrIrpContext
= UDFAllocateIrpContext(Irp
, DeviceObject
);
67 RC
= UDFCommonWrite(PtrIrpContext
, Irp
);
70 RC
= STATUS_INSUFFICIENT_RESOURCES
;
71 Irp
->IoStatus
.Status
= RC
;
72 Irp
->IoStatus
.Information
= 0;
74 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
77 } _SEH2_EXCEPT (UDFExceptionFilter(PtrIrpContext
, _SEH2_GetExceptionInformation())) {
79 RC
= UDFExceptionHandler(PtrIrpContext
, Irp
);
81 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR
, RC
);
85 IoSetTopLevelIrp(NULL
);
88 FsRtlExitFileSystem();
94 /*************************************************************************
96 * Function: UDFCommonWrite()
99 * The actual work is performed here. This routine may be invoked in one'
100 * of the two possible contexts:
101 * (a) in the context of a system worker thread
102 * (b) in the context of the original caller
104 * Expected Interrupt Level (for execution) :
108 * Return Value: STATUS_SUCCESS/Error
110 *************************************************************************/
113 PtrUDFIrpContext PtrIrpContext
,
116 NTSTATUS RC
= STATUS_SUCCESS
;
117 PIO_STACK_LOCATION IrpSp
= NULL
;
118 LARGE_INTEGER ByteOffset
;
119 ULONG WriteLength
= 0, TruncatedLength
= 0;
120 ULONG NumberBytesWritten
= 0;
121 PFILE_OBJECT FileObject
= NULL
;
122 PtrUDFFCB Fcb
= NULL
;
123 PtrUDFCCB Ccb
= NULL
;
125 PtrUDFNTRequiredFCB NtReqFcb
= NULL
;
126 PERESOURCE PtrResourceAcquired
= NULL
;
127 PERESOURCE PtrResourceAcquired2
= NULL
;
128 PVOID SystemBuffer
= NULL
;
129 // PVOID TmpBuffer = NULL;
130 // uint32 KeyValue = 0;
139 BOOLEAN CacheLocked
= FALSE
;
141 BOOLEAN CanWait
= FALSE
;
142 BOOLEAN PagingIo
= FALSE
;
143 BOOLEAN NonBufferedIo
= FALSE
;
144 BOOLEAN SynchronousIo
= FALSE
;
145 BOOLEAN IsThisADeferredWrite
= FALSE
;
146 BOOLEAN WriteToEOF
= FALSE
;
147 BOOLEAN Resized
= FALSE
;
148 BOOLEAN RecursiveWriteThrough
= FALSE
;
149 BOOLEAN WriteFileSizeToDirNdx
= FALSE
;
150 BOOLEAN ZeroBlock
= FALSE
;
151 BOOLEAN VcbAcquired
= FALSE
;
152 BOOLEAN ZeroBlockDone
= FALSE
;
154 TmPrint(("UDFCommonWrite: irp %x\n", Irp
));
159 TopIrp
= IoGetTopLevelIrp();
161 switch((ULONG
)TopIrp
) {
162 case FSRTL_FSP_TOP_LEVEL_IRP
:
163 UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n"));
165 case FSRTL_CACHE_TOP_LEVEL_IRP
:
166 UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n"));
168 case FSRTL_MOD_WRITE_TOP_LEVEL_IRP
:
169 UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n"));
171 case FSRTL_FAST_IO_TOP_LEVEL_IRP
:
172 UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n"));
176 UDFPrint((" NULL TOP_LEVEL_IRP\n"));
180 UDFPrint((" TOP_LEVEL_IRP\n"));
182 UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp
));
187 // First, get a pointer to the current I/O stack location
188 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
190 MmPrint((" Enter Irp, MDL=%x\n", Irp
->MdlAddress
));
191 if(Irp
->MdlAddress
) {
192 UDFTouch(Irp
->MdlAddress
);
195 FileObject
= IrpSp
->FileObject
;
198 // If this happens to be a MDL write complete request, then
199 // allocated MDL can be freed. This may cause a recursive write
200 // back into the FSD.
201 if (IrpSp
->MinorFunction
& IRP_MN_COMPLETE
) {
202 // Caller wants to tell the Cache Manager that a previously
203 // allocated MDL can be freed.
204 UDFMdlComplete(PtrIrpContext
, Irp
, IrpSp
, FALSE
);
205 // The IRP has been completed.
206 try_return(RC
= STATUS_SUCCESS
);
209 // If this is a request at IRQL DISPATCH_LEVEL, then post the request
210 if (IrpSp
->MinorFunction
& IRP_MN_DPC
) {
211 try_return(RC
= STATUS_PENDING
);
214 // Get the FCB and CCB pointers
215 Ccb
= (PtrUDFCCB
)(FileObject
->FsContext2
);
221 if(Fcb
->FCBFlags
& UDF_FCB_DELETED
) {
223 try_return(RC
= STATUS_TOO_LATE
);
226 // is this operation allowed ?
227 if(Vcb
->VCBFlags
& UDF_VCB_FLAGS_MEDIA_READ_ONLY
) {
228 try_return(RC
= STATUS_ACCESS_DENIED
);
230 Vcb
->VCBFlags
|= UDF_VCB_SKIP_EJECT_CHECK
;
232 // Disk based file systems might decide to verify the logical volume
233 // (if required and only if removable media are supported) at this time
234 // As soon as Tray is locked, we needn't call UDFVerifyVcb()
236 ByteOffset
= IrpSp
->Parameters
.Write
.ByteOffset
;
238 CanWait
= (PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_CAN_BLOCK
) ? TRUE
: FALSE
;
239 PagingIo
= (Irp
->Flags
& IRP_PAGING_IO
) ? TRUE
: FALSE
;
240 NonBufferedIo
= (Irp
->Flags
& IRP_NOCACHE
) ? TRUE
: FALSE
;
241 SynchronousIo
= (FileObject
->Flags
& FO_SYNCHRONOUS_IO
) ? TRUE
: FALSE
;
242 UDFPrint((" Flags: %s; %s; %s; %s; Irp(W): %8.8x\n",
243 CanWait
? "Wt" : "nw", PagingIo
? "Pg" : "np",
244 NonBufferedIo
? "NBuf" : "buff", SynchronousIo
? "Snc" : "Asc",
247 NtReqFcb
= Fcb
->NTRequiredFCB
;
249 Res1Acq
= UDFIsResourceAcquired(&(NtReqFcb
->MainResource
));
251 Res1Acq
= PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_RES1_ACQ
;
253 Res2Acq
= UDFIsResourceAcquired(&(NtReqFcb
->PagingIoResource
));
255 Res2Acq
= PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_RES2_ACQ
;
259 (Fcb
->NodeIdentifier
.NodeType
!= UDF_NODE_TYPE_VCB
)) {
260 if((Fcb
->NodeIdentifier
.NodeType
!= UDF_NODE_TYPE_VCB
) &&
261 UDFIsAStream(Fcb
->FileInfo
)) {
262 UDFNotifyFullReportChange( Vcb
, Fcb
->FileInfo
,
263 FILE_NOTIFY_CHANGE_STREAM_WRITE
,
264 FILE_ACTION_MODIFIED_STREAM
);
266 UDFNotifyFullReportChange( Vcb
, Fcb
->FileInfo
,
267 FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_LAST_ACCESS
,
268 FILE_ACTION_MODIFIED
);
272 // Get some of the parameters supplied to us
273 WriteLength
= IrpSp
->Parameters
.Write
.Length
;
274 if (WriteLength
== 0) {
275 // a 0 byte write can be immediately succeeded
276 if (SynchronousIo
&& !PagingIo
&& NT_SUCCESS(RC
)) {
277 // NT expects changing CurrentByteOffset to zero in this case
278 FileObject
->CurrentByteOffset
.QuadPart
= 0;
283 // If this is the normal file we have to check for
284 // write access according to the current state of the file locks.
286 !FsRtlCheckLockForWriteAccess( &(NtReqFcb
->FileLock
), Irp
) ) {
287 try_return( RC
= STATUS_FILE_LOCK_CONFLICT
);
291 // Is this a write of the volume itself ?
293 if (Fcb
->NodeIdentifier
.NodeType
== UDF_NODE_TYPE_VCB
) {
294 // Yup, we need to send this on to the disk driver after
295 // validation of the offset and length.
298 try_return(RC
= STATUS_PENDING
);
299 // I dislike the idea of writing to not locked media
300 if(!(Vcb
->VCBFlags
& UDF_VCB_FLAGS_VOLUME_LOCKED
)) {
301 try_return(RC
= STATUS_ACCESS_DENIED
);
304 if(PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_FLUSH2_REQUIRED
) {
306 UDFPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n"));
307 PtrIrpContext
->IrpContextFlags
&= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED
;
309 if(!(Vcb
->VCBFlags
& UDF_VCB_FLAGS_RAW_DISK
)) {
310 UDFCloseAllSystemDelayedInDir(Vcb
, Vcb
->RootDirFCB
->FileInfo
);
312 #ifdef UDF_DELAYED_CLOSE
313 UDFCloseAllDelayed(Vcb
);
314 #endif //UDF_DELAYED_CLOSE
318 // Acquire the volume resource exclusive
319 UDFAcquireResourceExclusive(&(Vcb
->VCBResource
), TRUE
);
320 PtrResourceAcquired
= &(Vcb
->VCBResource
);
322 // I dislike the idea of writing to mounted media too, but M$ has another point of view...
323 if(Vcb
->VCBFlags
& UDF_VCB_FLAGS_VOLUME_MOUNTED
) {
324 // flush system cache
325 UDFFlushLogicalVolume(NULL
, NULL
, Vcb
, 0);
327 #if defined(_MSC_VER) && !defined(__clang__)
330 CollectStatistics(Vcb
, MetaDataWrites
);
331 CollectStatisticsEx(Vcb
, MetaDataWriteBytes
, NumberBytesWritten
);
334 // Forward the request to the lower level driver
335 // Lock the callers buffer
336 if (!NT_SUCCESS(RC
= UDFLockCallersBuffer(PtrIrpContext
, Irp
, TRUE
, WriteLength
))) {
339 SystemBuffer
= UDFGetCallersBuffer(PtrIrpContext
, Irp
);
341 try_return(RC
= STATUS_INVALID_USER_BUFFER
);
342 // Indicate, that volume contents can change after this operation
343 // This flag will force VerifyVolume in future
344 UDFPrint((" set UnsafeIoctl\n"));
345 Vcb
->VCBFlags
|= UDF_VCB_FLAGS_UNSAFE_IOCTL
;
346 // Make sure, that volume will never be quick-remounted
347 // It is very important for ChkUdf utility.
349 // Perform actual Write
350 RC
= UDFTWrite(Vcb
, SystemBuffer
, WriteLength
,
351 (ULONG
)(ByteOffset
.QuadPart
>> Vcb
->BlockSizeBits
),
352 &NumberBytesWritten
);
353 UDFUnlockCallersBuffer(PtrIrpContext
, Irp
, SystemBuffer
);
357 if(Vcb
->VCBFlags
& UDF_VCB_FLAGS_VOLUME_READ_ONLY
) {
358 try_return(RC
= STATUS_ACCESS_DENIED
);
361 // back pressure for very smart and fast system cache ;)
364 if(Vcb
->VerifyCtx
.QueuedCount
||
365 Vcb
->VerifyCtx
.ItemCount
>= UDF_MAX_VERIFY_CACHE
) {
366 UDFVVerify(Vcb
, UFD_VERIFY_FLAG_WAIT
);
369 if(Vcb
->VerifyCtx
.ItemCount
> UDF_SYS_CACHE_STOP_THR
) {
370 UDFVVerify(Vcb
, UFD_VERIFY_FLAG_WAIT
);
374 // The FSD (if it is a "nice" FSD) should check whether it is
375 // convenient to allow the write to proceed by utilizing the
376 // CcCanIWrite() function call. If it is not convenient to perform
377 // the write at this time, we should defer the request for a while.
378 // The check should not however be performed for non-cached write
379 // operations. To determine whether we are retrying the operation
380 // or now, use Flags in the IrpContext structure we have created
382 IsThisADeferredWrite
= (PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_DEFERRED_WRITE
) ? TRUE
: FALSE
;
384 if (!NonBufferedIo
) {
385 MmPrint((" CcCanIWrite()\n"));
386 if (!CcCanIWrite(FileObject
, WriteLength
, CanWait
, IsThisADeferredWrite
)) {
387 // Cache Manager and/or the VMM does not want us to perform
388 // the write at this time. Post the request.
389 PtrIrpContext
->IrpContextFlags
|= UDF_IRP_CONTEXT_DEFERRED_WRITE
;
390 UDFPrint(("UDFCommonWrite: Defer write\n"));
391 MmPrint((" CcDeferWrite()\n"));
392 CcDeferWrite(FileObject
, UDFDeferredWriteCallBack
, PtrIrpContext
, Irp
, WriteLength
, IsThisADeferredWrite
);
393 try_return(RC
= STATUS_PENDING
);
397 // If the write request is directed to a page file,
398 // send the request directly to the disk
399 if (Fcb
->FCBFlags
& UDF_FCB_PAGE_FILE
) {
400 NonBufferedIo
= TRUE
;
403 // We can continue. Check whether this write operation is targeted
404 // to a directory object in which case the UDF FSD will disallow
405 // the write request.
406 if (Fcb
->FCBFlags
& UDF_FCB_DIRECTORY
) {
407 RC
= STATUS_INVALID_DEVICE_REQUEST
;
411 // Validate start offset and length supplied.
412 // Here is a special check that determines whether the caller wishes to
413 // begin the write at current end-of-file (whatever the value of that
415 if(ByteOffset
.HighPart
== (LONG
)0xFFFFFFFF) {
416 if(ByteOffset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
) {
418 ByteOffset
= NtReqFcb
->CommonFCBHeader
.FileSize
;
420 if(ByteOffset
.LowPart
== FILE_USE_FILE_POINTER_POSITION
) {
421 ByteOffset
= FileObject
->CurrentByteOffset
;
425 // Check if this volume has already been shut down. If it has, fail
426 // this write request.
427 if (Vcb
->VCBFlags
& UDF_VCB_FLAGS_SHUTDOWN
) {
428 try_return(RC
= STATUS_TOO_LATE
);
431 // Paging I/O write operations are special. If paging i/o write
432 // requests begin beyond end-of-file, the request should be no-oped
434 // requests extend beyond current end of file, they should be truncated
435 // to current end-of-file.
436 if(PagingIo
&& (WriteToEOF
|| ((ByteOffset
.QuadPart
+ WriteLength
) > NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
))) {
437 if (ByteOffset
.QuadPart
> NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
) {
440 TruncatedLength
= (ULONG
)(NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
- ByteOffset
.QuadPart
);
442 if(!TruncatedLength
) try_return(RC
= STATUS_SUCCESS
);
444 TruncatedLength
= WriteLength
;
447 #if defined(_MSC_VER) && !defined(__clang__)
450 CollectStatistics(Vcb
, UserFileWrites
);
451 CollectStatisticsEx(Vcb
, UserFileWriteBytes
, NumberBytesWritten
);
455 // There are certain complications that arise when the same file stream
456 // has been opened for cached and non-cached access. The FSD is then
457 // responsible for maintaining a consistent view of the data seen by
459 // If this happens to be a non-buffered I/O, we should __try to flush the
460 // cached data (if some other file object has already initiated caching
461 // on the file stream). We should also __try to purge the cached
462 // information though the purge will probably fail if the file has been
463 // mapped into some process' virtual address space
464 // WARNING !!! we should not flush data beyond valid data length
465 if ( NonBufferedIo
&&
467 NtReqFcb
->SectionObject
.DataSectionObject
&&
469 (ByteOffset
.QuadPart
< NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
)) {
472 // Try to acquire the FCB MainResource exclusively
473 if(!UDFAcquireResourceExclusive(&(NtReqFcb
->MainResource
), CanWait
)) {
474 try_return(RC
= STATUS_PENDING
);
476 PtrResourceAcquired
= &(NtReqFcb
->MainResource
);
480 // We hold PagingIo shared around the flush to fix a
481 // cache coherency problem.
482 UDFAcquireSharedStarveExclusive(&(NtReqFcb
->PagingIoResource
), TRUE
);
483 PtrResourceAcquired2
= &(NtReqFcb
->PagingIoResource
);
486 // Flush and then attempt to purge the cache
487 if((ByteOffset
.QuadPart
+ TruncatedLength
) > NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
) {
488 NumberBytesWritten
= TruncatedLength
;
490 NumberBytesWritten
= (ULONG
)(NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
- ByteOffset
.QuadPart
);
493 MmPrint((" CcFlushCache()\n"));
494 CcFlushCache(&(NtReqFcb
->SectionObject
), &ByteOffset
, NumberBytesWritten
, &(Irp
->IoStatus
));
496 if(PtrResourceAcquired2
) {
497 UDFReleaseResource(&(NtReqFcb
->PagingIoResource
));
498 PtrResourceAcquired2
= NULL
;
500 // If the flush failed, return error to the caller
501 if (!NT_SUCCESS(RC
= Irp
->IoStatus
.Status
)) {
502 NumberBytesWritten
= 0;
507 // Acquiring and immediately dropping the resource serializes
508 // us behind any other writes taking place (either from the
509 // lazy writer or modified page writer).
510 UDFAcquireResourceExclusive(&(NtReqFcb
->PagingIoResource
), TRUE
);
511 UDFReleaseResource(&(NtReqFcb
->PagingIoResource
));
514 // Attempt the purge and ignore the return code
515 MmPrint((" CcPurgeCacheSection()\n"));
516 CcPurgeCacheSection(&(NtReqFcb
->SectionObject
), &ByteOffset
,
517 NumberBytesWritten
, FALSE
);
518 NumberBytesWritten
= 0;
519 // We are finished with our flushing and purging
520 if(PtrResourceAcquired
) {
521 UDFReleaseResource(PtrResourceAcquired
);
522 PtrResourceAcquired
= NULL
;
526 // Determine if we were called by the lazywriter.
527 // We reuse 'IsThisADeferredWrite' here to decrease stack usage
528 IsThisADeferredWrite
= (NtReqFcb
->LazyWriterThreadID
== (uint32
)PsGetCurrentThread());
530 // Acquire the appropriate FCB resource
532 // PagingIoResource is already acquired exclusive
533 // on LazyWrite condition (see UDFAcqLazyWrite())
534 ASSERT(NonBufferedIo
);
535 if(!IsThisADeferredWrite
) {
537 // Try to acquire the FCB PagingIoResource exclusive
538 if(!UDFAcquireResourceExclusive(&(NtReqFcb
->PagingIoResource
), CanWait
)) {
539 try_return(RC
= STATUS_PENDING
);
541 // Remember the resource that was acquired
542 PtrResourceAcquired2
= &(NtReqFcb
->PagingIoResource
);
546 // Try to acquire the FCB MainResource shared
549 if(!UDFAcquireResourceExclusive(&(NtReqFcb
->PagingIoResource
), CanWait
)) {
550 //if(!UDFAcquireSharedWaitForExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
551 try_return(RC
= STATUS_PENDING
);
553 PtrResourceAcquired2
= &(NtReqFcb
->PagingIoResource
);
557 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb
);
558 if(!UDFAcquireResourceExclusive(&(NtReqFcb
->MainResource
), CanWait
)) {
559 //if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
560 try_return(RC
= STATUS_PENDING
);
562 PtrResourceAcquired
= &(NtReqFcb
->MainResource
);
565 // Remember the resource that was acquired
568 // Set the flag indicating if Fast I/O is possible
569 NtReqFcb
->CommonFCBHeader
.IsFastIoPossible
= UDFIsFastIoPossible(Fcb
);
570 /* if(NtReqFcb->CommonFCBHeader.IsFastIoPossible == FastIoIsPossible) {
571 NtReqFcb->CommonFCBHeader.IsFastIoPossible = FastIoIsQuestionable;
574 if ( (Irp
->Flags
& IRP_SYNCHRONOUS_PAGING_IO
) &&
575 (PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_NOT_TOP_LEVEL
)) {
577 // This clause determines if the top level request was
578 // in the FastIo path.
579 if ((ULONG
)TopIrp
> FSRTL_MAX_TOP_LEVEL_IRP_FLAG
) {
581 PIO_STACK_LOCATION IrpStack
;
582 ASSERT( TopIrp
->Type
== IO_TYPE_IRP
);
583 IrpStack
= IoGetCurrentIrpStackLocation(TopIrp
);
585 // Finally this routine detects if the Top irp was a
586 // write to this file and thus we are the writethrough.
587 if ((IrpStack
->MajorFunction
== IRP_MJ_WRITE
) &&
588 (IrpStack
->FileObject
->FsContext
== FileObject
->FsContext
)) {
590 RecursiveWriteThrough
= TRUE
;
591 PtrIrpContext
->IrpContextFlags
|= UDF_IRP_CONTEXT_WRITE_THROUGH
;
596 // Here is the deal with ValidDataLength and FileSize:
598 // Rule 1: PagingIo is never allowed to extend file size.
600 // Rule 2: Only the top level requestor may extend Valid
601 // Data Length. This may be paging IO, as when a
602 // a user maps a file, but will never be as a result
603 // of cache lazy writer writes since they are not the
604 // top level request.
606 // Rule 3: If, using Rules 1 and 2, we decide we must extend
607 // file size or valid data, we take the Fcb exclusive.
609 // Check whether the current request will extend the file size,
610 // or the valid data length (if the FSD supports the concept of a
611 // valid data length associated with the file stream). In either case,
612 // inform the Cache Manager at this time using CcSetFileSizes() about
613 // the new file length. Note that real FSD implementations will have to
614 // first allocate enough on-disk space at this point (before they
615 // inform the Cache Manager about the new size) to ensure that the write
616 // will subsequently not fail due to lack of disk space.
618 OldVDL
= NtReqFcb
->CommonFCBHeader
.ValidDataLength
.QuadPart
;
619 ZeroBlock
= (ByteOffset
.QuadPart
> OldVDL
);
622 !RecursiveWriteThrough
&&
623 !IsThisADeferredWrite
) {
627 ExtendFS
= (ByteOffset
.QuadPart
+ TruncatedLength
> NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
);
629 if( WriteToEOF
|| ZeroBlock
|| ExtendFS
) {
630 // we are extending the file;
633 try_return(RC
= STATUS_PENDING
);
635 // Release any resources acquired above ...
636 if (PtrResourceAcquired2
) {
637 UDFReleaseResource(PtrResourceAcquired2
);
638 PtrResourceAcquired2
= NULL
;
640 if (PtrResourceAcquired
) {
641 UDFReleaseResource(PtrResourceAcquired
);
642 PtrResourceAcquired
= NULL
;
644 if(!UDFAcquireResourceShared(&(Vcb
->VCBResource
), CanWait
)) {
645 try_return(RC
= STATUS_PENDING
);
649 // Try to acquire the FCB MainResource exclusively
650 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb
);
651 if(!UDFAcquireResourceExclusive(&(NtReqFcb
->MainResource
), CanWait
)) {
652 try_return(RC
= STATUS_PENDING
);
654 // Remember the resource that was acquired
655 PtrResourceAcquired
= &(NtReqFcb
->MainResource
);
660 AdPrint((" Try to acquire PagingIoRes\n"));
661 UDFAcquireResourceExclusive(&(NtReqFcb
->PagingIoResource
), TRUE
);
662 PtrResourceAcquired2
= &(NtReqFcb
->PagingIoResource
);
664 AdPrint((" PagingIoRes Ok, Resizing...\n"));
667 RC
= UDFResizeFile__(Vcb
, Fcb
->FileInfo
, ByteOffset
.QuadPart
+ TruncatedLength
);
669 if(!NT_SUCCESS(RC
)) {
670 if(PtrResourceAcquired2
) {
671 UDFReleaseResource(&(NtReqFcb
->PagingIoResource
));
672 PtrResourceAcquired2
= NULL
;
677 // ... and inform the Cache Manager about it
678 NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
= ByteOffset
.QuadPart
+ TruncatedLength
;
679 NtReqFcb
->CommonFCBHeader
.AllocationSize
.QuadPart
= UDFGetFileAllocationSize(Vcb
, Fcb
->FileInfo
);
680 if(!Vcb
->LowFreeSpace
) {
681 NtReqFcb
->CommonFCBHeader
.AllocationSize
.QuadPart
+= (PAGE_SIZE
*9-1);
683 NtReqFcb
->CommonFCBHeader
.AllocationSize
.QuadPart
+= (PAGE_SIZE
-1);
685 NtReqFcb
->CommonFCBHeader
.AllocationSize
.LowPart
&= ~(PAGE_SIZE
-1);
688 UDFPrint(("UDFCommonWrite: Set size %x (alloc size %x)\n", ByteOffset
.LowPart
+ TruncatedLength
, NtReqFcb
->CommonFCBHeader
.AllocationSize
.LowPart
));
689 if (CcIsFileCached(FileObject
)) {
691 MmPrint((" CcSetFileSizes()\n"));
692 CcSetFileSizes(FileObject
, (PCC_FILE_SIZES
)&(NtReqFcb
->CommonFCBHeader
.AllocationSize
));
693 NtReqFcb
->NtReqFCBFlags
|= UDF_NTREQ_FCB_MODIFIED
;
695 // Attempt to Zero newly added fragment
696 // and ignore the return code
697 // This should be done to inform cache manager
698 // that given extent has no cached data
699 // (Otherwise, CM sometimes thinks that it has)
701 NtReqFcb
->NtReqFCBFlags
|= UDF_NTREQ_FCB_MODIFIED
;
702 ThPrint((" UDFZeroDataEx(1)\n"));
703 UDFZeroDataEx(NtReqFcb
,
705 /*ByteOffset.QuadPart*/ NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
- OldVDL
,
706 CanWait
, Vcb
, FileObject
);
708 ZeroBlockDone
= TRUE
;
712 if (PtrResourceAcquired2
) {
713 UDFReleaseResource(PtrResourceAcquired2
);
714 PtrResourceAcquired2
= NULL
;
717 // Inform any pending IRPs (notify change directory).
718 if(UDFIsAStream(Fcb
->FileInfo
)) {
719 UDFNotifyFullReportChange( Vcb
, Fcb
->FileInfo
,
720 FILE_NOTIFY_CHANGE_STREAM_SIZE
,
721 FILE_ACTION_MODIFIED_STREAM
);
723 UDFNotifyFullReportChange( Vcb
, Fcb
->FileInfo
,
724 FILE_NOTIFY_CHANGE_SIZE
,
725 FILE_ACTION_MODIFIED
);
731 #ifdef UDF_DISABLE_SYSTEM_CACHE_MANAGER
732 NonBufferedIo
= TRUE
;
734 if(Fcb
&& Fcb
->FileInfo
&& Fcb
->FileInfo
->Dloc
) {
735 AdPrint(("UDFCommonWrite: DataLoc %x, Mapping %x\n", Fcb
->FileInfo
->Dloc
->DataLoc
, Fcb
->FileInfo
->Dloc
->DataLoc
.Mapping
));
738 // Branch here for cached vs non-cached I/O
739 if (!NonBufferedIo
) {
741 // The caller wishes to perform cached I/O. Initiate caching if
742 // this is the first cached I/O operation using this file object
743 if (!FileObject
->PrivateCacheMap
) {
744 // This is the first cached I/O operation. You must ensure
745 // that the FCB Common FCB Header contains valid sizes at this time
746 UDFPrint(("UDFCommonWrite: Init system cache\n"));
747 MmPrint((" CcInitializeCacheMap()\n"));
748 CcInitializeCacheMap(FileObject
, (PCC_FILE_SIZES
)(&(NtReqFcb
->CommonFCBHeader
.AllocationSize
)),
749 FALSE
, // We will not utilize pin access for this file
750 &(UDFGlobalData
.CacheMgrCallBacks
), // callbacks
751 NtReqFcb
); // The context used in callbacks
752 MmPrint((" CcSetReadAheadGranularity()\n"));
753 CcSetReadAheadGranularity(FileObject
, Vcb
->SystemCacheGran
);
757 if(ZeroBlock
&& !ZeroBlockDone
) {
758 ThPrint((" UDFZeroDataEx(2)\n"));
759 UDFZeroDataEx(NtReqFcb
,
761 /*ByteOffset.QuadPart*/ ByteOffset
.QuadPart
+ TruncatedLength
- OldVDL
,
762 CanWait
, Vcb
, FileObject
);
763 if(ByteOffset
.LowPart
& (PAGE_SIZE
-1)) {
767 WriteFileSizeToDirNdx
= (PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_WRITE_THROUGH
) ?
769 // Check and see if this request requires a MDL returned to the caller
770 if (IrpSp
->MinorFunction
& IRP_MN_MDL
) {
771 // Caller does want a MDL returned. Note that this mode
772 // implies that the caller is prepared to block
773 MmPrint((" CcPrepareMdlWrite()\n"));
774 // CcPrepareMdlWrite(FileObject, &ByteOffset, TruncatedLength, &(Irp->MdlAddress), &(Irp->IoStatus));
775 // NumberBytesWritten = Irp->IoStatus.Information;
776 // RC = Irp->IoStatus.Status;
778 NumberBytesWritten
= 0;
779 RC
= STATUS_INVALID_PARAMETER
;
784 if(NtReqFcb
->SectionObject
.DataSectionObject
&&
785 TruncatedLength
>= 0x10000 &&
786 ByteOffset
.LowPart
&&
787 !(ByteOffset
.LowPart
& 0x00ffffff)) {
789 //if(WinVer_Id() < WinVer_2k) {
790 //LARGE_INTEGER flush_offs;
791 //flush_offs.QuadPart = ByteOffset.QuadPart - 0x100*0x10000;
792 MmPrint((" CcFlushCache() 16Mb\n"));
793 //CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, 0x100*0x10000, &(Irp->IoStatus));
795 // there was a nice idea: flush just previous part. But it doesn't work
796 CcFlushCache(&(NtReqFcb
->SectionObject
), NULL
, 0, &(Irp
->IoStatus
));
800 // This is a regular run-of-the-mill cached I/O request. Let the
801 // Cache Manager worry about it!
802 // First though, we need a buffer pointer (address) that is valid
804 // We needn't call CcZeroData 'cause udf_info.cpp will care about it
805 SystemBuffer
= UDFGetCallersBuffer(PtrIrpContext
, Irp
);
807 try_return(RC
= STATUS_INVALID_USER_BUFFER
);
808 ASSERT(SystemBuffer
);
809 NtReqFcb
->NtReqFCBFlags
|= UDF_NTREQ_FCB_MODIFIED
;
810 PerfPrint(("UDFCommonWrite: CcCopyWrite %x bytes at %x\n", TruncatedLength
, ByteOffset
.LowPart
));
811 MmPrint((" CcCopyWrite()\n"));
812 if(!CcCopyWrite(FileObject
, &(ByteOffset
), TruncatedLength
, CanWait
, SystemBuffer
)) {
813 // The caller was not prepared to block and data is not immediately
814 // available in the system cache
815 // Mark Irp Pending ...
816 try_return(RC
= STATUS_PENDING
);
819 UDFUnlockCallersBuffer(PtrIrpContext
, Irp
, SystemBuffer
);
822 NumberBytesWritten
= TruncatedLength
;
828 MmPrint((" Write NonBufferedIo\n"));
830 // We needn't call CcZeroData here (like in Fat driver)
831 // 'cause we've already done it above
832 // (see call to UDFZeroDataEx() )
833 if (!RecursiveWriteThrough
&&
834 !IsThisADeferredWrite
&&
835 (OldVDL
< ByteOffset
.QuadPart
)) {
837 ASSERT(!ZeroBlockDone
);
839 UDFZeroDataEx(NtReqFcb
,
841 /*ByteOffset.QuadPart*/ ByteOffset
.QuadPart
- OldVDL
,
842 CanWait
, Vcb
, FileObject
);
844 if(OldVDL
< (ByteOffset
.QuadPart
+ TruncatedLength
)) {
845 NtReqFcb
->CommonFCBHeader
.ValidDataLength
.QuadPart
= ByteOffset
.QuadPart
+ TruncatedLength
;
849 if((ULONG
)TopIrp
== FSRTL_MOD_WRITE_TOP_LEVEL_IRP
) {
850 UDFPrint(("FSRTL_MOD_WRITE_TOP_LEVEL_IRP => CanWait\n"));
853 if((ULONG
)TopIrp
== FSRTL_CACHE_TOP_LEVEL_IRP
) {
854 UDFPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n"));
858 if(NtReqFcb
->AcqSectionCount
|| NtReqFcb
->AcqFlushCount
) {
859 MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb
->AcqSectionCount
, NtReqFcb
->AcqFlushCount
));
863 /* if((TopIrp != Irp)) {
864 UDFPrint(("(TopIrp != Irp) => CanWait\n"));
868 if(KeGetCurrentIrql() > PASSIVE_LEVEL
) {
869 MmPrint((" !PASSIVE_LEVEL\n"));
871 PtrIrpContext
->IrpContextFlags
|= UDF_IRP_CONTEXT_FORCED_POST
;
873 // Successful check will cause WCache lock
874 if(!CanWait
&& UDFIsFileCached__(Vcb
, Fcb
->FileInfo
, ByteOffset
.QuadPart
, TruncatedLength
, TRUE
)) {
875 UDFPrint(("UDFCommonWrite: Cached => CanWait\n"));
879 // Send the request to lower level drivers
881 UDFPrint(("UDFCommonWrite: Post physical write %x bytes at %x\n", TruncatedLength
, ByteOffset
.LowPart
));
883 try_return(RC
= STATUS_PENDING
);
887 if(UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb
->PagingIoResource
))) {
888 PtrResourceAcquired2
= &(NtReqFcb
->PagingIoResource
);
892 PerfPrint(("UDFCommonWrite: Physical write %x bytes at %x\n", TruncatedLength
, ByteOffset
.LowPart
));
894 // Lock the callers buffer
895 if (!NT_SUCCESS(RC
= UDFLockCallersBuffer(PtrIrpContext
, Irp
, TRUE
, TruncatedLength
))) {
899 SystemBuffer
= UDFGetCallersBuffer(PtrIrpContext
, Irp
);
901 try_return(RC
= STATUS_INVALID_USER_BUFFER
);
903 NtReqFcb
->NtReqFCBFlags
|= UDF_NTREQ_FCB_MODIFIED
;
904 RC
= UDFWriteFile__(Vcb
, Fcb
->FileInfo
, ByteOffset
.QuadPart
, TruncatedLength
,
905 CacheLocked
, (PCHAR
)SystemBuffer
, &NumberBytesWritten
);
907 UDFUnlockCallersBuffer(PtrIrpContext
, Irp
, SystemBuffer
);
909 #if defined(_MSC_VER) && !defined(__clang__)
912 CollectStatistics(Vcb
, UserDiskWrites
);
914 CollectStatistics2(Vcb
, NonCachedDiskWrites
);
917 WriteFileSizeToDirNdx
= TRUE
;
927 WCacheEODirect__(&(Vcb
->FastCache
), Vcb
);
930 // Release any resources acquired here ...
931 if(PtrResourceAcquired2
) {
932 UDFReleaseResource(PtrResourceAcquired2
);
934 if(PtrResourceAcquired
) {
936 (PtrResourceAcquired
==
937 &(NtReqFcb
->MainResource
))) {
938 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb
);
940 UDFReleaseResource(PtrResourceAcquired
);
943 UDFReleaseResource(&(Vcb
->VCBResource
));
946 // Post IRP if required
947 if(RC
== STATUS_PENDING
) {
949 // Lock the callers buffer here. Then invoke a common routine to
950 // perform the post operation.
951 if (!(IrpSp
->MinorFunction
& IRP_MN_MDL
)) {
952 RC
= UDFLockCallersBuffer(PtrIrpContext
, Irp
, FALSE
, WriteLength
);
953 ASSERT(NT_SUCCESS(RC
));
957 PtrIrpContext
->IrpContextFlags
|= UDF_IRP_CONTEXT_RES1_ACQ
;
960 PtrIrpContext
->IrpContextFlags
|= UDF_IRP_CONTEXT_RES2_ACQ
;
964 // Perform the post operation which will mark the IRP pending
965 // and will return STATUS_PENDING back to us
966 RC
= UDFPostRequest(PtrIrpContext
, Irp
);
969 // For synchronous I/O, the FSD must maintain the current byte offset
970 // Do not do this however, if I/O is marked as paging-io
971 if (SynchronousIo
&& !PagingIo
&& NT_SUCCESS(RC
)) {
972 FileObject
->CurrentByteOffset
.QuadPart
= ByteOffset
.QuadPart
+ NumberBytesWritten
;
974 // If the write completed successfully and this was not a paging-io
975 // operation, set a flag in the CCB that indicates that a write was
976 // performed and that the file time should be updated at cleanup
977 if (NT_SUCCESS(RC
) && !PagingIo
) {
978 Ccb
->CCBFlags
|= UDF_CCB_MODIFIED
;
979 // If the file size was changed, set a flag in the FCB indicating that
981 FileObject
->Flags
|= FO_FILE_MODIFIED
;
983 if(!WriteFileSizeToDirNdx
) {
984 FileObject
->Flags
|= FO_FILE_SIZE_CHANGED
;
986 ASize
= UDFGetFileAllocationSize(Vcb
, Fcb
->FileInfo
);
987 UDFSetFileSizeInDirNdx(Vcb
, Fcb
->FileInfo
, &ASize
);
990 // Update ValidDataLength
991 if(!IsThisADeferredWrite
&&
993 if(NtReqFcb
->CommonFCBHeader
.ValidDataLength
.QuadPart
< (ByteOffset
.QuadPart
+ NumberBytesWritten
)) {
995 NtReqFcb
->CommonFCBHeader
.ValidDataLength
.QuadPart
=
996 min(NtReqFcb
->CommonFCBHeader
.FileSize
.QuadPart
,
997 ByteOffset
.QuadPart
+ NumberBytesWritten
);
1002 // If the request failed, and we had done some nasty stuff like
1003 // extending the file size (including informing the Cache Manager
1004 // about the new file size), and allocating on-disk space etc., undo
1007 // Can complete the IRP here if no exception was encountered
1008 if(!_SEH2_AbnormalTermination() &&
1010 Irp
->IoStatus
.Status
= RC
;
1011 Irp
->IoStatus
.Information
= NumberBytesWritten
;
1013 MmPrint((" Complete Irp, MDL=%x\n", Irp
->MdlAddress
));
1014 if(Irp
->MdlAddress
) {
1015 UDFTouch(Irp
->MdlAddress
);
1017 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
1019 // Free up the Irp Context
1020 UDFReleaseIrpContext(PtrIrpContext
);
1022 } // can we complete the IRP ?
1023 } _SEH2_END
; // end of "__finally" processing
1027 } // end UDFCommonWrite()
1029 /*************************************************************************
1031 * Function: UDFDeferredWriteCallBack()
1034 * Invoked by the cache manager in the context of a worker thread.
1035 * Typically, you can simply post the request at this point (just
1036 * as you would have if the original request could not block) to
1037 * perform the write in the context of a system worker thread.
1039 * Expected Interrupt Level (for execution) :
1041 * IRQL_PASSIVE_LEVEL
1043 * Return Value: None
1045 *************************************************************************/
1048 UDFDeferredWriteCallBack(
1049 IN PVOID Context1
, // Should be PtrIrpContext
1050 IN PVOID Context2
// Should be Irp
1053 UDFPrint(("UDFDeferredWriteCallBack\n"));
1054 // We should typically simply post the request to our internal
1055 // queue of posted requests (just as we would if the original write
1056 // could not be completed because the caller could not block).
1057 // Once we post the request, return from this routine. The write
1058 // will then be retried in the context of a system worker thread
1059 UDFPostRequest((PtrUDFIrpContext
)Context1
, (PIRP
)Context2
);
1061 } // end UDFDeferredWriteCallBack()
1063 /*************************************************************************
1065 *************************************************************************/
1067 #define USE_CcCopyWrite_TO_ZERO
1071 PtrUDFNTRequiredFCB NtReqFcb
,
1074 //#ifndef ALLOW_SPARSE
1076 //#endif //ALLOW_SPARSE
1078 PFILE_OBJECT FileObject
1082 #ifdef USE_CcCopyWrite_TO_ZERO
1084 #endif //USE_CcCopyWrite_TO_ZERO
1086 // We'll just purge cache section here,
1087 // without call to CcZeroData()
1088 // 'cause udf_info.cpp will care about it
1090 #define PURGE_BLOCK_SZ 0x10000000
1092 // NOTE: if FS engine doesn't suport
1093 // sparse/unrecorded areas, CcZeroData must be called
1094 // In this case we'll see some recursive WRITE requests
1097 MmPrint((" UDFPurgeCacheEx_(): Offs: %I64x, ", Offset
));
1098 MmPrint((" Len: %lx\n", Length
));
1099 SECTION_OBJECT_POINTERS
* SectionObject
= &(NtReqFcb
->SectionObject
);
1101 LONGLONG Offset0
, OffsetX
, VDL
;
1104 if((Off_l
= ((ULONG
)Offset0
& (PAGE_SIZE
-1)))) {
1107 // ...|dddddddddddd00000|....
1109 #ifndef USE_CcCopyWrite_TO_ZERO
1110 *((PULONG
)&Offset0
) &= ~(PAGE_SIZE
-1);
1111 MmPrint((" CcFlushCache(s) Offs %I64x, Len %x\n", Offset0
, Off_l
));
1112 CcFlushCache( SectionObject
, (PLARGE_INTEGER
)&Offset0
, Off_l
, NULL
);
1113 #else //USE_CcCopyWrite_TO_ZERO
1114 // ...|ddddd000000000000|....
1116 PgLen
= PAGE_SIZE
- Off_l
; /*(*((PULONG)&Offset) & (PAGE_SIZE-1))*/
1119 PgLen
= (ULONG
)Length
;
1121 MmPrint((" ZeroCache (CcWrite) Offs %I64x, Len %x\n", Offset
, PgLen
));
1123 if(FileObject
&& Vcb
) {
1128 if (SectionObject
->SharedCacheMap
) {
1129 CcCopyWrite(FileObject
, (PLARGE_INTEGER
)&Offset
, PgLen
, TRUE
|| CanWait
, Vcb
->ZBuffer
);
1136 MmPrint((" Can't use CcWrite to zero cache\n"));
1139 #endif //USE_CcCopyWrite_TO_ZERO
1141 VDL
= NtReqFcb
->CommonFCBHeader
.ValidDataLength
.QuadPart
;
1142 OffsetX
= Offset
+Length
;
1143 if((Off_l
= ((ULONG
)OffsetX
& (PAGE_SIZE
-1)))) {
1146 #ifndef USE_CcCopyWrite_TO_ZERO
1147 Off_l
= ( (ULONG
)(VDL
-OffsetX
) > PAGE_SIZE
) ?
1148 (PAGE_SIZE
- Off_l
) :
1149 ((ULONG
)(VDL
-OffsetX
));
1150 *((PULONG
)&OffsetX
) &= ~(PAGE_SIZE
-1);
1151 MmPrint((" CcFlushCache(e) Offs %I64x, Len %x\n", OffsetX
, Off_l
));
1152 CcFlushCache( SectionObject
, (PLARGE_INTEGER
)&OffsetX
, Off_l
, NULL
);
1153 #else //USE_CcCopyWrite_TO_ZERO
1154 if(VDL
- OffsetX
> PAGE_SIZE
) {
1155 PgLen
= (ULONG
)OffsetX
& ~(PAGE_SIZE
-1);
1157 PgLen
= (ULONG
)(VDL
- OffsetX
) & ~(PAGE_SIZE
-1);
1159 // ...|000000000000ddddd|....
1161 MmPrint((" ZeroCache (CcWrite - 2) Offs %I64x, Len %x\n", OffsetX
, PgLen
));
1163 if(FileObject
&& Vcb
) {
1166 if (SectionObject
->SharedCacheMap
) {
1167 CcCopyWrite(FileObject
, (PLARGE_INTEGER
)&OffsetX
, PgLen
, TRUE
|| CanWait
, Vcb
->ZBuffer
);
1172 MmPrint((" Can't use CcWrite to zero cache (2)\n"));
1175 #endif //USE_CcCopyWrite_TO_ZERO
1178 #ifndef USE_CcCopyWrite_TO_ZERO
1180 #else //USE_CcCopyWrite_TO_ZERO
1182 #endif //USE_CcCopyWrite_TO_ZERO
1184 MmPrint((" CcPurgeCacheSection()\n"));
1185 if(PURGE_BLOCK_SZ
> Length
) {
1186 CcPurgeCacheSection(SectionObject
, (PLARGE_INTEGER
)&Offset
,
1187 (ULONG
)Length
, FALSE
);
1189 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += Length;
1190 ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <=
1191 NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
1192 MmPrint((" CcFlushCache()\n"));
1193 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL );
1195 #ifndef ALLOW_SPARSE
1197 #endif //ALLOW_SPARSE
1200 CcPurgeCacheSection(SectionObject
, (PLARGE_INTEGER
)&Offset
,
1201 PURGE_BLOCK_SZ
, FALSE
);
1203 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += PURGE_BLOCK_SZ;
1204 ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <=
1205 NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
1206 MmPrint((" CcFlushCache()\n"));
1207 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL );
1209 #ifndef ALLOW_SPARSE
1211 #endif //ALLOW_SPARSE
1212 Length
-= PURGE_BLOCK_SZ
;
1213 Offset
+= PURGE_BLOCK_SZ
;
1216 #ifndef USE_CcCopyWrite_TO_ZERO
1218 #endif //USE_CcCopyWrite_TO_ZERO
1220 NtReqFcb
->CommonFCBHeader
.ValidDataLength
.QuadPart
= Offset
;
1222 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
1225 } // end UDFPurgeCacheEx_()
1227 #endif //UDF_READ_ONLY_BUILD