xref: /reactos/drivers/filesystems/udfs/write.cpp (revision c2c66aff)
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 #ifdef _MSC_VER
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 #ifdef _MSC_VER
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 #ifdef _MSC_VER
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
1228