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 SIZE_T 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_PTR)TopIrp) { 162 case FSRTL_FSP_TOP_LEVEL_IRP: 163 UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n")); 164 break; 165 case FSRTL_CACHE_TOP_LEVEL_IRP: 166 UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n")); 167 break; 168 case FSRTL_MOD_WRITE_TOP_LEVEL_IRP: 169 UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n")); 170 break; 171 case FSRTL_FAST_IO_TOP_LEVEL_IRP: 172 UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n")); 173 BrutePoint(); 174 break; 175 case NULL: 176 UDFPrint((" NULL TOP_LEVEL_IRP\n")); 177 break; 178 default: 179 if(TopIrp == Irp) { 180 UDFPrint((" TOP_LEVEL_IRP\n")); 181 } else { 182 UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp)); 183 } 184 break; 185 } 186 187 // First, get a pointer to the current I/O stack location 188 IrpSp = IoGetCurrentIrpStackLocation(Irp); 189 ASSERT(IrpSp); 190 MmPrint((" Enter Irp, MDL=%x\n", Irp->MdlAddress)); 191 if(Irp->MdlAddress) { 192 UDFTouch(Irp->MdlAddress); 193 } 194 195 FileObject = IrpSp->FileObject; 196 ASSERT(FileObject); 197 198 // If this happens to be a MDL write complete request, then 199 // allocated MDL can be freed. This may cause a recursive write 200 // back into the FSD. 201 if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { 202 // Caller wants to tell the Cache Manager that a previously 203 // allocated MDL can be freed. 204 UDFMdlComplete(PtrIrpContext, Irp, IrpSp, FALSE); 205 // The IRP has been completed. 206 try_return(RC = STATUS_SUCCESS); 207 } 208 209 // If this is a request at IRQL DISPATCH_LEVEL, then post the request 210 if (IrpSp->MinorFunction & IRP_MN_DPC) { 211 try_return(RC = STATUS_PENDING); 212 } 213 214 // Get the FCB and CCB pointers 215 Ccb = (PtrUDFCCB)(FileObject->FsContext2); 216 ASSERT(Ccb); 217 Fcb = Ccb->Fcb; 218 ASSERT(Fcb); 219 Vcb = Fcb->Vcb; 220 221 if(Fcb->FCBFlags & UDF_FCB_DELETED) { 222 ASSERT(FALSE); 223 try_return(RC = STATUS_TOO_LATE); 224 } 225 226 // is this operation allowed ? 227 if(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY) { 228 try_return(RC = STATUS_ACCESS_DENIED); 229 } 230 Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; 231 232 // Disk based file systems might decide to verify the logical volume 233 // (if required and only if removable media are supported) at this time 234 // As soon as Tray is locked, we needn't call UDFVerifyVcb() 235 236 ByteOffset = IrpSp->Parameters.Write.ByteOffset; 237 238 CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; 239 PagingIo = (Irp->Flags & IRP_PAGING_IO) ? TRUE : FALSE; 240 NonBufferedIo = (Irp->Flags & IRP_NOCACHE) ? TRUE : FALSE; 241 SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO) ? TRUE : FALSE; 242 UDFPrint((" Flags: %s; %s; %s; %s; Irp(W): %8.8x\n", 243 CanWait ? "Wt" : "nw", PagingIo ? "Pg" : "np", 244 NonBufferedIo ? "NBuf" : "buff", SynchronousIo ? "Snc" : "Asc", 245 Irp->Flags)); 246 247 NtReqFcb = Fcb->NTRequiredFCB; 248 249 Res1Acq = UDFIsResourceAcquired(&(NtReqFcb->MainResource)); 250 if(!Res1Acq) { 251 Res1Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES1_ACQ; 252 } 253 Res2Acq = UDFIsResourceAcquired(&(NtReqFcb->PagingIoResource)); 254 if(!Res2Acq) { 255 Res2Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES2_ACQ; 256 } 257 258 if(!NonBufferedIo && 259 (Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB)) { 260 if((Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB) && 261 UDFIsAStream(Fcb->FileInfo)) { 262 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, 263 FILE_NOTIFY_CHANGE_STREAM_WRITE, 264 FILE_ACTION_MODIFIED_STREAM); 265 } else { 266 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, 267 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS, 268 FILE_ACTION_MODIFIED); 269 } 270 } 271 272 // Get some of the parameters supplied to us 273 WriteLength = IrpSp->Parameters.Write.Length; 274 if (WriteLength == 0) { 275 // a 0 byte write can be immediately succeeded 276 if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) { 277 // NT expects changing CurrentByteOffset to zero in this case 278 FileObject->CurrentByteOffset.QuadPart = 0; 279 } 280 try_return(RC); 281 } 282 283 // If this is the normal file we have to check for 284 // write access according to the current state of the file locks. 285 if (!PagingIo && 286 !FsRtlCheckLockForWriteAccess( &(NtReqFcb->FileLock), Irp) ) { 287 try_return( RC = STATUS_FILE_LOCK_CONFLICT ); 288 } 289 290 // ********** 291 // Is this a write of the volume itself ? 292 // ********** 293 if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) { 294 // Yup, we need to send this on to the disk driver after 295 // validation of the offset and length. 296 Vcb = (PVCB)(Fcb); 297 if(!CanWait) 298 try_return(RC = STATUS_PENDING); 299 // I dislike the idea of writing to not locked media 300 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED)) { 301 try_return(RC = STATUS_ACCESS_DENIED); 302 } 303 304 if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) { 305 306 UDFPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n")); 307 PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED; 308 309 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) { 310 UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); 311 } 312 #ifdef UDF_DELAYED_CLOSE 313 UDFCloseAllDelayed(Vcb); 314 #endif //UDF_DELAYED_CLOSE 315 316 } 317 318 // Acquire the volume resource exclusive 319 UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); 320 PtrResourceAcquired = &(Vcb->VCBResource); 321 322 // I dislike the idea of writing to mounted media too, but M$ has another point of view... 323 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) { 324 // flush system cache 325 UDFFlushLogicalVolume(NULL, NULL, Vcb, 0); 326 } 327 #if defined(_MSC_VER) && !defined(__clang__) 328 /* FIXME */ 329 if(PagingIo) { 330 CollectStatistics(Vcb, MetaDataWrites); 331 CollectStatisticsEx(Vcb, MetaDataWriteBytes, NumberBytesWritten); 332 } 333 #endif 334 // Forward the request to the lower level driver 335 // Lock the callers buffer 336 if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, WriteLength))) { 337 try_return(RC); 338 } 339 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); 340 if(!SystemBuffer) 341 try_return(RC = STATUS_INVALID_USER_BUFFER); 342 // Indicate, that volume contents can change after this operation 343 // This flag will force VerifyVolume in future 344 UDFPrint((" set UnsafeIoctl\n")); 345 Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL; 346 // Make sure, that volume will never be quick-remounted 347 // It is very important for ChkUdf utility. 348 Vcb->SerialNumber--; 349 // Perform actual Write 350 RC = UDFTWrite(Vcb, SystemBuffer, WriteLength, 351 (ULONG)(ByteOffset.QuadPart >> Vcb->BlockSizeBits), 352 &NumberBytesWritten); 353 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); 354 try_return(RC); 355 } 356 357 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) { 358 try_return(RC = STATUS_ACCESS_DENIED); 359 } 360 361 // back pressure for very smart and fast system cache ;) 362 if(!NonBufferedIo) { 363 // cached IO 364 if(Vcb->VerifyCtx.QueuedCount || 365 Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) { 366 UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT); 367 } 368 } else { 369 if(Vcb->VerifyCtx.ItemCount > UDF_SYS_CACHE_STOP_THR) { 370 UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT); 371 } 372 } 373 374 // The FSD (if it is a "nice" FSD) should check whether it is 375 // convenient to allow the write to proceed by utilizing the 376 // CcCanIWrite() function call. If it is not convenient to perform 377 // the write at this time, we should defer the request for a while. 378 // The check should not however be performed for non-cached write 379 // operations. To determine whether we are retrying the operation 380 // or now, use Flags in the IrpContext structure we have created 381 382 IsThisADeferredWrite = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_DEFERRED_WRITE) ? TRUE : FALSE; 383 384 if (!NonBufferedIo) { 385 MmPrint((" CcCanIWrite()\n")); 386 if (!CcCanIWrite(FileObject, WriteLength, CanWait, IsThisADeferredWrite)) { 387 // Cache Manager and/or the VMM does not want us to perform 388 // the write at this time. Post the request. 389 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_DEFERRED_WRITE; 390 UDFPrint(("UDFCommonWrite: Defer write\n")); 391 MmPrint((" CcDeferWrite()\n")); 392 CcDeferWrite(FileObject, UDFDeferredWriteCallBack, PtrIrpContext, Irp, WriteLength, IsThisADeferredWrite); 393 try_return(RC = STATUS_PENDING); 394 } 395 } 396 397 // If the write request is directed to a page file, 398 // send the request directly to the disk 399 if (Fcb->FCBFlags & UDF_FCB_PAGE_FILE) { 400 NonBufferedIo = TRUE; 401 } 402 403 // We can continue. Check whether this write operation is targeted 404 // to a directory object in which case the UDF FSD will disallow 405 // the write request. 406 if (Fcb->FCBFlags & UDF_FCB_DIRECTORY) { 407 RC = STATUS_INVALID_DEVICE_REQUEST; 408 try_return(RC); 409 } 410 411 // Validate start offset and length supplied. 412 // Here is a special check that determines whether the caller wishes to 413 // begin the write at current end-of-file (whatever the value of that 414 // offset might be) 415 if(ByteOffset.HighPart == (LONG)0xFFFFFFFF) { 416 if(ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE) { 417 WriteToEOF = TRUE; 418 ByteOffset = NtReqFcb->CommonFCBHeader.FileSize; 419 } else 420 if(ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) { 421 ByteOffset = FileObject->CurrentByteOffset; 422 } 423 } 424 425 // Check if this volume has already been shut down. If it has, fail 426 // this write request. 427 if (Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN) { 428 try_return(RC = STATUS_TOO_LATE); 429 } 430 431 // Paging I/O write operations are special. If paging i/o write 432 // requests begin beyond end-of-file, the request should be no-oped 433 // If paging i/o 434 // requests extend beyond current end of file, they should be truncated 435 // to current end-of-file. 436 if(PagingIo && (WriteToEOF || ((ByteOffset.QuadPart + WriteLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart))) { 437 if (ByteOffset.QuadPart > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) { 438 TruncatedLength = 0; 439 } else { 440 TruncatedLength = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart); 441 } 442 if(!TruncatedLength) try_return(RC = STATUS_SUCCESS); 443 } else { 444 TruncatedLength = WriteLength; 445 } 446 447 #if defined(_MSC_VER) && !defined(__clang__) 448 /* FIXME */ 449 if(PagingIo) { 450 CollectStatistics(Vcb, UserFileWrites); 451 CollectStatisticsEx(Vcb, UserFileWriteBytes, NumberBytesWritten); 452 } 453 #endif 454 455 // There are certain complications that arise when the same file stream 456 // has been opened for cached and non-cached access. The FSD is then 457 // responsible for maintaining a consistent view of the data seen by 458 // the caller. 459 // If this happens to be a non-buffered I/O, we should __try to flush the 460 // cached data (if some other file object has already initiated caching 461 // on the file stream). We should also __try to purge the cached 462 // information though the purge will probably fail if the file has been 463 // mapped into some process' virtual address space 464 // WARNING !!! we should not flush data beyond valid data length 465 if ( NonBufferedIo && 466 !PagingIo && 467 NtReqFcb->SectionObject.DataSectionObject && 468 TruncatedLength && 469 (ByteOffset.QuadPart < NtReqFcb->CommonFCBHeader.FileSize.QuadPart)) { 470 471 if(!Res1Acq) { 472 // Try to acquire the FCB MainResource exclusively 473 if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) { 474 try_return(RC = STATUS_PENDING); 475 } 476 PtrResourceAcquired = &(NtReqFcb->MainResource); 477 } 478 479 if(!Res2Acq) { 480 // We hold PagingIo shared around the flush to fix a 481 // cache coherency problem. 482 UDFAcquireSharedStarveExclusive(&(NtReqFcb->PagingIoResource), TRUE ); 483 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); 484 } 485 486 // Flush and then attempt to purge the cache 487 if((ByteOffset.QuadPart + TruncatedLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) { 488 NumberBytesWritten = TruncatedLength; 489 } else { 490 NumberBytesWritten = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart); 491 } 492 493 MmPrint((" CcFlushCache()\n")); 494 CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, NumberBytesWritten, &(Irp->IoStatus)); 495 496 if(PtrResourceAcquired2) { 497 UDFReleaseResource(&(NtReqFcb->PagingIoResource)); 498 PtrResourceAcquired2 = NULL; 499 } 500 // If the flush failed, return error to the caller 501 if (!NT_SUCCESS(RC = Irp->IoStatus.Status)) { 502 NumberBytesWritten = 0; 503 try_return(RC); 504 } 505 506 if(!Res2Acq) { 507 // Acquiring and immediately dropping the resource serializes 508 // us behind any other writes taking place (either from the 509 // lazy writer or modified page writer). 510 UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE ); 511 UDFReleaseResource(&(NtReqFcb->PagingIoResource)); 512 } 513 514 // Attempt the purge and ignore the return code 515 MmPrint((" CcPurgeCacheSection()\n")); 516 CcPurgeCacheSection(&(NtReqFcb->SectionObject), &ByteOffset, 517 NumberBytesWritten, FALSE); 518 NumberBytesWritten = 0; 519 // We are finished with our flushing and purging 520 if(PtrResourceAcquired) { 521 UDFReleaseResource(PtrResourceAcquired); 522 PtrResourceAcquired = NULL; 523 } 524 } 525 526 // Determine if we were called by the lazywriter. 527 // We reuse 'IsThisADeferredWrite' here to decrease stack usage 528 IsThisADeferredWrite = (NtReqFcb->LazyWriterThreadID == HandleToUlong(PsGetCurrentThreadId())); 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_PTR)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_PTR)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_PTR)TopIrp == FSRTL_CACHE_TOP_LEVEL_IRP) { 854 UDFPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n")); 855 CanWait = TRUE; 856 } 857 858 if(NtReqFcb->AcqSectionCount || NtReqFcb->AcqFlushCount) { 859 MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb->AcqSectionCount, NtReqFcb->AcqFlushCount)); 860 CanWait = TRUE; 861 } else 862 {} 863 /* if((TopIrp != Irp)) { 864 UDFPrint(("(TopIrp != Irp) => CanWait\n")); 865 CanWait = TRUE; 866 } else*/ 867 #endif 868 if(KeGetCurrentIrql() > PASSIVE_LEVEL) { 869 MmPrint((" !PASSIVE_LEVEL\n")); 870 CanWait = FALSE; 871 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FORCED_POST; 872 } 873 // Successful check will cause WCache lock 874 if(!CanWait && UDFIsFileCached__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, TRUE)) { 875 UDFPrint(("UDFCommonWrite: Cached => CanWait\n")); 876 CacheLocked = TRUE; 877 CanWait = TRUE; 878 } 879 // Send the request to lower level drivers 880 if(!CanWait) { 881 UDFPrint(("UDFCommonWrite: Post physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart)); 882 883 try_return(RC = STATUS_PENDING); 884 } 885 886 if(!Res2Acq) { 887 if(UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb->PagingIoResource))) { 888 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); 889 } 890 } 891 892 PerfPrint(("UDFCommonWrite: Physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart)); 893 894 // Lock the callers buffer 895 if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, TruncatedLength))) { 896 try_return(RC); 897 } 898 899 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); 900 if(!SystemBuffer) { 901 try_return(RC = STATUS_INVALID_USER_BUFFER); 902 } 903 NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED; 904 RC = UDFWriteFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, 905 CacheLocked, (PCHAR)SystemBuffer, &NumberBytesWritten); 906 907 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); 908 909 #if defined(_MSC_VER) && !defined(__clang__) 910 /* FIXME */ 911 if(PagingIo) { 912 CollectStatistics(Vcb, UserDiskWrites); 913 } else { 914 CollectStatistics2(Vcb, NonCachedDiskWrites); 915 } 916 #endif 917 WriteFileSizeToDirNdx = TRUE; 918 919 try_return(RC); 920 } 921 922 try_exit: NOTHING; 923 924 } _SEH2_FINALLY { 925 926 if(CacheLocked) { 927 WCacheEODirect__(&(Vcb->FastCache), Vcb); 928 } 929 930 // Release any resources acquired here ... 931 if(PtrResourceAcquired2) { 932 UDFReleaseResource(PtrResourceAcquired2); 933 } 934 if(PtrResourceAcquired) { 935 if(NtReqFcb && 936 (PtrResourceAcquired == 937 &(NtReqFcb->MainResource))) { 938 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 939 } 940 UDFReleaseResource(PtrResourceAcquired); 941 } 942 if(VcbAcquired) { 943 UDFReleaseResource(&(Vcb->VCBResource)); 944 } 945 946 // Post IRP if required 947 if(RC == STATUS_PENDING) { 948 949 // Lock the callers buffer here. Then invoke a common routine to 950 // perform the post operation. 951 if (!(IrpSp->MinorFunction & IRP_MN_MDL)) { 952 RC = UDFLockCallersBuffer(PtrIrpContext, Irp, FALSE, WriteLength); 953 ASSERT(NT_SUCCESS(RC)); 954 } 955 if(PagingIo) { 956 if(Res1Acq) { 957 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES1_ACQ; 958 } 959 if(Res2Acq) { 960 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES2_ACQ; 961 } 962 } 963 964 // Perform the post operation which will mark the IRP pending 965 // and will return STATUS_PENDING back to us 966 RC = UDFPostRequest(PtrIrpContext, Irp); 967 968 } else { 969 // For synchronous I/O, the FSD must maintain the current byte offset 970 // Do not do this however, if I/O is marked as paging-io 971 if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) { 972 FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + NumberBytesWritten; 973 } 974 // If the write completed successfully and this was not a paging-io 975 // operation, set a flag in the CCB that indicates that a write was 976 // performed and that the file time should be updated at cleanup 977 if (NT_SUCCESS(RC) && !PagingIo) { 978 Ccb->CCBFlags |= UDF_CCB_MODIFIED; 979 // If the file size was changed, set a flag in the FCB indicating that 980 // this occurred. 981 FileObject->Flags |= FO_FILE_MODIFIED; 982 if(Resized) { 983 if(!WriteFileSizeToDirNdx) { 984 FileObject->Flags |= FO_FILE_SIZE_CHANGED; 985 } else { 986 ASize = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo); 987 UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &ASize); 988 } 989 } 990 // Update ValidDataLength 991 if(!IsThisADeferredWrite && 992 NtReqFcb) { 993 if(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart < (ByteOffset.QuadPart + NumberBytesWritten)) { 994 995 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = 996 min(NtReqFcb->CommonFCBHeader.FileSize.QuadPart, 997 ByteOffset.QuadPart + NumberBytesWritten); 998 } 999 } 1000 } 1001 1002 // If the request failed, and we had done some nasty stuff like 1003 // extending the file size (including informing the Cache Manager 1004 // about the new file size), and allocating on-disk space etc., undo 1005 // it at this time. 1006 1007 // Can complete the IRP here if no exception was encountered 1008 if(!_SEH2_AbnormalTermination() && 1009 Irp) { 1010 Irp->IoStatus.Status = RC; 1011 Irp->IoStatus.Information = NumberBytesWritten; 1012 // complete the IRP 1013 MmPrint((" Complete Irp, MDL=%x\n", Irp->MdlAddress)); 1014 if(Irp->MdlAddress) { 1015 UDFTouch(Irp->MdlAddress); 1016 } 1017 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 1018 } 1019 // Free up the Irp Context 1020 UDFReleaseIrpContext(PtrIrpContext); 1021 1022 } // can we complete the IRP ? 1023 } _SEH2_END; // end of "__finally" processing 1024 1025 UDFPrint(("\n")); 1026 return(RC); 1027 } // end UDFCommonWrite() 1028 1029 /************************************************************************* 1030 * 1031 * Function: UDFDeferredWriteCallBack() 1032 * 1033 * Description: 1034 * Invoked by the cache manager in the context of a worker thread. 1035 * Typically, you can simply post the request at this point (just 1036 * as you would have if the original request could not block) to 1037 * perform the write in the context of a system worker thread. 1038 * 1039 * Expected Interrupt Level (for execution) : 1040 * 1041 * IRQL_PASSIVE_LEVEL 1042 * 1043 * Return Value: None 1044 * 1045 *************************************************************************/ 1046 VOID 1047 NTAPI 1048 UDFDeferredWriteCallBack( 1049 IN PVOID Context1, // Should be PtrIrpContext 1050 IN PVOID Context2 // Should be Irp 1051 ) 1052 { 1053 UDFPrint(("UDFDeferredWriteCallBack\n")); 1054 // We should typically simply post the request to our internal 1055 // queue of posted requests (just as we would if the original write 1056 // could not be completed because the caller could not block). 1057 // Once we post the request, return from this routine. The write 1058 // will then be retried in the context of a system worker thread 1059 UDFPostRequest((PtrUDFIrpContext)Context1, (PIRP)Context2); 1060 1061 } // end UDFDeferredWriteCallBack() 1062 1063 /************************************************************************* 1064 * 1065 *************************************************************************/ 1066 1067 #define USE_CcCopyWrite_TO_ZERO 1068 1069 VOID 1070 UDFPurgeCacheEx_( 1071 PtrUDFNTRequiredFCB NtReqFcb, 1072 LONGLONG Offset, 1073 LONGLONG Length, 1074 //#ifndef ALLOW_SPARSE 1075 BOOLEAN CanWait, 1076 //#endif //ALLOW_SPARSE 1077 PVCB Vcb, 1078 PFILE_OBJECT FileObject 1079 ) 1080 { 1081 ULONG Off_l; 1082 #ifdef USE_CcCopyWrite_TO_ZERO 1083 ULONG PgLen; 1084 #endif //USE_CcCopyWrite_TO_ZERO 1085 1086 // We'll just purge cache section here, 1087 // without call to CcZeroData() 1088 // 'cause udf_info.cpp will care about it 1089 1090 #define PURGE_BLOCK_SZ 0x10000000 1091 1092 // NOTE: if FS engine doesn't suport 1093 // sparse/unrecorded areas, CcZeroData must be called 1094 // In this case we'll see some recursive WRITE requests 1095 1096 _SEH2_TRY { 1097 MmPrint((" UDFPurgeCacheEx_(): Offs: %I64x, ", Offset)); 1098 MmPrint((" Len: %lx\n", Length)); 1099 SECTION_OBJECT_POINTERS* SectionObject = &(NtReqFcb->SectionObject); 1100 if(Length) { 1101 LONGLONG Offset0, OffsetX, VDL; 1102 1103 Offset0 = Offset; 1104 if((Off_l = ((ULONG)Offset0 & (PAGE_SIZE-1)))) { 1105 // Offset, Offset0 1106 // v 1107 // ...|dddddddddddd00000|.... 1108 // |<- Off_l ->| 1109 #ifndef USE_CcCopyWrite_TO_ZERO 1110 *((PULONG)&Offset0) &= ~(PAGE_SIZE-1); 1111 MmPrint((" CcFlushCache(s) Offs %I64x, Len %x\n", Offset0, Off_l)); 1112 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset0, Off_l, NULL ); 1113 #else //USE_CcCopyWrite_TO_ZERO 1114 // ...|ddddd000000000000|.... 1115 // |<- PgLen ->| 1116 PgLen = PAGE_SIZE - Off_l; /*(*((PULONG)&Offset) & (PAGE_SIZE-1))*/ 1117 // 1118 if(PgLen > Length) 1119 PgLen = (ULONG)Length; 1120 1121 MmPrint((" ZeroCache (CcWrite) Offs %I64x, Len %x\n", Offset, PgLen)); 1122 #ifdef DBG 1123 if(FileObject && Vcb) { 1124 1125 ASSERT(CanWait); 1126 #endif //DBG 1127 if (PgLen) { 1128 if (SectionObject->SharedCacheMap) { 1129 CcCopyWrite(FileObject, (PLARGE_INTEGER)&Offset, PgLen, TRUE || CanWait, Vcb->ZBuffer); 1130 } 1131 Offset += PgLen; 1132 Length -= PgLen; 1133 } 1134 #ifdef DBG 1135 } else { 1136 MmPrint((" Can't use CcWrite to zero cache\n")); 1137 } 1138 #endif //DBG 1139 #endif //USE_CcCopyWrite_TO_ZERO 1140 } 1141 VDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart; 1142 OffsetX = Offset+Length; 1143 if((Off_l = ((ULONG)OffsetX & (PAGE_SIZE-1)))) { 1144 1145 if(OffsetX < VDL) { 1146 #ifndef USE_CcCopyWrite_TO_ZERO 1147 Off_l = ( (ULONG)(VDL-OffsetX) > PAGE_SIZE ) ? 1148 (PAGE_SIZE - Off_l) : 1149 ((ULONG)(VDL-OffsetX)); 1150 *((PULONG)&OffsetX) &= ~(PAGE_SIZE-1); 1151 MmPrint((" CcFlushCache(e) Offs %I64x, Len %x\n", OffsetX, Off_l)); 1152 CcFlushCache( SectionObject, (PLARGE_INTEGER)&OffsetX, Off_l, NULL ); 1153 #else //USE_CcCopyWrite_TO_ZERO 1154 if(VDL - OffsetX > PAGE_SIZE) { 1155 PgLen = (ULONG)OffsetX & ~(PAGE_SIZE-1); 1156 } else { 1157 PgLen = (ULONG)(VDL - OffsetX) & ~(PAGE_SIZE-1); 1158 } 1159 // ...|000000000000ddddd|.... 1160 // |<- PgLen ->| 1161 MmPrint((" ZeroCache (CcWrite - 2) Offs %I64x, Len %x\n", OffsetX, PgLen)); 1162 #ifdef DBG 1163 if(FileObject && Vcb) { 1164 ASSERT(CanWait); 1165 #endif //DBG 1166 if (SectionObject->SharedCacheMap) { 1167 CcCopyWrite(FileObject, (PLARGE_INTEGER)&OffsetX, PgLen, TRUE || CanWait, Vcb->ZBuffer); 1168 } 1169 Length -= PgLen; 1170 #ifdef DBG 1171 } else { 1172 MmPrint((" Can't use CcWrite to zero cache (2)\n")); 1173 } 1174 #endif //DBG 1175 #endif //USE_CcCopyWrite_TO_ZERO 1176 } 1177 } 1178 #ifndef USE_CcCopyWrite_TO_ZERO 1179 do 1180 #else //USE_CcCopyWrite_TO_ZERO 1181 while(Length) 1182 #endif //USE_CcCopyWrite_TO_ZERO 1183 { 1184 MmPrint((" CcPurgeCacheSection()\n")); 1185 if(PURGE_BLOCK_SZ > Length) { 1186 CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset, 1187 (ULONG)Length, FALSE); 1188 /* 1189 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += Length; 1190 ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <= 1191 NtReqFcb->CommonFCBHeader.FileSize.QuadPart); 1192 MmPrint((" CcFlushCache()\n")); 1193 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL ); 1194 */ 1195 #ifndef ALLOW_SPARSE 1196 // UDFZeroFile__( 1197 #endif //ALLOW_SPARSE 1198 break; 1199 } else { 1200 CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset, 1201 PURGE_BLOCK_SZ, FALSE); 1202 /* 1203 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += PURGE_BLOCK_SZ; 1204 ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <= 1205 NtReqFcb->CommonFCBHeader.FileSize.QuadPart); 1206 MmPrint((" CcFlushCache()\n")); 1207 CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL ); 1208 */ 1209 #ifndef ALLOW_SPARSE 1210 // UDFZeroFile__( 1211 #endif //ALLOW_SPARSE 1212 Length -= PURGE_BLOCK_SZ; 1213 Offset += PURGE_BLOCK_SZ; 1214 } 1215 } 1216 #ifndef USE_CcCopyWrite_TO_ZERO 1217 while(Length); 1218 #endif //USE_CcCopyWrite_TO_ZERO 1219 if(VDL < Offset) 1220 NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = Offset; 1221 } 1222 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 1223 BrutePoint(); 1224 } _SEH2_END; 1225 } // end UDFPurgeCacheEx_() 1226 1227 #endif //UDF_READ_ONLY_BUILD 1228