ceaa69bda0198fd15b7a06fdf48e2c4de13fe405
[reactos.git] / drivers / filesystems / udfs / write.cpp
1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*************************************************************************
7 *
8 * File: Write.cpp
9 *
10 * Module: UDF File System Driver (Kernel mode execution only)
11 *
12 * Description:
13 * Contains code to handle the "Write" dispatch entry point.
14 *
15 *************************************************************************/
16
17 #include "udffs.h"
18
19 // define the file specific bug-check id
20 #define UDF_BUG_CHECK_ID UDF_FILE_WRITE
21
22 #ifndef UDF_READ_ONLY_BUILD
23
24 /*************************************************************************
25 *
26 * Function: UDFWrite()
27 *
28 * Description:
29 * The I/O Manager will invoke this routine to handle a write
30 * request
31 *
32 * Expected Interrupt Level (for execution) :
33 *
34 * IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
35 * to be deferred to a worker thread context)
36 *
37 * Return Value: STATUS_SUCCESS/Error
38 *
39 *************************************************************************/
40 NTSTATUS
41 NTAPI
42 UDFWrite(
43 PDEVICE_OBJECT DeviceObject, // the logical volume device object
44 PIRP Irp // I/O Request Packet
45 )
46 {
47 NTSTATUS RC = STATUS_SUCCESS;
48 PtrUDFIrpContext PtrIrpContext = NULL;
49 BOOLEAN AreWeTopLevel = FALSE;
50
51 TmPrint(("UDFWrite: , thrd:%8.8x\n",PsGetCurrentThread()));
52
53 FsRtlEnterFileSystem();
54 ASSERT(DeviceObject);
55 ASSERT(Irp);
56
57 // set the top level context
58 AreWeTopLevel = UDFIsIrpTopLevel(Irp);
59 ASSERT(!UDFIsFSDevObj(DeviceObject));
60
61 _SEH2_TRY {
62
63 // get an IRP context structure and issue the request
64 PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
65 if(PtrIrpContext) {
66
67 RC = UDFCommonWrite(PtrIrpContext, Irp);
68
69 } else {
70 RC = STATUS_INSUFFICIENT_RESOURCES;
71 Irp->IoStatus.Status = RC;
72 Irp->IoStatus.Information = 0;
73 // complete the IRP
74 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
75 }
76
77 } _SEH2_EXCEPT (UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
78
79 RC = UDFExceptionHandler(PtrIrpContext, Irp);
80
81 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
82 } _SEH2_END;
83
84 if (AreWeTopLevel) {
85 IoSetTopLevelIrp(NULL);
86 }
87
88 FsRtlExitFileSystem();
89
90 return(RC);
91 } // end UDFWrite()
92
93
94 /*************************************************************************
95 *
96 * Function: UDFCommonWrite()
97 *
98 * Description:
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
103 *
104 * Expected Interrupt Level (for execution) :
105 *
106 * IRQL_PASSIVE_LEVEL
107 *
108 * Return Value: STATUS_SUCCESS/Error
109 *
110 *************************************************************************/
111 NTSTATUS
112 UDFCommonWrite(
113 PtrUDFIrpContext PtrIrpContext,
114 PIRP Irp)
115 {
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;
124 PVCB Vcb = 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;
131 PIRP TopIrp;
132
133 LONGLONG ASize;
134 LONGLONG OldVDL;
135
136 ULONG Res1Acq = 0;
137 ULONG Res2Acq = 0;
138
139 BOOLEAN CacheLocked = FALSE;
140
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;
153
154 TmPrint(("UDFCommonWrite: irp %x\n", Irp));
155
156 _SEH2_TRY {
157
158
159 TopIrp = IoGetTopLevelIrp();
160
161 switch((ULONG)TopIrp) {
162 case FSRTL_FSP_TOP_LEVEL_IRP:
163 UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n"));
164 break;
165 case FSRTL_CACHE_TOP_LEVEL_IRP:
166 UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n"));
167 break;
168 case FSRTL_MOD_WRITE_TOP_LEVEL_IRP:
169 UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n"));
170 break;
171 case FSRTL_FAST_IO_TOP_LEVEL_IRP:
172 UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n"));
173 BrutePoint();
174 break;
175 case NULL:
176 UDFPrint((" NULL TOP_LEVEL_IRP\n"));
177 break;
178 default:
179 if(TopIrp == Irp) {
180 UDFPrint((" TOP_LEVEL_IRP\n"));
181 } else {
182 UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp));
183 }
184 break;
185 }
186
187 // First, get a pointer to the current I/O stack location
188 IrpSp = IoGetCurrentIrpStackLocation(Irp);
189 ASSERT(IrpSp);
190 MmPrint((" Enter Irp, MDL=%x\n", Irp->MdlAddress));
191 if(Irp->MdlAddress) {
192 UDFTouch(Irp->MdlAddress);
193 }
194
195 FileObject = IrpSp->FileObject;
196 ASSERT(FileObject);
197
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);
207 }
208
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);
212 }
213
214 // Get the FCB and CCB pointers
215 Ccb = (PtrUDFCCB)(FileObject->FsContext2);
216 ASSERT(Ccb);
217 Fcb = Ccb->Fcb;
218 ASSERT(Fcb);
219 Vcb = Fcb->Vcb;
220
221 if(Fcb->FCBFlags & UDF_FCB_DELETED) {
222 ASSERT(FALSE);
223 try_return(RC = STATUS_TOO_LATE);
224 }
225
226 // is this operation allowed ?
227 if(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY) {
228 try_return(RC = STATUS_ACCESS_DENIED);
229 }
230 Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
231
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()
235
236 ByteOffset = IrpSp->Parameters.Write.ByteOffset;
237
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",
245 Irp->Flags));
246
247 NtReqFcb = Fcb->NTRequiredFCB;
248
249 Res1Acq = UDFIsResourceAcquired(&(NtReqFcb->MainResource));
250 if(!Res1Acq) {
251 Res1Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES1_ACQ;
252 }
253 Res2Acq = UDFIsResourceAcquired(&(NtReqFcb->PagingIoResource));
254 if(!Res2Acq) {
255 Res2Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES2_ACQ;
256 }
257
258 if(!NonBufferedIo &&
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);
265 } else {
266 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
267 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS,
268 FILE_ACTION_MODIFIED);
269 }
270 }
271
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;
279 }
280 try_return(RC);
281 }
282
283 // If this is the normal file we have to check for
284 // write access according to the current state of the file locks.
285 if (!PagingIo &&
286 !FsRtlCheckLockForWriteAccess( &(NtReqFcb->FileLock), Irp) ) {
287 try_return( RC = STATUS_FILE_LOCK_CONFLICT );
288 }
289
290 // **********
291 // Is this a write of the volume itself ?
292 // **********
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.
296 Vcb = (PVCB)(Fcb);
297 if(!CanWait)
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);
302 }
303
304 if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) {
305
306 UDFPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n"));
307 PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED;
308
309 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) {
310 UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo);
311 }
312 #ifdef UDF_DELAYED_CLOSE
313 UDFCloseAllDelayed(Vcb);
314 #endif //UDF_DELAYED_CLOSE
315
316 }
317
318 // Acquire the volume resource exclusive
319 UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
320 PtrResourceAcquired = &(Vcb->VCBResource);
321
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);
326 }
327 #if defined(_MSC_VER) && !defined(__clang__)
328 /* FIXME */
329 if(PagingIo) {
330 CollectStatistics(Vcb, MetaDataWrites);
331 CollectStatisticsEx(Vcb, MetaDataWriteBytes, NumberBytesWritten);
332 }
333 #endif
334 // Forward the request to the lower level driver
335 // Lock the callers buffer
336 if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, WriteLength))) {
337 try_return(RC);
338 }
339 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
340 if(!SystemBuffer)
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.
348 Vcb->SerialNumber--;
349 // Perform actual Write
350 RC = UDFTWrite(Vcb, SystemBuffer, WriteLength,
351 (ULONG)(ByteOffset.QuadPart >> Vcb->BlockSizeBits),
352 &NumberBytesWritten);
353 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
354 try_return(RC);
355 }
356
357 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) {
358 try_return(RC = STATUS_ACCESS_DENIED);
359 }
360
361 // back pressure for very smart and fast system cache ;)
362 if(!NonBufferedIo) {
363 // cached IO
364 if(Vcb->VerifyCtx.QueuedCount ||
365 Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) {
366 UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT);
367 }
368 } else {
369 if(Vcb->VerifyCtx.ItemCount > UDF_SYS_CACHE_STOP_THR) {
370 UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT);
371 }
372 }
373
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
381
382 IsThisADeferredWrite = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_DEFERRED_WRITE) ? TRUE : FALSE;
383
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);
394 }
395 }
396
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;
401 }
402
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;
408 try_return(RC);
409 }
410
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
414 // offset might be)
415 if(ByteOffset.HighPart == (LONG)0xFFFFFFFF) {
416 if(ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE) {
417 WriteToEOF = TRUE;
418 ByteOffset = NtReqFcb->CommonFCBHeader.FileSize;
419 } else
420 if(ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) {
421 ByteOffset = FileObject->CurrentByteOffset;
422 }
423 }
424
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);
429 }
430
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
433 // If paging i/o
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) {
438 TruncatedLength = 0;
439 } else {
440 TruncatedLength = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart);
441 }
442 if(!TruncatedLength) try_return(RC = STATUS_SUCCESS);
443 } else {
444 TruncatedLength = WriteLength;
445 }
446
447 #if defined(_MSC_VER) && !defined(__clang__)
448 /* FIXME */
449 if(PagingIo) {
450 CollectStatistics(Vcb, UserFileWrites);
451 CollectStatisticsEx(Vcb, UserFileWriteBytes, NumberBytesWritten);
452 }
453 #endif
454
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
458 // the caller.
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 &&
466 !PagingIo &&
467 NtReqFcb->SectionObject.DataSectionObject &&
468 TruncatedLength &&
469 (ByteOffset.QuadPart < NtReqFcb->CommonFCBHeader.FileSize.QuadPart)) {
470
471 if(!Res1Acq) {
472 // Try to acquire the FCB MainResource exclusively
473 if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
474 try_return(RC = STATUS_PENDING);
475 }
476 PtrResourceAcquired = &(NtReqFcb->MainResource);
477 }
478
479 if(!Res2Acq) {
480 // We hold PagingIo shared around the flush to fix a
481 // cache coherency problem.
482 UDFAcquireSharedStarveExclusive(&(NtReqFcb->PagingIoResource), TRUE );
483 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
484 }
485
486 // Flush and then attempt to purge the cache
487 if((ByteOffset.QuadPart + TruncatedLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) {
488 NumberBytesWritten = TruncatedLength;
489 } else {
490 NumberBytesWritten = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart);
491 }
492
493 MmPrint((" CcFlushCache()\n"));
494 CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, NumberBytesWritten, &(Irp->IoStatus));
495
496 if(PtrResourceAcquired2) {
497 UDFReleaseResource(&(NtReqFcb->PagingIoResource));
498 PtrResourceAcquired2 = NULL;
499 }
500 // If the flush failed, return error to the caller
501 if (!NT_SUCCESS(RC = Irp->IoStatus.Status)) {
502 NumberBytesWritten = 0;
503 try_return(RC);
504 }
505
506 if(!Res2Acq) {
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));
512 }
513
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;
523 }
524 }
525
526 // Determine if we were called by the lazywriter.
527 // We reuse 'IsThisADeferredWrite' here to decrease stack usage
528 IsThisADeferredWrite = (NtReqFcb->LazyWriterThreadID == (uint32)PsGetCurrentThread());
529
530 // Acquire the appropriate FCB resource
531 if(PagingIo) {
532 // PagingIoResource is already acquired exclusive
533 // on LazyWrite condition (see UDFAcqLazyWrite())
534 ASSERT(NonBufferedIo);
535 if(!IsThisADeferredWrite) {
536 if(!Res2Acq) {
537 // Try to acquire the FCB PagingIoResource exclusive
538 if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
539 try_return(RC = STATUS_PENDING);
540 }
541 // Remember the resource that was acquired
542 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
543 }
544 }
545 } else {
546 // Try to acquire the FCB MainResource shared
547 if(NonBufferedIo) {
548 if(!Res2Acq) {
549 if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
550 //if(!UDFAcquireSharedWaitForExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
551 try_return(RC = STATUS_PENDING);
552 }
553 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
554 }
555 } else {
556 if(!Res1Acq) {
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);
561 }
562 PtrResourceAcquired = &(NtReqFcb->MainResource);
563 }
564 }
565 // Remember the resource that was acquired
566 }
567
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;
572 }*/
573
574 if ( (Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) &&
575 (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_TOP_LEVEL)) {
576
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) {
580
581 PIO_STACK_LOCATION IrpStack;
582 ASSERT( TopIrp->Type == IO_TYPE_IRP );
583 IrpStack = IoGetCurrentIrpStackLocation(TopIrp);
584
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)) {
589
590 RecursiveWriteThrough = TRUE;
591 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_WRITE_THROUGH;
592 }
593 }
594 }
595
596 // Here is the deal with ValidDataLength and FileSize:
597 //
598 // Rule 1: PagingIo is never allowed to extend file size.
599 //
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.
605 //
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.
608
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.
617
618 OldVDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart;
619 ZeroBlock = (ByteOffset.QuadPart > OldVDL);
620
621 if (!PagingIo &&
622 !RecursiveWriteThrough &&
623 !IsThisADeferredWrite) {
624
625 BOOLEAN ExtendFS;
626
627 ExtendFS = (ByteOffset.QuadPart + TruncatedLength > NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
628
629 if( WriteToEOF || ZeroBlock || ExtendFS) {
630 // we are extending the file;
631
632 if(!CanWait)
633 try_return(RC = STATUS_PENDING);
634 // CanWait = TRUE;
635 // Release any resources acquired above ...
636 if (PtrResourceAcquired2) {
637 UDFReleaseResource(PtrResourceAcquired2);
638 PtrResourceAcquired2 = NULL;
639 }
640 if (PtrResourceAcquired) {
641 UDFReleaseResource(PtrResourceAcquired);
642 PtrResourceAcquired = NULL;
643 }
644 if(!UDFAcquireResourceShared(&(Vcb->VCBResource), CanWait)) {
645 try_return(RC = STATUS_PENDING);
646 }
647 VcbAcquired = TRUE;
648 if(!Res1Acq) {
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);
653 }
654 // Remember the resource that was acquired
655 PtrResourceAcquired = &(NtReqFcb->MainResource);
656 }
657
658 if(!Res2Acq) {
659 // allocate space...
660 AdPrint((" Try to acquire PagingIoRes\n"));
661 UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE );
662 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
663 }
664 AdPrint((" PagingIoRes Ok, Resizing...\n"));
665
666 if(ExtendFS) {
667 RC = UDFResizeFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart + TruncatedLength);
668
669 if(!NT_SUCCESS(RC)) {
670 if(PtrResourceAcquired2) {
671 UDFReleaseResource(&(NtReqFcb->PagingIoResource));
672 PtrResourceAcquired2 = NULL;
673 }
674 try_return(RC);
675 }
676 Resized = TRUE;
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);
682 } else {
683 NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart += (PAGE_SIZE-1);
684 }
685 NtReqFcb->CommonFCBHeader.AllocationSize.LowPart &= ~(PAGE_SIZE-1);
686 }
687
688 UDFPrint(("UDFCommonWrite: Set size %x (alloc size %x)\n", ByteOffset.LowPart + TruncatedLength, NtReqFcb->CommonFCBHeader.AllocationSize.LowPart));
689 if (CcIsFileCached(FileObject)) {
690 if(ExtendFS) {
691 MmPrint((" CcSetFileSizes()\n"));
692 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize));
693 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
694 }
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)
700 if(ZeroBlock) {
701 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
702 ThPrint((" UDFZeroDataEx(1)\n"));
703 UDFZeroDataEx(NtReqFcb,
704 OldVDL,
705 /*ByteOffset.QuadPart*/ NtReqFcb->CommonFCBHeader.FileSize.QuadPart - OldVDL,
706 CanWait, Vcb, FileObject);
707 #ifdef UDF_DBG
708 ZeroBlockDone = TRUE;
709 #endif //UDF_DBG
710 }
711 }
712 if (PtrResourceAcquired2) {
713 UDFReleaseResource(PtrResourceAcquired2);
714 PtrResourceAcquired2 = NULL;
715 }
716
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);
722 } else {
723 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
724 FILE_NOTIFY_CHANGE_SIZE,
725 FILE_ACTION_MODIFIED);
726 }
727 }
728
729 }
730
731 #ifdef UDF_DISABLE_SYSTEM_CACHE_MANAGER
732 NonBufferedIo = TRUE;
733 #endif
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));
736 }
737
738 // Branch here for cached vs non-cached I/O
739 if (!NonBufferedIo) {
740
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);
754
755 }
756
757 if(ZeroBlock && !ZeroBlockDone) {
758 ThPrint((" UDFZeroDataEx(2)\n"));
759 UDFZeroDataEx(NtReqFcb,
760 OldVDL,
761 /*ByteOffset.QuadPart*/ ByteOffset.QuadPart + TruncatedLength - OldVDL,
762 CanWait, Vcb, FileObject);
763 if(ByteOffset.LowPart & (PAGE_SIZE-1)) {
764 }
765 }
766
767 WriteFileSizeToDirNdx = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_WRITE_THROUGH) ?
768 TRUE : FALSE;
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;
777
778 NumberBytesWritten = 0;
779 RC = STATUS_INVALID_PARAMETER;
780
781 try_return(RC);
782 }
783
784 if(NtReqFcb->SectionObject.DataSectionObject &&
785 TruncatedLength >= 0x10000 &&
786 ByteOffset.LowPart &&
787 !(ByteOffset.LowPart & 0x00ffffff)) {
788
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));
794
795 // there was a nice idea: flush just previous part. But it doesn't work
796 CcFlushCache(&(NtReqFcb->SectionObject), NULL, 0, &(Irp->IoStatus));
797 //}
798 }
799
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
803
804 // We needn't call CcZeroData 'cause udf_info.cpp will care about it
805 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
806 if(!SystemBuffer)
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);
817 }
818
819 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
820 // We have the data
821 RC = STATUS_SUCCESS;
822 NumberBytesWritten = TruncatedLength;
823
824 try_return(RC);
825
826 } else {
827
828 MmPrint((" Write NonBufferedIo\n"));
829
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)) {
836 #ifdef UDF_DBG
837 ASSERT(!ZeroBlockDone);
838 #endif //UDF_DBG
839 UDFZeroDataEx(NtReqFcb,
840 OldVDL,
841 /*ByteOffset.QuadPart*/ ByteOffset.QuadPart - OldVDL,
842 CanWait, Vcb, FileObject);
843 }
844 if(OldVDL < (ByteOffset.QuadPart + TruncatedLength)) {
845 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = ByteOffset.QuadPart + TruncatedLength;
846 }
847
848 #if 1
849 if((ULONG)TopIrp == FSRTL_MOD_WRITE_TOP_LEVEL_IRP) {
850 UDFPrint(("FSRTL_MOD_WRITE_TOP_LEVEL_IRP => CanWait\n"));
851 CanWait = TRUE;
852 } else
853 if((ULONG)TopIrp == FSRTL_CACHE_TOP_LEVEL_IRP) {
854 UDFPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n"));
855 CanWait = TRUE;
856 }
857
858 if(NtReqFcb->AcqSectionCount || NtReqFcb->AcqFlushCount) {
859 MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb->AcqSectionCount, NtReqFcb->AcqFlushCount));
860 CanWait = TRUE;
861 } else
862 {}
863 /* if((TopIrp != Irp)) {
864 UDFPrint(("(TopIrp != Irp) => CanWait\n"));
865 CanWait = TRUE;
866 } else*/
867 #endif
868 if(KeGetCurrentIrql() > PASSIVE_LEVEL) {
869 MmPrint((" !PASSIVE_LEVEL\n"));
870 CanWait = FALSE;
871 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FORCED_POST;
872 }
873 // Successful check will cause WCache lock
874 if(!CanWait && UDFIsFileCached__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, TRUE)) {
875 UDFPrint(("UDFCommonWrite: Cached => CanWait\n"));
876 CacheLocked = TRUE;
877 CanWait = TRUE;
878 }
879 // Send the request to lower level drivers
880 if(!CanWait) {
881 UDFPrint(("UDFCommonWrite: Post physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
882
883 try_return(RC = STATUS_PENDING);
884 }
885
886 if(!Res2Acq) {
887 if(UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb->PagingIoResource))) {
888 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
889 }
890 }
891
892 PerfPrint(("UDFCommonWrite: Physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
893
894 // Lock the callers buffer
895 if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, TruncatedLength))) {
896 try_return(RC);
897 }
898
899 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
900 if(!SystemBuffer) {
901 try_return(RC = STATUS_INVALID_USER_BUFFER);
902 }
903 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
904 RC = UDFWriteFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength,
905 CacheLocked, (PCHAR)SystemBuffer, &NumberBytesWritten);
906
907 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
908
909 #if defined(_MSC_VER) && !defined(__clang__)
910 /* FIXME */
911 if(PagingIo) {
912 CollectStatistics(Vcb, UserDiskWrites);
913 } else {
914 CollectStatistics2(Vcb, NonCachedDiskWrites);
915 }
916 #endif
917 WriteFileSizeToDirNdx = TRUE;
918
919 try_return(RC);
920 }
921
922 try_exit: NOTHING;
923
924 } _SEH2_FINALLY {
925
926 if(CacheLocked) {
927 WCacheEODirect__(&(Vcb->FastCache), Vcb);
928 }
929
930 // Release any resources acquired here ...
931 if(PtrResourceAcquired2) {
932 UDFReleaseResource(PtrResourceAcquired2);
933 }
934 if(PtrResourceAcquired) {
935 if(NtReqFcb &&
936 (PtrResourceAcquired ==
937 &(NtReqFcb->MainResource))) {
938 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
939 }
940 UDFReleaseResource(PtrResourceAcquired);
941 }
942 if(VcbAcquired) {
943 UDFReleaseResource(&(Vcb->VCBResource));
944 }
945
946 // Post IRP if required
947 if(RC == STATUS_PENDING) {
948
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));
954 }
955 if(PagingIo) {
956 if(Res1Acq) {
957 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES1_ACQ;
958 }
959 if(Res2Acq) {
960 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES2_ACQ;
961 }
962 }
963
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);
967
968 } else {
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;
973 }
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
980 // this occurred.
981 FileObject->Flags |= FO_FILE_MODIFIED;
982 if(Resized) {
983 if(!WriteFileSizeToDirNdx) {
984 FileObject->Flags |= FO_FILE_SIZE_CHANGED;
985 } else {
986 ASize = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo);
987 UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &ASize);
988 }
989 }
990 // Update ValidDataLength
991 if(!IsThisADeferredWrite &&
992 NtReqFcb) {
993 if(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart < (ByteOffset.QuadPart + NumberBytesWritten)) {
994
995 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart =
996 min(NtReqFcb->CommonFCBHeader.FileSize.QuadPart,
997 ByteOffset.QuadPart + NumberBytesWritten);
998 }
999 }
1000 }
1001
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
1005 // it at this time.
1006
1007 // Can complete the IRP here if no exception was encountered
1008 if(!_SEH2_AbnormalTermination() &&
1009 Irp) {
1010 Irp->IoStatus.Status = RC;
1011 Irp->IoStatus.Information = NumberBytesWritten;
1012 // complete the IRP
1013 MmPrint((" Complete Irp, MDL=%x\n", Irp->MdlAddress));
1014 if(Irp->MdlAddress) {
1015 UDFTouch(Irp->MdlAddress);
1016 }
1017 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1018 }
1019 // Free up the Irp Context
1020 UDFReleaseIrpContext(PtrIrpContext);
1021
1022 } // can we complete the IRP ?
1023 } _SEH2_END; // end of "__finally" processing
1024
1025 UDFPrint(("\n"));
1026 return(RC);
1027 } // end UDFCommonWrite()
1028
1029 /*************************************************************************
1030 *
1031 * Function: UDFDeferredWriteCallBack()
1032 *
1033 * Description:
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.
1038 *
1039 * Expected Interrupt Level (for execution) :
1040 *
1041 * IRQL_PASSIVE_LEVEL
1042 *
1043 * Return Value: None
1044 *
1045 *************************************************************************/
1046 VOID
1047 NTAPI
1048 UDFDeferredWriteCallBack(
1049 IN PVOID Context1, // Should be PtrIrpContext
1050 IN PVOID Context2 // Should be Irp
1051 )
1052 {
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);
1060
1061 } // end UDFDeferredWriteCallBack()
1062
1063 /*************************************************************************
1064 *
1065 *************************************************************************/
1066
1067 #define USE_CcCopyWrite_TO_ZERO
1068
1069 VOID
1070 UDFPurgeCacheEx_(
1071 PtrUDFNTRequiredFCB NtReqFcb,
1072 LONGLONG Offset,
1073 LONGLONG Length,
1074 //#ifndef ALLOW_SPARSE
1075 BOOLEAN CanWait,
1076 //#endif //ALLOW_SPARSE
1077 PVCB Vcb,
1078 PFILE_OBJECT FileObject
1079 )
1080 {
1081 ULONG Off_l;
1082 #ifdef USE_CcCopyWrite_TO_ZERO
1083 ULONG PgLen;
1084 #endif //USE_CcCopyWrite_TO_ZERO
1085
1086 // We'll just purge cache section here,
1087 // without call to CcZeroData()
1088 // 'cause udf_info.cpp will care about it
1089
1090 #define PURGE_BLOCK_SZ 0x10000000
1091
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
1095
1096 _SEH2_TRY {
1097 MmPrint((" UDFPurgeCacheEx_(): Offs: %I64x, ", Offset));
1098 MmPrint((" Len: %lx\n", Length));
1099 SECTION_OBJECT_POINTERS* SectionObject = &(NtReqFcb->SectionObject);
1100 if(Length) {
1101 LONGLONG Offset0, OffsetX, VDL;
1102
1103 Offset0 = Offset;
1104 if((Off_l = ((ULONG)Offset0 & (PAGE_SIZE-1)))) {
1105 // Offset, Offset0
1106 // v
1107 // ...|dddddddddddd00000|....
1108 // |<- Off_l ->|
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|....
1115 // |<- PgLen ->|
1116 PgLen = PAGE_SIZE - Off_l; /*(*((PULONG)&Offset) & (PAGE_SIZE-1))*/
1117 //
1118 if(PgLen > Length)
1119 PgLen = (ULONG)Length;
1120
1121 MmPrint((" ZeroCache (CcWrite) Offs %I64x, Len %x\n", Offset, PgLen));
1122 #ifdef DBG
1123 if(FileObject && Vcb) {
1124
1125 ASSERT(CanWait);
1126 #endif //DBG
1127 if (PgLen) {
1128 if (SectionObject->SharedCacheMap) {
1129 CcCopyWrite(FileObject, (PLARGE_INTEGER)&Offset, PgLen, TRUE || CanWait, Vcb->ZBuffer);
1130 }
1131 Offset += PgLen;
1132 Length -= PgLen;
1133 }
1134 #ifdef DBG
1135 } else {
1136 MmPrint((" Can't use CcWrite to zero cache\n"));
1137 }
1138 #endif //DBG
1139 #endif //USE_CcCopyWrite_TO_ZERO
1140 }
1141 VDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart;
1142 OffsetX = Offset+Length;
1143 if((Off_l = ((ULONG)OffsetX & (PAGE_SIZE-1)))) {
1144
1145 if(OffsetX < VDL) {
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);
1156 } else {
1157 PgLen = (ULONG)(VDL - OffsetX) & ~(PAGE_SIZE-1);
1158 }
1159 // ...|000000000000ddddd|....
1160 // |<- PgLen ->|
1161 MmPrint((" ZeroCache (CcWrite - 2) Offs %I64x, Len %x\n", OffsetX, PgLen));
1162 #ifdef DBG
1163 if(FileObject && Vcb) {
1164 ASSERT(CanWait);
1165 #endif //DBG
1166 if (SectionObject->SharedCacheMap) {
1167 CcCopyWrite(FileObject, (PLARGE_INTEGER)&OffsetX, PgLen, TRUE || CanWait, Vcb->ZBuffer);
1168 }
1169 Length -= PgLen;
1170 #ifdef DBG
1171 } else {
1172 MmPrint((" Can't use CcWrite to zero cache (2)\n"));
1173 }
1174 #endif //DBG
1175 #endif //USE_CcCopyWrite_TO_ZERO
1176 }
1177 }
1178 #ifndef USE_CcCopyWrite_TO_ZERO
1179 do
1180 #else //USE_CcCopyWrite_TO_ZERO
1181 while(Length)
1182 #endif //USE_CcCopyWrite_TO_ZERO
1183 {
1184 MmPrint((" CcPurgeCacheSection()\n"));
1185 if(PURGE_BLOCK_SZ > Length) {
1186 CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset,
1187 (ULONG)Length, FALSE);
1188 /*
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 );
1194 */
1195 #ifndef ALLOW_SPARSE
1196 // UDFZeroFile__(
1197 #endif //ALLOW_SPARSE
1198 break;
1199 } else {
1200 CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset,
1201 PURGE_BLOCK_SZ, FALSE);
1202 /*
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 );
1208 */
1209 #ifndef ALLOW_SPARSE
1210 // UDFZeroFile__(
1211 #endif //ALLOW_SPARSE
1212 Length -= PURGE_BLOCK_SZ;
1213 Offset += PURGE_BLOCK_SZ;
1214 }
1215 }
1216 #ifndef USE_CcCopyWrite_TO_ZERO
1217 while(Length);
1218 #endif //USE_CcCopyWrite_TO_ZERO
1219 if(VDL < Offset)
1220 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = Offset;
1221 }
1222 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1223 BrutePoint();
1224 } _SEH2_END;
1225 } // end UDFPurgeCacheEx_()
1226
1227 #endif //UDF_READ_ONLY_BUILD