01964f1803e08ab1126e7c87de90adc44f8f2d78
[reactos.git] / reactos / 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 ////////////////////////////////////////////////////////////////////
5 /*************************************************************************
6 *
7 * File: Write.cpp
8 *
9 * Module: UDF File System Driver (Kernel mode execution only)
10 *
11 * Description:
12 * Contains code to handle the "Write" dispatch entry point.
13 *
14 *************************************************************************/
15
16 #include "udffs.h"
17
18 // define the file specific bug-check id
19 #define UDF_BUG_CHECK_ID UDF_FILE_WRITE
20
21 #ifndef UDF_READ_ONLY_BUILD
22
23 /*************************************************************************
24 *
25 * Function: UDFWrite()
26 *
27 * Description:
28 * The I/O Manager will invoke this routine to handle a write
29 * request
30 *
31 * Expected Interrupt Level (for execution) :
32 *
33 * IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
34 * to be deferred to a worker thread context)
35 *
36 * Return Value: STATUS_SUCCESS/Error
37 *
38 *************************************************************************/
39 NTSTATUS
40 NTAPI
41 UDFWrite(
42 PDEVICE_OBJECT DeviceObject, // the logical volume device object
43 PIRP Irp // I/O Request Packet
44 )
45 {
46 NTSTATUS RC = STATUS_SUCCESS;
47 PtrUDFIrpContext PtrIrpContext = NULL;
48 BOOLEAN AreWeTopLevel = FALSE;
49
50 TmPrint(("UDFWrite: , thrd:%8.8x\n",PsGetCurrentThread()));
51
52 FsRtlEnterFileSystem();
53 ASSERT(DeviceObject);
54 ASSERT(Irp);
55
56 // set the top level context
57 AreWeTopLevel = UDFIsIrpTopLevel(Irp);
58 ASSERT(!UDFIsFSDevObj(DeviceObject));
59
60 _SEH2_TRY {
61
62 // get an IRP context structure and issue the request
63 PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
64 if(PtrIrpContext) {
65
66 RC = UDFCommonWrite(PtrIrpContext, Irp);
67
68 } else {
69 RC = STATUS_INSUFFICIENT_RESOURCES;
70 Irp->IoStatus.Status = RC;
71 Irp->IoStatus.Information = 0;
72 // complete the IRP
73 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
74 }
75
76 } _SEH2_EXCEPT (UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
77
78 RC = UDFExceptionHandler(PtrIrpContext, Irp);
79
80 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
81 } _SEH2_END;
82
83 if (AreWeTopLevel) {
84 IoSetTopLevelIrp(NULL);
85 }
86
87 FsRtlExitFileSystem();
88
89 return(RC);
90 } // end UDFWrite()
91
92
93 /*************************************************************************
94 *
95 * Function: UDFCommonWrite()
96 *
97 * Description:
98 * The actual work is performed here. This routine may be invoked in one'
99 * of the two possible contexts:
100 * (a) in the context of a system worker thread
101 * (b) in the context of the original caller
102 *
103 * Expected Interrupt Level (for execution) :
104 *
105 * IRQL_PASSIVE_LEVEL
106 *
107 * Return Value: STATUS_SUCCESS/Error
108 *
109 *************************************************************************/
110 NTSTATUS
111 UDFCommonWrite(
112 PtrUDFIrpContext PtrIrpContext,
113 PIRP Irp)
114 {
115 NTSTATUS RC = STATUS_SUCCESS;
116 PIO_STACK_LOCATION IrpSp = NULL;
117 LARGE_INTEGER ByteOffset;
118 ULONG WriteLength = 0, TruncatedLength = 0;
119 ULONG NumberBytesWritten = 0;
120 PFILE_OBJECT FileObject = NULL;
121 PtrUDFFCB Fcb = NULL;
122 PtrUDFCCB Ccb = NULL;
123 PVCB Vcb = NULL;
124 PtrUDFNTRequiredFCB NtReqFcb = NULL;
125 PERESOURCE PtrResourceAcquired = NULL;
126 PERESOURCE PtrResourceAcquired2 = NULL;
127 PVOID SystemBuffer = NULL;
128 // PVOID TmpBuffer = NULL;
129 // uint32 KeyValue = 0;
130 PIRP TopIrp;
131
132 LONGLONG ASize;
133 LONGLONG OldVDL;
134
135 ULONG Res1Acq = 0;
136 ULONG Res2Acq = 0;
137
138 BOOLEAN CacheLocked = FALSE;
139
140 BOOLEAN CanWait = FALSE;
141 BOOLEAN PagingIo = FALSE;
142 BOOLEAN NonBufferedIo = FALSE;
143 BOOLEAN SynchronousIo = FALSE;
144 BOOLEAN IsThisADeferredWrite = FALSE;
145 BOOLEAN WriteToEOF = FALSE;
146 BOOLEAN Resized = FALSE;
147 BOOLEAN RecursiveWriteThrough = FALSE;
148 BOOLEAN WriteFileSizeToDirNdx = FALSE;
149 BOOLEAN ZeroBlock = FALSE;
150 BOOLEAN VcbAcquired = FALSE;
151 BOOLEAN ZeroBlockDone = FALSE;
152
153 TmPrint(("UDFCommonWrite: irp %x\n", Irp));
154
155 _SEH2_TRY {
156
157
158 TopIrp = IoGetTopLevelIrp();
159
160 switch((ULONG)TopIrp) {
161 case FSRTL_FSP_TOP_LEVEL_IRP:
162 KdPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n"));
163 break;
164 case FSRTL_CACHE_TOP_LEVEL_IRP:
165 KdPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n"));
166 break;
167 case FSRTL_MOD_WRITE_TOP_LEVEL_IRP:
168 KdPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n"));
169 break;
170 case FSRTL_FAST_IO_TOP_LEVEL_IRP:
171 KdPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n"));
172 BrutePoint();
173 break;
174 case NULL:
175 KdPrint((" NULL TOP_LEVEL_IRP\n"));
176 break;
177 default:
178 if(TopIrp == Irp) {
179 KdPrint((" TOP_LEVEL_IRP\n"));
180 } else {
181 KdPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp));
182 }
183 break;
184 }
185
186 // First, get a pointer to the current I/O stack location
187 IrpSp = IoGetCurrentIrpStackLocation(Irp);
188 ASSERT(IrpSp);
189 MmPrint((" Enter Irp, MDL=%x\n", Irp->MdlAddress));
190 if(Irp->MdlAddress) {
191 UDFTouch(Irp->MdlAddress);
192 }
193
194 FileObject = IrpSp->FileObject;
195 ASSERT(FileObject);
196
197 // If this happens to be a MDL write complete request, then
198 // allocated MDL can be freed. This may cause a recursive write
199 // back into the FSD.
200 if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
201 // Caller wants to tell the Cache Manager that a previously
202 // allocated MDL can be freed.
203 UDFMdlComplete(PtrIrpContext, Irp, IrpSp, FALSE);
204 // The IRP has been completed.
205 try_return(RC = STATUS_SUCCESS);
206 }
207
208 // If this is a request at IRQL DISPATCH_LEVEL, then post the request
209 if (IrpSp->MinorFunction & IRP_MN_DPC) {
210 try_return(RC = STATUS_PENDING);
211 }
212
213 // Get the FCB and CCB pointers
214 Ccb = (PtrUDFCCB)(FileObject->FsContext2);
215 ASSERT(Ccb);
216 Fcb = Ccb->Fcb;
217 ASSERT(Fcb);
218 Vcb = Fcb->Vcb;
219
220 if(Fcb->FCBFlags & UDF_FCB_DELETED) {
221 ASSERT(FALSE);
222 try_return(RC = STATUS_TOO_LATE);
223 }
224
225 // is this operation allowed ?
226 if(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY) {
227 try_return(RC = STATUS_ACCESS_DENIED);
228 }
229 Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
230
231 #ifdef EVALUATION_TIME_LIMIT
232 // License Key check
233 if(UDFGlobalData.UDFFlags & UDF_DATA_FLAGS_UNREGISTERED) {
234 Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
235 try_return(RC = STATUS_ACCESS_DENIED);
236 }
237 #endif //EVALUATION_TIME_LIMIT
238
239 // Disk based file systems might decide to verify the logical volume
240 // (if required and only if removable media are supported) at this time
241 // As soon as Tray is locked, we needn't call UDFVerifyVcb()
242
243 ByteOffset = IrpSp->Parameters.Write.ByteOffset;
244
245 CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE;
246 PagingIo = (Irp->Flags & IRP_PAGING_IO) ? TRUE : FALSE;
247 NonBufferedIo = (Irp->Flags & IRP_NOCACHE) ? TRUE : FALSE;
248 SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO) ? TRUE : FALSE;
249 KdPrint((" Flags: %s; %s; %s; %s; Irp(W): %8.8x\n",
250 CanWait ? "Wt" : "nw", PagingIo ? "Pg" : "np",
251 NonBufferedIo ? "NBuf" : "buff", SynchronousIo ? "Snc" : "Asc",
252 Irp->Flags));
253
254 NtReqFcb = Fcb->NTRequiredFCB;
255
256 Res1Acq = UDFIsResourceAcquired(&(NtReqFcb->MainResource));
257 if(!Res1Acq) {
258 Res1Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES1_ACQ;
259 }
260 Res2Acq = UDFIsResourceAcquired(&(NtReqFcb->PagingIoResource));
261 if(!Res2Acq) {
262 Res2Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES2_ACQ;
263 }
264
265 if(!NonBufferedIo &&
266 (Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB)) {
267 if((Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB) &&
268 UDFIsAStream(Fcb->FileInfo)) {
269 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
270 FILE_NOTIFY_CHANGE_STREAM_WRITE,
271 FILE_ACTION_MODIFIED_STREAM);
272 } else {
273 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
274 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS,
275 FILE_ACTION_MODIFIED);
276 }
277 }
278
279 // Get some of the parameters supplied to us
280 WriteLength = IrpSp->Parameters.Write.Length;
281 if (WriteLength == 0) {
282 // a 0 byte write can be immediately succeeded
283 if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) {
284 // NT expects changing CurrentByteOffset to zero in this case
285 FileObject->CurrentByteOffset.QuadPart = 0;
286 }
287 try_return(RC);
288 }
289
290 // If this is the normal file we have to check for
291 // write access according to the current state of the file locks.
292 if (!PagingIo &&
293 !FsRtlCheckLockForWriteAccess( &(NtReqFcb->FileLock), Irp) ) {
294 try_return( RC = STATUS_FILE_LOCK_CONFLICT );
295 }
296
297 // **********
298 // Is this a write of the volume itself ?
299 // **********
300 if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) {
301 // Yup, we need to send this on to the disk driver after
302 // validation of the offset and length.
303 Vcb = (PVCB)(Fcb);
304 if(!CanWait)
305 try_return(RC = STATUS_PENDING);
306 // I dislike the idea of writing to not locked media
307 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED)) {
308 try_return(RC = STATUS_ACCESS_DENIED);
309 }
310
311 if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) {
312
313 KdPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n"));
314 PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED;
315
316 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) {
317 UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo);
318 }
319 #ifdef UDF_DELAYED_CLOSE
320 UDFCloseAllDelayed(Vcb);
321 #endif //UDF_DELAYED_CLOSE
322
323 }
324
325 // Acquire the volume resource exclusive
326 UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
327 PtrResourceAcquired = &(Vcb->VCBResource);
328
329 // I dislike the idea of writing to mounted media too, but M$ has another point of view...
330 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) {
331 // flush system cache
332 UDFFlushLogicalVolume(NULL, NULL, Vcb, 0);
333 }
334 #ifdef _MSC_VER
335 /* FIXME */
336 if(PagingIo) {
337 CollectStatistics(Vcb, MetaDataWrites);
338 CollectStatisticsEx(Vcb, MetaDataWriteBytes, NumberBytesWritten);
339 }
340 #endif
341 // Forward the request to the lower level driver
342 // Lock the callers buffer
343 if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, WriteLength))) {
344 try_return(RC);
345 }
346 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
347 if(!SystemBuffer)
348 try_return(RC = STATUS_INVALID_USER_BUFFER);
349 // Indicate, that volume contents can change after this operation
350 // This flag will force VerifyVolume in future
351 KdPrint((" set UnsafeIoctl\n"));
352 Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL;
353 // Make sure, that volume will never be quick-remounted
354 // It is very important for ChkUdf utility.
355 Vcb->SerialNumber--;
356 // Perform actual Write
357 RC = UDFTWrite(Vcb, SystemBuffer, WriteLength,
358 (ULONG)(ByteOffset.QuadPart >> Vcb->BlockSizeBits),
359 &NumberBytesWritten);
360 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
361 try_return(RC);
362 }
363
364 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) {
365 try_return(RC = STATUS_ACCESS_DENIED);
366 }
367
368 // back pressure for very smart and fast system cache ;)
369 if(!NonBufferedIo) {
370 // cached IO
371 if(Vcb->VerifyCtx.QueuedCount ||
372 Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) {
373 UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT);
374 }
375 } else {
376 if(Vcb->VerifyCtx.ItemCount > UDF_SYS_CACHE_STOP_THR) {
377 UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT);
378 }
379 }
380
381 // The FSD (if it is a "nice" FSD) should check whether it is
382 // convenient to allow the write to proceed by utilizing the
383 // CcCanIWrite() function call. If it is not convenient to perform
384 // the write at this time, we should defer the request for a while.
385 // The check should not however be performed for non-cached write
386 // operations. To determine whether we are retrying the operation
387 // or now, use Flags in the IrpContext structure we have created
388
389 IsThisADeferredWrite = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_DEFERRED_WRITE) ? TRUE : FALSE;
390
391 if (!NonBufferedIo) {
392 MmPrint((" CcCanIWrite()\n"));
393 if (!CcCanIWrite(FileObject, WriteLength, CanWait, IsThisADeferredWrite)) {
394 // Cache Manager and/or the VMM does not want us to perform
395 // the write at this time. Post the request.
396 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_DEFERRED_WRITE;
397 KdPrint(("UDFCommonWrite: Defer write\n"));
398 MmPrint((" CcDeferWrite()\n"));
399 CcDeferWrite(FileObject, UDFDeferredWriteCallBack, PtrIrpContext, Irp, WriteLength, IsThisADeferredWrite);
400 try_return(RC = STATUS_PENDING);
401 }
402 }
403
404 // If the write request is directed to a page file,
405 // send the request directly to the disk
406 if (Fcb->FCBFlags & UDF_FCB_PAGE_FILE) {
407 NonBufferedIo = TRUE;
408 }
409
410 // We can continue. Check whether this write operation is targeted
411 // to a directory object in which case the UDF FSD will disallow
412 // the write request.
413 if (Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
414 RC = STATUS_INVALID_DEVICE_REQUEST;
415 try_return(RC);
416 }
417
418 // Validate start offset and length supplied.
419 // Here is a special check that determines whether the caller wishes to
420 // begin the write at current end-of-file (whatever the value of that
421 // offset might be)
422 if(ByteOffset.HighPart == (LONG)0xFFFFFFFF) {
423 if(ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE) {
424 WriteToEOF = TRUE;
425 ByteOffset = NtReqFcb->CommonFCBHeader.FileSize;
426 } else
427 if(ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) {
428 ByteOffset = FileObject->CurrentByteOffset;
429 }
430 }
431
432 // Check if this volume has already been shut down. If it has, fail
433 // this write request.
434 if (Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN) {
435 try_return(RC = STATUS_TOO_LATE);
436 }
437
438 #ifdef EVALUATION_TIME_LIMIT
439 // License Key check
440 if(UDFGlobalData.UDFFlags & UDF_DATA_FLAGS_UNREGISTERED) {
441 Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
442 try_return(RC = STATUS_ACCESS_DENIED);
443 }
444 #endif //EVALUATION_TIME_LIMIT
445
446 // Paging I/O write operations are special. If paging i/o write
447 // requests begin beyond end-of-file, the request should be no-oped
448 // If paging i/o
449 // requests extend beyond current end of file, they should be truncated
450 // to current end-of-file.
451 if(PagingIo && (WriteToEOF || ((ByteOffset.QuadPart + WriteLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart))) {
452 if (ByteOffset.QuadPart > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) {
453 TruncatedLength = 0;
454 } else {
455 TruncatedLength = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart);
456 }
457 if(!TruncatedLength) try_return(RC = STATUS_SUCCESS);
458 } else {
459 TruncatedLength = WriteLength;
460 }
461
462 #ifdef _MSC_VER
463 /* FIXME */
464 if(PagingIo) {
465 CollectStatistics(Vcb, UserFileWrites);
466 CollectStatisticsEx(Vcb, UserFileWriteBytes, NumberBytesWritten);
467 }
468 #endif
469
470 // There are certain complications that arise when the same file stream
471 // has been opened for cached and non-cached access. The FSD is then
472 // responsible for maintaining a consistent view of the data seen by
473 // the caller.
474 // If this happens to be a non-buffered I/O, we should __try to flush the
475 // cached data (if some other file object has already initiated caching
476 // on the file stream). We should also __try to purge the cached
477 // information though the purge will probably fail if the file has been
478 // mapped into some process' virtual address space
479 // WARNING !!! we should not flush data beyond valid data length
480 if ( NonBufferedIo &&
481 !PagingIo &&
482 NtReqFcb->SectionObject.DataSectionObject &&
483 TruncatedLength &&
484 (ByteOffset.QuadPart < NtReqFcb->CommonFCBHeader.FileSize.QuadPart)) {
485
486 if(!Res1Acq) {
487 // Try to acquire the FCB MainResource exclusively
488 if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
489 try_return(RC = STATUS_PENDING);
490 }
491 PtrResourceAcquired = &(NtReqFcb->MainResource);
492 }
493
494 if(!Res2Acq) {
495 // We hold PagingIo shared around the flush to fix a
496 // cache coherency problem.
497 UDFAcquireSharedStarveExclusive(&(NtReqFcb->PagingIoResource), TRUE );
498 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
499 }
500
501 // Flush and then attempt to purge the cache
502 if((ByteOffset.QuadPart + TruncatedLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) {
503 NumberBytesWritten = TruncatedLength;
504 } else {
505 NumberBytesWritten = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart);
506 }
507
508 MmPrint((" CcFlushCache()\n"));
509 CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, NumberBytesWritten, &(Irp->IoStatus));
510
511 if(PtrResourceAcquired2) {
512 UDFReleaseResource(&(NtReqFcb->PagingIoResource));
513 PtrResourceAcquired2 = NULL;
514 }
515 // If the flush failed, return error to the caller
516 if (!NT_SUCCESS(RC = Irp->IoStatus.Status)) {
517 NumberBytesWritten = 0;
518 try_return(RC);
519 }
520
521 if(!Res2Acq) {
522 // Acquiring and immediately dropping the resource serializes
523 // us behind any other writes taking place (either from the
524 // lazy writer or modified page writer).
525 UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE );
526 UDFReleaseResource(&(NtReqFcb->PagingIoResource));
527 }
528
529 // Attempt the purge and ignore the return code
530 MmPrint((" CcPurgeCacheSection()\n"));
531 CcPurgeCacheSection(&(NtReqFcb->SectionObject), &ByteOffset,
532 NumberBytesWritten, FALSE);
533 NumberBytesWritten = 0;
534 // We are finished with our flushing and purging
535 if(PtrResourceAcquired) {
536 UDFReleaseResource(PtrResourceAcquired);
537 PtrResourceAcquired = NULL;
538 }
539 }
540
541 // Determine if we were called by the lazywriter.
542 // We reuse 'IsThisADeferredWrite' here to decrease stack usage
543 IsThisADeferredWrite = (NtReqFcb->LazyWriterThreadID == (uint32)PsGetCurrentThread());
544
545 // Acquire the appropriate FCB resource
546 if(PagingIo) {
547 // PagingIoResource is already acquired exclusive
548 // on LazyWrite condition (see UDFAcqLazyWrite())
549 ASSERT(NonBufferedIo);
550 if(!IsThisADeferredWrite) {
551 if(!Res2Acq) {
552 // Try to acquire the FCB PagingIoResource exclusive
553 if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
554 try_return(RC = STATUS_PENDING);
555 }
556 // Remember the resource that was acquired
557 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
558 }
559 }
560 } else {
561 // Try to acquire the FCB MainResource shared
562 if(NonBufferedIo) {
563 if(!Res2Acq) {
564 if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
565 //if(!UDFAcquireSharedWaitForExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
566 try_return(RC = STATUS_PENDING);
567 }
568 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
569 }
570 } else {
571 if(!Res1Acq) {
572 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
573 if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
574 //if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
575 try_return(RC = STATUS_PENDING);
576 }
577 PtrResourceAcquired = &(NtReqFcb->MainResource);
578 }
579 }
580 // Remember the resource that was acquired
581 }
582
583 // Set the flag indicating if Fast I/O is possible
584 NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
585 /* if(NtReqFcb->CommonFCBHeader.IsFastIoPossible == FastIoIsPossible) {
586 NtReqFcb->CommonFCBHeader.IsFastIoPossible = FastIoIsQuestionable;
587 }*/
588
589 if ( (Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) &&
590 (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_TOP_LEVEL)) {
591
592 // This clause determines if the top level request was
593 // in the FastIo path.
594 if ((ULONG)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG) {
595
596 PIO_STACK_LOCATION IrpStack;
597 ASSERT( TopIrp->Type == IO_TYPE_IRP );
598 IrpStack = IoGetCurrentIrpStackLocation(TopIrp);
599
600 // Finally this routine detects if the Top irp was a
601 // write to this file and thus we are the writethrough.
602 if ((IrpStack->MajorFunction == IRP_MJ_WRITE) &&
603 (IrpStack->FileObject->FsContext == FileObject->FsContext)) {
604
605 RecursiveWriteThrough = TRUE;
606 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_WRITE_THROUGH;
607 }
608 }
609 }
610
611 // Here is the deal with ValidDataLength and FileSize:
612 //
613 // Rule 1: PagingIo is never allowed to extend file size.
614 //
615 // Rule 2: Only the top level requestor may extend Valid
616 // Data Length. This may be paging IO, as when a
617 // a user maps a file, but will never be as a result
618 // of cache lazy writer writes since they are not the
619 // top level request.
620 //
621 // Rule 3: If, using Rules 1 and 2, we decide we must extend
622 // file size or valid data, we take the Fcb exclusive.
623
624 // Check whether the current request will extend the file size,
625 // or the valid data length (if the FSD supports the concept of a
626 // valid data length associated with the file stream). In either case,
627 // inform the Cache Manager at this time using CcSetFileSizes() about
628 // the new file length. Note that real FSD implementations will have to
629 // first allocate enough on-disk space at this point (before they
630 // inform the Cache Manager about the new size) to ensure that the write
631 // will subsequently not fail due to lack of disk space.
632
633 OldVDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart;
634 ZeroBlock = (ByteOffset.QuadPart > OldVDL);
635
636 if (!PagingIo &&
637 !RecursiveWriteThrough &&
638 !IsThisADeferredWrite) {
639
640 BOOLEAN ExtendFS;
641
642 ExtendFS = (ByteOffset.QuadPart + TruncatedLength > NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
643
644 if( WriteToEOF || ZeroBlock || ExtendFS) {
645 // we are extending the file;
646
647 if(!CanWait)
648 try_return(RC = STATUS_PENDING);
649 // CanWait = TRUE;
650 // Release any resources acquired above ...
651 if (PtrResourceAcquired2) {
652 UDFReleaseResource(PtrResourceAcquired2);
653 PtrResourceAcquired2 = NULL;
654 }
655 if (PtrResourceAcquired) {
656 UDFReleaseResource(PtrResourceAcquired);
657 PtrResourceAcquired = NULL;
658 }
659 if(!UDFAcquireResourceShared(&(Vcb->VCBResource), CanWait)) {
660 try_return(RC = STATUS_PENDING);
661 }
662 VcbAcquired = TRUE;
663 if(!Res1Acq) {
664 // Try to acquire the FCB MainResource exclusively
665 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
666 if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
667 try_return(RC = STATUS_PENDING);
668 }
669 // Remember the resource that was acquired
670 PtrResourceAcquired = &(NtReqFcb->MainResource);
671 }
672
673 if(!Res2Acq) {
674 // allocate space...
675 AdPrint((" Try to acquire PagingIoRes\n"));
676 UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE );
677 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
678 }
679 AdPrint((" PagingIoRes Ok, Resizing...\n"));
680
681 if(ExtendFS) {
682 RC = UDFResizeFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart + TruncatedLength);
683
684 if(!NT_SUCCESS(RC)) {
685 if(PtrResourceAcquired2) {
686 UDFReleaseResource(&(NtReqFcb->PagingIoResource));
687 PtrResourceAcquired2 = NULL;
688 }
689 try_return(RC);
690 }
691 Resized = TRUE;
692 // ... and inform the Cache Manager about it
693 NtReqFcb->CommonFCBHeader.FileSize.QuadPart = ByteOffset.QuadPart + TruncatedLength;
694 NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo);
695 if(!Vcb->LowFreeSpace) {
696 NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart += (PAGE_SIZE*9-1);
697 } else {
698 NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart += (PAGE_SIZE-1);
699 }
700 NtReqFcb->CommonFCBHeader.AllocationSize.LowPart &= ~(PAGE_SIZE-1);
701 }
702
703 KdPrint(("UDFCommonWrite: Set size %x (alloc size %x)\n", ByteOffset.LowPart + TruncatedLength, NtReqFcb->CommonFCBHeader.AllocationSize.LowPart));
704 if (CcIsFileCached(FileObject)) {
705 if(ExtendFS) {
706 MmPrint((" CcSetFileSizes()\n"));
707 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize));
708 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
709 }
710 // Attempt to Zero newly added fragment
711 // and ignore the return code
712 // This should be done to inform cache manager
713 // that given extent has no cached data
714 // (Otherwise, CM sometimes thinks that it has)
715 if(ZeroBlock) {
716 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
717 ThPrint((" UDFZeroDataEx(1)\n"));
718 UDFZeroDataEx(NtReqFcb,
719 OldVDL,
720 /*ByteOffset.QuadPart*/ NtReqFcb->CommonFCBHeader.FileSize.QuadPart - OldVDL,
721 CanWait, Vcb, FileObject);
722 #ifdef UDF_DBG
723 ZeroBlockDone = TRUE;
724 #endif //UDF_DBG
725 }
726 }
727 if (PtrResourceAcquired2) {
728 UDFReleaseResource(PtrResourceAcquired2);
729 PtrResourceAcquired2 = NULL;
730 }
731
732 // Inform any pending IRPs (notify change directory).
733 if(UDFIsAStream(Fcb->FileInfo)) {
734 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
735 FILE_NOTIFY_CHANGE_STREAM_SIZE,
736 FILE_ACTION_MODIFIED_STREAM);
737 } else {
738 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
739 FILE_NOTIFY_CHANGE_SIZE,
740 FILE_ACTION_MODIFIED);
741 }
742 }
743
744 }
745
746 #ifdef UDF_DISABLE_SYSTEM_CACHE_MANAGER
747 NonBufferedIo = TRUE;
748 #endif
749 if(Fcb && Fcb->FileInfo && Fcb->FileInfo->Dloc) {
750 AdPrint(("UDFCommonWrite: DataLoc %x, Mapping %x\n", Fcb->FileInfo->Dloc->DataLoc, Fcb->FileInfo->Dloc->DataLoc.Mapping));
751 }
752
753 // Branch here for cached vs non-cached I/O
754 if (!NonBufferedIo) {
755
756 // The caller wishes to perform cached I/O. Initiate caching if
757 // this is the first cached I/O operation using this file object
758 if (!FileObject->PrivateCacheMap) {
759 // This is the first cached I/O operation. You must ensure
760 // that the FCB Common FCB Header contains valid sizes at this time
761 KdPrint(("UDFCommonWrite: Init system cache\n"));
762 MmPrint((" CcInitializeCacheMap()\n"));
763 CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&(NtReqFcb->CommonFCBHeader.AllocationSize)),
764 FALSE, // We will not utilize pin access for this file
765 &(UDFGlobalData.CacheMgrCallBacks), // callbacks
766 NtReqFcb); // The context used in callbacks
767 MmPrint((" CcSetReadAheadGranularity()\n"));
768 CcSetReadAheadGranularity(FileObject, Vcb->SystemCacheGran);
769
770 }
771
772 if(ZeroBlock && !ZeroBlockDone) {
773 ThPrint((" UDFZeroDataEx(2)\n"));
774 UDFZeroDataEx(NtReqFcb,
775 OldVDL,
776 /*ByteOffset.QuadPart*/ ByteOffset.QuadPart + TruncatedLength - OldVDL,
777 CanWait, Vcb, FileObject);
778 if(ByteOffset.LowPart & (PAGE_SIZE-1)) {
779 }
780 }
781
782 WriteFileSizeToDirNdx = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_WRITE_THROUGH) ?
783 TRUE : FALSE;
784 // Check and see if this request requires a MDL returned to the caller
785 if (IrpSp->MinorFunction & IRP_MN_MDL) {
786 // Caller does want a MDL returned. Note that this mode
787 // implies that the caller is prepared to block
788 MmPrint((" CcPrepareMdlWrite()\n"));
789 // CcPrepareMdlWrite(FileObject, &ByteOffset, TruncatedLength, &(Irp->MdlAddress), &(Irp->IoStatus));
790 // NumberBytesWritten = Irp->IoStatus.Information;
791 // RC = Irp->IoStatus.Status;
792
793 NumberBytesWritten = 0;
794 RC = STATUS_INVALID_PARAMETER;
795
796 try_return(RC);
797 }
798
799 if(NtReqFcb->SectionObject.DataSectionObject &&
800 TruncatedLength >= 0x10000 &&
801 ByteOffset.LowPart &&
802 !(ByteOffset.LowPart & 0x00ffffff)) {
803
804 //if(WinVer_Id() < WinVer_2k) {
805 //LARGE_INTEGER flush_offs;
806 //flush_offs.QuadPart = ByteOffset.QuadPart - 0x100*0x10000;
807 MmPrint((" CcFlushCache() 16Mb\n"));
808 //CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, 0x100*0x10000, &(Irp->IoStatus));
809
810 // there was a nice idea: flush just previous part. But it doesn't work
811 CcFlushCache(&(NtReqFcb->SectionObject), NULL, 0, &(Irp->IoStatus));
812 //}
813 }
814
815 // This is a regular run-of-the-mill cached I/O request. Let the
816 // Cache Manager worry about it!
817 // First though, we need a buffer pointer (address) that is valid
818
819 // We needn't call CcZeroData 'cause udf_info.cpp will care about it
820 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
821 if(!SystemBuffer)
822 try_return(RC = STATUS_INVALID_USER_BUFFER);
823 ASSERT(SystemBuffer);
824 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
825 PerfPrint(("UDFCommonWrite: CcCopyWrite %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
826 MmPrint((" CcCopyWrite()\n"));
827 if(!CcCopyWrite(FileObject, &(ByteOffset), TruncatedLength, CanWait, SystemBuffer)) {
828 // The caller was not prepared to block and data is not immediately
829 // available in the system cache
830 // Mark Irp Pending ...
831 try_return(RC = STATUS_PENDING);
832 }
833
834 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
835 // We have the data
836 RC = STATUS_SUCCESS;
837 NumberBytesWritten = TruncatedLength;
838
839 try_return(RC);
840
841 } else {
842
843 MmPrint((" Write NonBufferedIo\n"));
844
845 // We needn't call CcZeroData here (like in Fat driver)
846 // 'cause we've already done it above
847 // (see call to UDFZeroDataEx() )
848 if (!RecursiveWriteThrough &&
849 !IsThisADeferredWrite &&
850 (OldVDL < ByteOffset.QuadPart)) {
851 #ifdef UDF_DBG
852 ASSERT(!ZeroBlockDone);
853 #endif //UDF_DBG
854 UDFZeroDataEx(NtReqFcb,
855 OldVDL,
856 /*ByteOffset.QuadPart*/ ByteOffset.QuadPart - OldVDL,
857 CanWait, Vcb, FileObject);
858 }
859 if(OldVDL < (ByteOffset.QuadPart + TruncatedLength)) {
860 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = ByteOffset.QuadPart + TruncatedLength;
861 }
862
863 #if 1
864 if((ULONG)TopIrp == FSRTL_MOD_WRITE_TOP_LEVEL_IRP) {
865 KdPrint(("FSRTL_MOD_WRITE_TOP_LEVEL_IRP => CanWait\n"));
866 CanWait = TRUE;
867 } else
868 if((ULONG)TopIrp == FSRTL_CACHE_TOP_LEVEL_IRP) {
869 KdPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n"));
870 CanWait = TRUE;
871 }
872
873 if(NtReqFcb->AcqSectionCount || NtReqFcb->AcqFlushCount) {
874 MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb->AcqSectionCount, NtReqFcb->AcqFlushCount));
875 CanWait = TRUE;
876 } else
877 {}
878 /* if((TopIrp != Irp)) {
879 KdPrint(("(TopIrp != Irp) => CanWait\n"));
880 CanWait = TRUE;
881 } else*/
882 #endif
883 if(KeGetCurrentIrql() > PASSIVE_LEVEL) {
884 MmPrint((" !PASSIVE_LEVEL\n"));
885 CanWait = FALSE;
886 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FORCED_POST;
887 }
888 // Successful check will cause WCache lock
889 if(!CanWait && UDFIsFileCached__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, TRUE)) {
890 KdPrint(("UDFCommonWrite: Cached => CanWait\n"));
891 CacheLocked = TRUE;
892 CanWait = TRUE;
893 }
894 // Send the request to lower level drivers
895 if(!CanWait) {
896 KdPrint(("UDFCommonWrite: Post physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
897
898 try_return(RC = STATUS_PENDING);
899 }
900
901 if(!Res2Acq) {
902 if(UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb->PagingIoResource))) {
903 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
904 }
905 }
906
907 PerfPrint(("UDFCommonWrite: Physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
908
909 // Lock the callers buffer
910 if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, TruncatedLength))) {
911 try_return(RC);
912 }
913
914 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
915 if(!SystemBuffer) {
916 try_return(RC = STATUS_INVALID_USER_BUFFER);
917 }
918 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
919 RC = UDFWriteFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength,
920 CacheLocked, (PCHAR)SystemBuffer, &NumberBytesWritten);
921
922 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
923
924 #ifdef _MSC_VER
925 /* FIXME */
926 if(PagingIo) {
927 CollectStatistics(Vcb, UserDiskWrites);
928 } else {
929 CollectStatistics2(Vcb, NonCachedDiskWrites);
930 }
931 #endif
932 WriteFileSizeToDirNdx = TRUE;
933
934 try_return(RC);
935 }
936
937 try_exit: NOTHING;
938
939 } _SEH2_FINALLY {
940
941 if(CacheLocked) {
942 WCacheEODirect__(&(Vcb->FastCache), Vcb);
943 }
944
945 // Release any resources acquired here ...
946 if(PtrResourceAcquired2) {
947 UDFReleaseResource(PtrResourceAcquired2);
948 }
949 if(PtrResourceAcquired) {
950 if(NtReqFcb &&
951 (PtrResourceAcquired ==
952 &(NtReqFcb->MainResource))) {
953 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
954 }
955 UDFReleaseResource(PtrResourceAcquired);
956 }
957 if(VcbAcquired) {
958 UDFReleaseResource(&(Vcb->VCBResource));
959 }
960
961 // Post IRP if required
962 if(RC == STATUS_PENDING) {
963
964 // Lock the callers buffer here. Then invoke a common routine to
965 // perform the post operation.
966 if (!(IrpSp->MinorFunction & IRP_MN_MDL)) {
967 RC = UDFLockCallersBuffer(PtrIrpContext, Irp, FALSE, WriteLength);
968 ASSERT(NT_SUCCESS(RC));
969 }
970 if(PagingIo) {
971 if(Res1Acq) {
972 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES1_ACQ;
973 }
974 if(Res2Acq) {
975 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES2_ACQ;
976 }
977 }
978
979 // Perform the post operation which will mark the IRP pending
980 // and will return STATUS_PENDING back to us
981 RC = UDFPostRequest(PtrIrpContext, Irp);
982
983 } else {
984 // For synchronous I/O, the FSD must maintain the current byte offset
985 // Do not do this however, if I/O is marked as paging-io
986 if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) {
987 FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + NumberBytesWritten;
988 }
989 // If the write completed successfully and this was not a paging-io
990 // operation, set a flag in the CCB that indicates that a write was
991 // performed and that the file time should be updated at cleanup
992 if (NT_SUCCESS(RC) && !PagingIo) {
993 Ccb->CCBFlags |= UDF_CCB_MODIFIED;
994 // If the file size was changed, set a flag in the FCB indicating that
995 // this occurred.
996 FileObject->Flags |= FO_FILE_MODIFIED;
997 if(Resized) {
998 if(!WriteFileSizeToDirNdx) {
999 FileObject->Flags |= FO_FILE_SIZE_CHANGED;
1000 } else {
1001 ASize = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo);
1002 UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &ASize);
1003 }
1004 }
1005 // Update ValidDataLength
1006 if(!IsThisADeferredWrite &&
1007 NtReqFcb) {
1008 if(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart < (ByteOffset.QuadPart + NumberBytesWritten)) {
1009
1010 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart =
1011 min(NtReqFcb->CommonFCBHeader.FileSize.QuadPart,
1012 ByteOffset.QuadPart + NumberBytesWritten);
1013 }
1014 }
1015 }
1016
1017 // If the request failed, and we had done some nasty stuff like
1018 // extending the file size (including informing the Cache Manager
1019 // about the new file size), and allocating on-disk space etc., undo
1020 // it at this time.
1021
1022 // Can complete the IRP here if no exception was encountered
1023 if(!_SEH2_AbnormalTermination() &&
1024 Irp) {
1025 Irp->IoStatus.Status = RC;
1026 Irp->IoStatus.Information = NumberBytesWritten;
1027 // complete the IRP
1028 MmPrint((" Complete Irp, MDL=%x\n", Irp->MdlAddress));
1029 if(Irp->MdlAddress) {
1030 UDFTouch(Irp->MdlAddress);
1031 }
1032 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
1033 }
1034 // Free up the Irp Context
1035 UDFReleaseIrpContext(PtrIrpContext);
1036
1037 } // can we complete the IRP ?
1038 } _SEH2_END; // end of "__finally" processing
1039
1040 KdPrint(("\n"));
1041 return(RC);
1042 } // end UDFCommonWrite()
1043
1044 /*************************************************************************
1045 *
1046 * Function: UDFDeferredWriteCallBack()
1047 *
1048 * Description:
1049 * Invoked by the cache manager in the context of a worker thread.
1050 * Typically, you can simply post the request at this point (just
1051 * as you would have if the original request could not block) to
1052 * perform the write in the context of a system worker thread.
1053 *
1054 * Expected Interrupt Level (for execution) :
1055 *
1056 * IRQL_PASSIVE_LEVEL
1057 *
1058 * Return Value: None
1059 *
1060 *************************************************************************/
1061 VOID
1062 NTAPI
1063 UDFDeferredWriteCallBack(
1064 IN PVOID Context1, // Should be PtrIrpContext
1065 IN PVOID Context2 // Should be Irp
1066 )
1067 {
1068 KdPrint(("UDFDeferredWriteCallBack\n"));
1069 // We should typically simply post the request to our internal
1070 // queue of posted requests (just as we would if the original write
1071 // could not be completed because the caller could not block).
1072 // Once we post the request, return from this routine. The write
1073 // will then be retried in the context of a system worker thread
1074 UDFPostRequest((PtrUDFIrpContext)Context1, (PIRP)Context2);
1075
1076 } // end UDFDeferredWriteCallBack()
1077
1078 /*************************************************************************
1079 *
1080 *************************************************************************/
1081
1082 #define USE_CcCopyWrite_TO_ZERO
1083
1084 VOID
1085 UDFPurgeCacheEx_(
1086 PtrUDFNTRequiredFCB NtReqFcb,
1087 LONGLONG Offset,
1088 LONGLONG Length,
1089 //#ifndef ALLOW_SPARSE
1090 BOOLEAN CanWait,
1091 //#endif //ALLOW_SPARSE
1092 PVCB Vcb,
1093 PFILE_OBJECT FileObject
1094 )
1095 {
1096 ULONG Off_l;
1097 #ifdef USE_CcCopyWrite_TO_ZERO
1098 ULONG PgLen;
1099 #endif //USE_CcCopyWrite_TO_ZERO
1100
1101 // We'll just purge cache section here,
1102 // without call to CcZeroData()
1103 // 'cause udf_info.cpp will care about it
1104
1105 #define PURGE_BLOCK_SZ 0x10000000
1106
1107 // NOTE: if FS engine doesn't suport
1108 // sparse/unrecorded areas, CcZeroData must be called
1109 // In this case we'll see some recursive WRITE requests
1110
1111 _SEH2_TRY {
1112 MmPrint((" UDFPurgeCacheEx_(): Offs: %I64x, ", Offset));
1113 MmPrint((" Len: %lx\n", Length));
1114 SECTION_OBJECT_POINTERS* SectionObject = &(NtReqFcb->SectionObject);
1115 if(Length) {
1116 LONGLONG Offset0, OffsetX, VDL;
1117
1118 Offset0 = Offset;
1119 if((Off_l = ((ULONG)Offset0 & (PAGE_SIZE-1)))) {
1120 // Offset, Offset0
1121 // v
1122 // ...|dddddddddddd00000|....
1123 // |<- Off_l ->|
1124 #ifndef USE_CcCopyWrite_TO_ZERO
1125 *((PULONG)&Offset0) &= ~(PAGE_SIZE-1);
1126 MmPrint((" CcFlushCache(s) Offs %I64x, Len %x\n", Offset0, Off_l));
1127 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset0, Off_l, NULL );
1128 #else //USE_CcCopyWrite_TO_ZERO
1129 // ...|ddddd000000000000|....
1130 // |<- PgLen ->|
1131 PgLen = PAGE_SIZE - Off_l; /*(*((PULONG)&Offset) & (PAGE_SIZE-1))*/
1132 //
1133 if(PgLen > Length)
1134 PgLen = (ULONG)Length;
1135
1136 MmPrint((" ZeroCache (CcWrite) Offs %I64x, Len %x\n", Offset, PgLen));
1137 #ifdef DBG
1138 if(FileObject && Vcb) {
1139
1140 ASSERT(CanWait);
1141 #endif //DBG
1142 if (PgLen) {
1143 if (SectionObject->SharedCacheMap) {
1144 CcCopyWrite(FileObject, (PLARGE_INTEGER)&Offset, PgLen, TRUE || CanWait, Vcb->ZBuffer);
1145 }
1146 Offset += PgLen;
1147 Length -= PgLen;
1148 }
1149 #ifdef DBG
1150 } else {
1151 MmPrint((" Can't use CcWrite to zero cache\n"));
1152 }
1153 #endif //DBG
1154 #endif //USE_CcCopyWrite_TO_ZERO
1155 }
1156 VDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart;
1157 OffsetX = Offset+Length;
1158 if((Off_l = ((ULONG)OffsetX & (PAGE_SIZE-1)))) {
1159
1160 if(OffsetX < VDL) {
1161 #ifndef USE_CcCopyWrite_TO_ZERO
1162 Off_l = ( (ULONG)(VDL-OffsetX) > PAGE_SIZE ) ?
1163 (PAGE_SIZE - Off_l) :
1164 ((ULONG)(VDL-OffsetX));
1165 *((PULONG)&OffsetX) &= ~(PAGE_SIZE-1);
1166 MmPrint((" CcFlushCache(e) Offs %I64x, Len %x\n", OffsetX, Off_l));
1167 CcFlushCache( SectionObject, (PLARGE_INTEGER)&OffsetX, Off_l, NULL );
1168 #else //USE_CcCopyWrite_TO_ZERO
1169 if(VDL - OffsetX > PAGE_SIZE) {
1170 PgLen = (ULONG)OffsetX & ~(PAGE_SIZE-1);
1171 } else {
1172 PgLen = (ULONG)(VDL - OffsetX) & ~(PAGE_SIZE-1);
1173 }
1174 // ...|000000000000ddddd|....
1175 // |<- PgLen ->|
1176 MmPrint((" ZeroCache (CcWrite - 2) Offs %I64x, Len %x\n", OffsetX, PgLen));
1177 #ifdef DBG
1178 if(FileObject && Vcb) {
1179 ASSERT(CanWait);
1180 #endif //DBG
1181 if (SectionObject->SharedCacheMap) {
1182 CcCopyWrite(FileObject, (PLARGE_INTEGER)&OffsetX, PgLen, TRUE || CanWait, Vcb->ZBuffer);
1183 }
1184 Length -= PgLen;
1185 #ifdef DBG
1186 } else {
1187 MmPrint((" Can't use CcWrite to zero cache (2)\n"));
1188 }
1189 #endif //DBG
1190 #endif //USE_CcCopyWrite_TO_ZERO
1191 }
1192 }
1193 #ifndef USE_CcCopyWrite_TO_ZERO
1194 do
1195 #else //USE_CcCopyWrite_TO_ZERO
1196 while(Length)
1197 #endif //USE_CcCopyWrite_TO_ZERO
1198 {
1199 MmPrint((" CcPurgeCacheSection()\n"));
1200 if(PURGE_BLOCK_SZ > Length) {
1201 CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset,
1202 (ULONG)Length, FALSE);
1203 /*
1204 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += Length;
1205 ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <=
1206 NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
1207 MmPrint((" CcFlushCache()\n"));
1208 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL );
1209 */
1210 #ifndef ALLOW_SPARSE
1211 // UDFZeroFile__(
1212 #endif //ALLOW_SPARSE
1213 break;
1214 } else {
1215 CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset,
1216 PURGE_BLOCK_SZ, FALSE);
1217 /*
1218 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += PURGE_BLOCK_SZ;
1219 ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <=
1220 NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
1221 MmPrint((" CcFlushCache()\n"));
1222 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL );
1223 */
1224 #ifndef ALLOW_SPARSE
1225 // UDFZeroFile__(
1226 #endif //ALLOW_SPARSE
1227 Length -= PURGE_BLOCK_SZ;
1228 Offset += PURGE_BLOCK_SZ;
1229 }
1230 }
1231 #ifndef USE_CcCopyWrite_TO_ZERO
1232 while(Length);
1233 #endif //USE_CcCopyWrite_TO_ZERO
1234 if(VDL < Offset)
1235 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = Offset;
1236 }
1237 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1238 BrutePoint();
1239 } _SEH2_END;
1240 } // end UDFPurgeCacheEx_()
1241
1242 #endif //UDF_READ_ONLY_BUILD