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: Read.cpp 9 * 10 * Module: UDF File System Driver (Kernel mode execution only) 11 * 12 * Description: 13 * Contains code to handle the "Read" 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_READ 21 22 #ifdef _M_IX86 23 #if DBG 24 #define OVERFLOW_READ_THRESHHOLD (0xE00) 25 #else 26 #define OVERFLOW_READ_THRESHHOLD (0xA00) 27 #endif // UDF_DBG 28 #else // defined(_M_IX86) 29 #define OVERFLOW_READ_THRESHHOLD (0x1000) 30 #endif // defined(_M_IX86) 31 32 //#define POST_LOCK_PAGES 33 34 35 /************************************************************************* 36 * 37 * Function: UDFRead() 38 * 39 * Description: 40 * The I/O Manager will invoke this routine to handle a read 41 * request 42 * 43 * Expected Interrupt Level (for execution) : 44 * 45 * IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution 46 * to be deferred to a worker thread context) 47 * 48 * Return Value: STATUS_SUCCESS/Error 49 * 50 *************************************************************************/ 51 NTSTATUS 52 NTAPI 53 UDFRead( 54 PDEVICE_OBJECT DeviceObject, // the logical volume device object 55 PIRP Irp) // I/O Request Packet 56 { 57 NTSTATUS RC = STATUS_SUCCESS; 58 PtrUDFIrpContext PtrIrpContext = NULL; 59 BOOLEAN AreWeTopLevel = FALSE; 60 61 TmPrint(("UDFRead: \n")); 62 63 FsRtlEnterFileSystem(); 64 ASSERT(DeviceObject); 65 ASSERT(Irp); 66 67 // set the top level context 68 AreWeTopLevel = UDFIsIrpTopLevel(Irp); 69 ASSERT(!UDFIsFSDevObj(DeviceObject)); 70 71 _SEH2_TRY { 72 73 // get an IRP context structure and issue the request 74 PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject); 75 if(PtrIrpContext) { 76 RC = UDFCommonRead(PtrIrpContext, Irp); 77 } else { 78 RC = STATUS_INSUFFICIENT_RESOURCES; 79 Irp->IoStatus.Status = RC; 80 Irp->IoStatus.Information = 0; 81 // complete the IRP 82 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 83 } 84 85 } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) { 86 87 RC = UDFExceptionHandler(PtrIrpContext, Irp); 88 89 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC); 90 } _SEH2_END; 91 92 if (AreWeTopLevel) { 93 IoSetTopLevelIrp(NULL); 94 } 95 96 FsRtlExitFileSystem(); 97 98 return(RC); 99 } // end UDFRead() 100 101 102 /************************************************************************* 103 * 104 * Function: UDFPostStackOverflowRead() 105 * 106 * Description: 107 * Post a read request that could not be processed by 108 * the fsp thread because of stack overflow potential. 109 * 110 * Arguments: 111 * Irp - Supplies the request to process. 112 * Fcb - Supplies the file. 113 * 114 * Return Value: STATUS_PENDING. 115 * 116 *************************************************************************/ 117 NTSTATUS 118 UDFPostStackOverflowRead( 119 IN PtrUDFIrpContext PtrIrpContext, 120 IN PIRP Irp, 121 IN PtrUDFFCB Fcb 122 ) 123 { 124 PKEVENT Event; 125 PERESOURCE Resource; 126 127 UDFPrint(("Getting too close to stack limit pass request to Fsp\n")); 128 129 // Allocate an event and get shared on the resource we will 130 // be later using the common read. 131 Event = (PKEVENT)MyAllocatePool__(NonPagedPool, sizeof(KEVENT)); 132 if(!Event) 133 return STATUS_INSUFFICIENT_RESOURCES; 134 KeInitializeEvent( Event, NotificationEvent, FALSE ); 135 136 if ((Irp->Flags & IRP_PAGING_IO) && (Fcb->NTRequiredFCB->CommonFCBHeader.PagingIoResource)) { 137 Resource = Fcb->NTRequiredFCB->CommonFCBHeader.PagingIoResource; 138 } else { 139 Resource = Fcb->NTRequiredFCB->CommonFCBHeader.Resource; 140 } 141 142 UDFAcquireResourceShared( Resource, TRUE ); 143 144 _SEH2_TRY { 145 // If this read is the result of a verify, we have to 146 // tell the overflow read routne to temporarily 147 // hijack the Vcb->VerifyThread field so that reads 148 // can go through. 149 FsRtlPostStackOverflow(PtrIrpContext, Event, UDFStackOverflowRead); 150 // And wait for the worker thread to complete the item 151 DbgWaitForSingleObject(Event, NULL); 152 153 } _SEH2_FINALLY { 154 155 UDFReleaseResource( Resource ); 156 MyFreePool__( Event ); 157 } _SEH2_END; 158 159 return STATUS_PENDING; 160 161 } // end UDFPostStackOverflowRead() 162 163 /************************************************************************* 164 * 165 * Function: UDFStackOverflowRead() 166 * 167 * Description: 168 * Process a read request that could not be processed by 169 * the fsp thread because of stack overflow potential. 170 * 171 * Arguments: 172 * Context - Supplies the IrpContext being processed 173 * Event - Supplies the event to be signaled when we are done processing this 174 * request. 175 * 176 * Expected Interrupt Level (for execution) : 177 * 178 * IRQL_PASSIVE_LEVEL 179 * 180 * Return Value: None. 181 * 182 *************************************************************************/ 183 VOID 184 NTAPI 185 UDFStackOverflowRead( 186 IN PVOID Context, 187 IN PKEVENT Event 188 ) 189 { 190 PtrUDFIrpContext PtrIrpContext = (PtrUDFIrpContext)Context; 191 NTSTATUS RC; 192 193 UDFPrint(("UDFStackOverflowRead: \n")); 194 // Make it now look like we can wait for I/O to complete 195 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_CAN_BLOCK; 196 197 // Do the read operation protected by a try-except clause 198 _SEH2_TRY { 199 UDFCommonRead(PtrIrpContext, PtrIrpContext->Irp); 200 } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) { 201 RC = UDFExceptionHandler(PtrIrpContext, PtrIrpContext->Irp); 202 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC); 203 } _SEH2_END; 204 205 // Set the stack overflow item's event to tell the original 206 // thread that we're done. 207 KeSetEvent( Event, 0, FALSE ); 208 } // end UDFStackOverflowRead() 209 210 211 /************************************************************************* 212 * 213 * Function: UDFCommonRead() 214 * 215 * Description: 216 * The actual work is performed here. This routine may be invoked in one 217 * of the two possible contexts: 218 * (a) in the context of a system worker thread 219 * (b) in the context of the original caller 220 * 221 * Expected Interrupt Level (for execution) : 222 * 223 * IRQL_PASSIVE_LEVEL 224 * 225 * Return Value: STATUS_SUCCESS/Error 226 * 227 *************************************************************************/ 228 NTSTATUS 229 UDFCommonRead( 230 PtrUDFIrpContext PtrIrpContext, 231 PIRP Irp 232 ) 233 { 234 NTSTATUS RC = STATUS_SUCCESS; 235 PIO_STACK_LOCATION IrpSp = NULL; 236 LARGE_INTEGER ByteOffset; 237 ULONG ReadLength = 0, TruncatedLength = 0; 238 SIZE_T NumberBytesRead = 0; 239 PFILE_OBJECT FileObject = NULL; 240 PtrUDFFCB Fcb = NULL; 241 PtrUDFCCB Ccb = NULL; 242 PVCB Vcb = NULL; 243 PtrUDFNTRequiredFCB NtReqFcb = NULL; 244 PERESOURCE PtrResourceAcquired = NULL; 245 PERESOURCE PtrResourceAcquired2 = NULL; 246 PVOID SystemBuffer = NULL; 247 PIRP TopIrp; 248 // uint32 KeyValue = 0; 249 250 ULONG Res1Acq = 0; 251 ULONG Res2Acq = 0; 252 253 BOOLEAN CacheLocked = FALSE; 254 255 BOOLEAN CanWait = FALSE; 256 BOOLEAN PagingIo = FALSE; 257 BOOLEAN NonBufferedIo = FALSE; 258 BOOLEAN SynchronousIo = FALSE; 259 260 TmPrint(("UDFCommonRead: irp %x\n", Irp)); 261 262 _SEH2_TRY { 263 264 TopIrp = IoGetTopLevelIrp(); 265 switch((ULONG_PTR)TopIrp) { 266 case FSRTL_FSP_TOP_LEVEL_IRP: 267 UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n")); 268 break; 269 case FSRTL_CACHE_TOP_LEVEL_IRP: 270 UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n")); 271 break; 272 case FSRTL_MOD_WRITE_TOP_LEVEL_IRP: 273 UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n")); 274 // BrutePoint() 275 break; 276 case FSRTL_FAST_IO_TOP_LEVEL_IRP: 277 UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n")); 278 // BrutePoint() 279 break; 280 case NULL: 281 UDFPrint((" NULL TOP_LEVEL_IRP\n")); 282 break; 283 default: 284 if(TopIrp == Irp) { 285 UDFPrint((" TOP_LEVEL_IRP\n")); 286 } else { 287 UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp)); 288 } 289 break; 290 } 291 // First, get a pointer to the current I/O stack location 292 IrpSp = IoGetCurrentIrpStackLocation(Irp); 293 ASSERT(IrpSp); 294 MmPrint((" Enter Irp, MDL=%x\n", Irp->MdlAddress)); 295 if(Irp->MdlAddress) { 296 UDFTouch(Irp->MdlAddress); 297 } 298 299 // If this happens to be a MDL read complete request, then 300 // there is not much processing that the FSD has to do. 301 if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { 302 // Caller wants to tell the Cache Manager that a previously 303 // allocated MDL can be freed. 304 UDFMdlComplete(PtrIrpContext, Irp, IrpSp, TRUE); 305 // The IRP has been completed. 306 try_return(RC = STATUS_SUCCESS); 307 } 308 309 // If this is a request at IRQL DISPATCH_LEVEL, then post 310 // the request (your FSD may choose to process it synchronously 311 // if you implement the support correctly; obviously you will be 312 // quite constrained in what you can do at such IRQL). 313 if (IrpSp->MinorFunction & IRP_MN_DPC) { 314 try_return(RC = STATUS_PENDING); 315 } 316 317 FileObject = IrpSp->FileObject; 318 ASSERT(FileObject); 319 320 // Get the FCB and CCB pointers 321 Ccb = (PtrUDFCCB)(FileObject->FsContext2); 322 ASSERT(Ccb); 323 Fcb = Ccb->Fcb; 324 ASSERT(Fcb); 325 Vcb = Fcb->Vcb; 326 327 if(Fcb->FCBFlags & UDF_FCB_DELETED) { 328 ASSERT(FALSE); 329 try_return(RC = STATUS_ACCESS_DENIED); 330 } 331 332 // check for stack overflow 333 if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) { 334 RC = UDFPostStackOverflowRead( PtrIrpContext, Irp, Fcb ); 335 try_return(RC); 336 } 337 338 // Disk based file systems might decide to verify the logical volume 339 // (if required and only if removable media are supported) at this time 340 // As soon as Tray is locked, we needn't call UDFVerifyVcb() 341 342 ByteOffset = IrpSp->Parameters.Read.ByteOffset; 343 344 CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; 345 PagingIo = (Irp->Flags & IRP_PAGING_IO) ? TRUE : FALSE; 346 NonBufferedIo = (Irp->Flags & IRP_NOCACHE) ? TRUE : FALSE; 347 SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO) ? TRUE : FALSE; 348 UDFPrint((" Flags: %s %s %s %s\n", 349 CanWait ? "W" : "w", PagingIo ? "Pg" : "pg", 350 NonBufferedIo ? "NBuf" : "buff", SynchronousIo ? "Snc" : "Asc")); 351 352 if(!NonBufferedIo && 353 (Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB)) { 354 if(UDFIsAStream(Fcb->FileInfo)) { 355 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, 356 FILE_NOTIFY_CHANGE_LAST_ACCESS, 357 FILE_ACTION_MODIFIED_STREAM); 358 } else { 359 UDFNotifyFullReportChange( Vcb, Fcb->FileInfo, 360 FILE_NOTIFY_CHANGE_LAST_ACCESS, 361 FILE_ACTION_MODIFIED); 362 } 363 } 364 365 // Get some of the parameters supplied to us 366 ReadLength = IrpSp->Parameters.Read.Length; 367 if (ReadLength == 0) { 368 // a 0 byte read can be immediately succeeded 369 try_return(RC); 370 } 371 UDFPrint((" ByteOffset = %I64x, ReadLength = %x\n", ByteOffset.QuadPart, ReadLength)); 372 373 // Is this a read of the volume itself ? 374 if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) { 375 // Yup, we need to send this on to the disk driver after 376 // validation of the offset and length. 377 Vcb = (PVCB)Fcb; 378 Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; 379 if(!CanWait) 380 try_return(RC = STATUS_PENDING); 381 382 383 if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) { 384 385 UDFPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n")); 386 PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED; 387 388 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) { 389 UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); 390 } 391 #ifdef UDF_DELAYED_CLOSE 392 UDFCloseAllDelayed(Vcb); 393 #endif //UDF_DELAYED_CLOSE 394 395 } 396 397 if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH_REQUIRED) { 398 399 UDFPrint((" UDF_IRP_CONTEXT_FLUSH_REQUIRED\n")); 400 PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH_REQUIRED; 401 402 // Acquire the volume resource exclusive 403 UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); 404 PtrResourceAcquired = &(Vcb->VCBResource); 405 406 UDFFlushLogicalVolume(NULL, NULL, Vcb, 0); 407 408 UDFReleaseResource(PtrResourceAcquired); 409 PtrResourceAcquired = NULL; 410 } 411 412 // Acquire the volume resource shared ... 413 UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); 414 PtrResourceAcquired = &(Vcb->VCBResource); 415 416 #if 0 417 if(PagingIo) { 418 CollectStatistics(Vcb, MetaDataReads); 419 CollectStatisticsEx(Vcb, MetaDataReadBytes, NumberBytesRead); 420 } 421 #endif 422 423 // Forward the request to the lower level driver 424 // Lock the callers buffer 425 if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, ReadLength))) { 426 try_return(RC); 427 } 428 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); 429 if(!SystemBuffer) { 430 try_return(RC = STATUS_INVALID_USER_BUFFER); 431 } 432 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) { 433 RC = UDFReadData(Vcb, TRUE, ByteOffset.QuadPart, 434 ReadLength, FALSE, (PCHAR)SystemBuffer, 435 &NumberBytesRead); 436 } else { 437 RC = UDFTRead(Vcb, SystemBuffer, ReadLength, 438 (ULONG)(ByteOffset.QuadPart >> Vcb->BlockSizeBits), 439 &NumberBytesRead); 440 } 441 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); 442 try_return(RC); 443 } 444 Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; 445 446 // If the read request is directed to a page file (if your FSD 447 // supports paging files), send the request directly to the disk 448 // driver. For requests directed to a page file, you have to trust 449 // that the offsets will be set correctly by the VMM. You should not 450 // attempt to acquire any FSD resources either. 451 if(Fcb->FCBFlags & UDF_FCB_PAGE_FILE) { 452 NonBufferedIo = TRUE; 453 } 454 455 if(ByteOffset.HighPart == -1) { 456 if(ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) { 457 ByteOffset = FileObject->CurrentByteOffset; 458 } 459 } 460 461 // If this read is directed to a directory, it is not allowed 462 // by the UDF FSD. 463 if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) { 464 RC = STATUS_INVALID_DEVICE_REQUEST; 465 try_return(RC); 466 } 467 468 NtReqFcb = Fcb->NTRequiredFCB; 469 470 Res1Acq = UDFIsResourceAcquired(&(NtReqFcb->MainResource)); 471 if(!Res1Acq) { 472 Res1Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES1_ACQ; 473 } 474 Res2Acq = UDFIsResourceAcquired(&(NtReqFcb->PagingIoResource)); 475 if(!Res2Acq) { 476 Res2Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES2_ACQ; 477 } 478 479 #if 0 480 if(PagingIo) { 481 CollectStatistics(Vcb, UserFileReads); 482 CollectStatisticsEx(Vcb, UserFileReadBytes, NumberBytesRead); 483 } 484 #endif 485 486 // This is a good place for oplock related processing. 487 488 // If this is the normal file we have to check for 489 // write access according to the current state of the file locks. 490 if (!PagingIo && 491 !FsRtlCheckLockForReadAccess( &(NtReqFcb->FileLock), Irp )) { 492 try_return( RC = STATUS_FILE_LOCK_CONFLICT ); 493 } 494 495 // Validate start offset and length supplied. 496 // If start offset is > end-of-file, return an appropriate error. Note 497 // that since a FCB resource has already been acquired, and since all 498 // file size changes require acquisition of both FCB resources, 499 // the contents of the FCB and associated data structures 500 // can safely be examined. 501 502 // Also note that we are using the file size in the "Common FCB Header" 503 // to perform the check. However, your FSD might decide to keep a 504 // separate copy in the FCB (or some other representation of the 505 // file associated with the FCB). 506 507 TruncatedLength = ReadLength; 508 if (ByteOffset.QuadPart >= NtReqFcb->CommonFCBHeader.FileSize.QuadPart) { 509 // Starting offset is >= file size 510 try_return(RC = STATUS_END_OF_FILE); 511 } 512 // We can also go ahead and truncate the read length here 513 // such that it is contained within the file size 514 if( NtReqFcb->CommonFCBHeader.FileSize.QuadPart < (ByteOffset.QuadPart + ReadLength) ) { 515 TruncatedLength = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart); 516 // we can't get ZERO here 517 } 518 UDFPrint((" TruncatedLength = %x\n", TruncatedLength)); 519 520 // There are certain complications that arise when the same file stream 521 // has been opened for cached and non-cached access. The FSD is then 522 // responsible for maintaining a consistent view of the data seen by 523 // the caller. 524 // Also, it is possible for file streams to be mapped in both as data files 525 // and as an executable. This could also lead to consistency problems since 526 // there now exist two separate sections (and pages) containing file 527 // information. 528 529 // The test below flushes the data cached in system memory if the current 530 // request madates non-cached access (file stream must be cached) and 531 // (a) the current request is not paging-io which indicates it is not 532 // a recursive I/O operation OR originating in the Cache Manager 533 // (b) OR the current request is paging-io BUT it did not originate via 534 // the Cache Manager (or is a recursive I/O operation) and we do 535 // have an image section that has been initialized. 536 #define UDF_REQ_NOT_VIA_CACHE_MGR(ptr) (!MmIsRecursiveIoFault() && ((ptr)->ImageSectionObject != NULL)) 537 538 if(NonBufferedIo && 539 (NtReqFcb->SectionObject.DataSectionObject != NULL)) { 540 if(!PagingIo) { 541 542 /* // We hold the main resource exclusive here because the flush 543 // may generate a recursive write in this thread. The PagingIo 544 // resource is held shared so the drop-and-release serialization 545 // below will work. 546 if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) { 547 try_return(RC = STATUS_PENDING); 548 } 549 PtrResourceAcquired = &(NtReqFcb->MainResource); 550 551 // We hold PagingIo shared around the flush to fix a 552 // cache coherency problem. 553 UDFAcquireResourceShared(&(NtReqFcb->PagingIoResource), TRUE );*/ 554 555 MmPrint((" CcFlushCache()\n")); 556 CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, TruncatedLength, &(Irp->IoStatus)); 557 558 /* UDFReleaseResource(&(NtReqFcb->PagingIoResource)); 559 UDFReleaseResource(PtrResourceAcquired); 560 PtrResourceAcquired = NULL; 561 // If the flush failed, return error to the caller 562 if(!NT_SUCCESS(RC = Irp->IoStatus.Status)) { 563 try_return(RC); 564 } 565 566 // Acquiring and immediately dropping the resource serializes 567 // us behind any other writes taking place (either from the 568 // lazy writer or modified page writer).*/ 569 if(!Res2Acq) { 570 UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE ); 571 UDFReleaseResource(&(NtReqFcb->PagingIoResource)); 572 } 573 } 574 } 575 576 // Acquire the appropriate FCB resource shared 577 if (PagingIo) { 578 // Try to acquire the FCB PagingIoResource shared 579 if(!Res2Acq) { 580 if (!UDFAcquireResourceShared(&(NtReqFcb->PagingIoResource), CanWait)) { 581 try_return(RC = STATUS_PENDING); 582 } 583 // Remember the resource that was acquired 584 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); 585 } 586 } else { 587 // Try to acquire the FCB MainResource shared 588 if(NonBufferedIo) { 589 if(!Res2Acq) { 590 if(!UDFAcquireSharedWaitForExclusive(&(NtReqFcb->PagingIoResource), CanWait)) { 591 try_return(RC = STATUS_PENDING); 592 } 593 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); 594 } 595 } else { 596 if(!Res1Acq) { 597 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 598 if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) { 599 try_return(RC = STATUS_PENDING); 600 } 601 // Remember the resource that was acquired 602 PtrResourceAcquired = &(NtReqFcb->MainResource); 603 } 604 } 605 } 606 607 // This is also a good place to set whether fast-io can be performed 608 // on this particular file or not. Your FSD must make it's own 609 // determination on whether or not to allow fast-io operations. 610 // Commonly, fast-io is not allowed if any byte range locks exist 611 // on the file or if oplocks prevent fast-io. Practically any reason 612 // choosen by your FSD could result in your setting FastIoIsNotPossible 613 // OR FastIoIsQuestionable instead of FastIoIsPossible. 614 615 NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb); 616 /* if(NtReqFcb->CommonFCBHeader.IsFastIoPossible == FastIoIsPossible) 617 NtReqFcb->CommonFCBHeader.IsFastIoPossible = FastIoIsQuestionable;*/ 618 619 #ifdef UDF_DISABLE_SYSTEM_CACHE_MANAGER 620 NonBufferedIo = TRUE; 621 #endif 622 623 if(Fcb && Fcb->FileInfo && Fcb->FileInfo->Dloc) { 624 AdPrint(("UDFCommonRead: DataLoc %x, Mapping %x\n", &Fcb->FileInfo->Dloc->DataLoc, Fcb->FileInfo->Dloc->DataLoc.Mapping)); 625 } 626 627 // Branch here for cached vs non-cached I/O 628 if (!NonBufferedIo) { 629 630 if(FileObject->Flags & FO_WRITE_THROUGH) { 631 CanWait = TRUE; 632 } 633 // The caller wishes to perform cached I/O. Initiate caching if 634 // this is the first cached I/O operation using this file object 635 if (!(FileObject->PrivateCacheMap)) { 636 // This is the first cached I/O operation. You must ensure 637 // that the FCB Common FCB Header contains valid sizes at this time 638 MmPrint((" CcInitializeCacheMap()\n")); 639 CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&(NtReqFcb->CommonFCBHeader.AllocationSize)), 640 FALSE, // We will not utilize pin access for this file 641 &(UDFGlobalData.CacheMgrCallBacks), // callbacks 642 NtReqFcb); // The context used in callbacks 643 MmPrint((" CcSetReadAheadGranularity()\n")); 644 CcSetReadAheadGranularity(FileObject, Vcb->SystemCacheGran); 645 } 646 647 // Check and see if this request requires a MDL returned to the caller 648 if (IrpSp->MinorFunction & IRP_MN_MDL) { 649 // Caller does want a MDL returned. Note that this mode 650 // implies that the caller is prepared to block 651 MmPrint((" CcMdlRead()\n")); 652 // CcMdlRead(FileObject, &ByteOffset, TruncatedLength, &(Irp->MdlAddress), &(Irp->IoStatus)); 653 // NumberBytesRead = Irp->IoStatus.Information; 654 // RC = Irp->IoStatus.Status; 655 NumberBytesRead = 0; 656 RC = STATUS_INVALID_PARAMETER; 657 658 try_return(RC); 659 } 660 661 // This is a regular run-of-the-mill cached I/O request. Let the 662 // Cache Manager worry about it! 663 // First though, we need a buffer pointer (address) that is valid 664 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); 665 if(!SystemBuffer) 666 try_return(RC = STATUS_INVALID_USER_BUFFER); 667 ASSERT(SystemBuffer); 668 MmPrint((" CcCopyRead()\n")); 669 if (!CcCopyRead(FileObject, &(ByteOffset), TruncatedLength, CanWait, SystemBuffer, &(Irp->IoStatus))) { 670 // The caller was not prepared to block and data is not immediately 671 // available in the system cache 672 try_return(RC = STATUS_PENDING); 673 } 674 675 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); 676 // We have the data 677 RC = Irp->IoStatus.Status; 678 NumberBytesRead = Irp->IoStatus.Information; 679 680 try_return(RC); 681 682 } else { 683 684 MmPrint((" Read NonBufferedIo\n")); 685 686 #if 1 687 if((ULONG_PTR)TopIrp == FSRTL_MOD_WRITE_TOP_LEVEL_IRP) { 688 UDFPrint(("FSRTL_MOD_WRITE_TOP_LEVEL_IRP => CanWait\n")); 689 CanWait = TRUE; 690 } else 691 if((ULONG_PTR)TopIrp == FSRTL_CACHE_TOP_LEVEL_IRP) { 692 UDFPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n")); 693 CanWait = TRUE; 694 } 695 696 if(NtReqFcb->AcqSectionCount || NtReqFcb->AcqFlushCount) { 697 MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb->AcqSectionCount, NtReqFcb->AcqFlushCount)); 698 CanWait = TRUE; 699 } else 700 {} 701 /* if((TopIrp != Irp)) { 702 UDFPrint(("(TopIrp != Irp) => CanWait\n")); 703 CanWait = TRUE; 704 } else*/ 705 #endif 706 if(KeGetCurrentIrql() > PASSIVE_LEVEL) { 707 MmPrint((" !PASSIVE_LEVEL\n")); 708 CanWait = FALSE; 709 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FORCED_POST; 710 } 711 if(!CanWait && UDFIsFileCached__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, FALSE)) { 712 MmPrint((" Locked => CanWait\n")); 713 CacheLocked = TRUE; 714 CanWait = TRUE; 715 } 716 717 // Send the request to lower level drivers 718 if(!CanWait) { 719 try_return(RC = STATUS_PENDING); 720 } 721 722 // ASSERT(NT_SUCCESS(RC)); 723 if(!Res2Acq) { 724 if(UDFAcquireResourceSharedWithCheck(&(NtReqFcb->PagingIoResource))) 725 PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource); 726 } 727 728 RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, TruncatedLength); 729 if(!NT_SUCCESS(RC)) { 730 try_return(RC); 731 } 732 733 SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp); 734 if(!SystemBuffer) { 735 try_return(RC = STATUS_INVALID_USER_BUFFER); 736 } 737 738 RC = UDFReadFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, 739 CacheLocked, (PCHAR)SystemBuffer, &NumberBytesRead); 740 /* // AFAIU, CacheManager wants this: 741 if(!NT_SUCCESS(RC)) { 742 NumberBytesRead = 0; 743 }*/ 744 745 UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer); 746 747 #if 0 748 if(PagingIo) { 749 CollectStatistics(Vcb, UserDiskReads); 750 } else { 751 CollectStatistics2(Vcb, NonCachedDiskReads); 752 } 753 #endif 754 755 try_return(RC); 756 757 // For paging-io, the FSD has to trust the VMM to do the right thing 758 759 // Here is a common method used by Windows NT native file systems 760 // that are in the process of sending a request to the disk driver. 761 // First, mark the IRP as pending, then invoke the lower level driver 762 // after setting a completion routine. 763 // Meanwhile, this particular thread can immediately return a 764 // STATUS_PENDING return code. 765 // The completion routine is then responsible for completing the IRP 766 // and unlocking appropriate resources 767 768 // Also, at this point, the FSD might choose to utilize the 769 // information contained in the ValidDataLength field to simply 770 // return zeroes to the caller for reads extending beyond current 771 // valid data length. 772 773 } 774 775 try_exit: NOTHING; 776 777 } _SEH2_FINALLY { 778 779 if(CacheLocked) { 780 WCacheEODirect__(&(Vcb->FastCache), Vcb); 781 } 782 783 // Release any resources acquired here ... 784 if(PtrResourceAcquired2) { 785 UDFReleaseResource(PtrResourceAcquired2); 786 } 787 if(PtrResourceAcquired) { 788 if(NtReqFcb && 789 (PtrResourceAcquired == 790 &(NtReqFcb->MainResource))) { 791 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 792 } 793 UDFReleaseResource(PtrResourceAcquired); 794 } 795 796 // Post IRP if required 797 if(RC == STATUS_PENDING) { 798 799 // Lock the callers buffer here. Then invoke a common routine to 800 // perform the post operation. 801 if (!(IrpSp->MinorFunction & IRP_MN_MDL)) { 802 RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, ReadLength); 803 ASSERT(NT_SUCCESS(RC)); 804 } 805 if(PagingIo) { 806 if(Res1Acq) { 807 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES1_ACQ; 808 } 809 if(Res2Acq) { 810 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES2_ACQ; 811 } 812 } 813 // Perform the post operation which will mark the IRP pending 814 // and will return STATUS_PENDING back to us 815 RC = UDFPostRequest(PtrIrpContext, Irp); 816 817 } else { 818 // For synchronous I/O, the FSD must maintain the current byte offset 819 // Do not do this however, if I/O is marked as paging-io 820 if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) { 821 FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + NumberBytesRead; 822 } 823 // If the read completed successfully and this was not a paging-io 824 // operation, set a flag in the CCB that indicates that a read was 825 // performed and that the file time should be updated at cleanup 826 if (NT_SUCCESS(RC) && !PagingIo) { 827 FileObject->Flags |= FO_FILE_FAST_IO_READ; 828 Ccb->CCBFlags |= UDF_CCB_ACCESSED; 829 } 830 831 if(!_SEH2_AbnormalTermination()) { 832 Irp->IoStatus.Status = RC; 833 Irp->IoStatus.Information = NumberBytesRead; 834 UDFPrint((" NumberBytesRead = %x\n", NumberBytesRead)); 835 // Free up the Irp Context 836 UDFReleaseIrpContext(PtrIrpContext); 837 // complete the IRP 838 MmPrint((" Complete Irp, MDL=%x\n", Irp->MdlAddress)); 839 if(Irp->MdlAddress) { 840 UDFTouch(Irp->MdlAddress); 841 } 842 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 843 } 844 } // can we complete the IRP ? 845 } _SEH2_END; // end of "__finally" processing 846 847 return(RC); 848 } // end UDFCommonRead() 849 850 851 #ifdef UDF_DBG 852 ULONG LockBufferCounter = 0; 853 ULONG BuildMdlCounter = 0; 854 #endif //UDF_DBG 855 856 /************************************************************************* 857 * 858 * Function: UDFGetCallersBuffer() 859 * 860 * Description: 861 * Obtain a pointer to the caller's buffer. 862 * 863 * Expected Interrupt Level (for execution) : 864 * 865 * IRQL_PASSIVE_LEVEL 866 * 867 * Return Value: STATUS_SUCCESS/Error 868 * 869 *************************************************************************/ 870 PVOID 871 UDFGetCallersBuffer( 872 PtrUDFIrpContext PtrIrpContext, 873 PIRP Irp 874 ) 875 { 876 VOID *ReturnedBuffer = NULL; 877 878 UDFPrint(("UDFGetCallersBuffer: \n")); 879 880 // If an MDL is supplied, use it. 881 if(Irp->MdlAddress) { 882 MmPrint((" UDFGetCallersBuffer: MmGetSystemAddressForMdl(Irp->MdlAddress) MDL=%x\n", Irp->MdlAddress)); 883 // ReturnedBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 884 ReturnedBuffer = MmGetSystemAddressForMdlSafer(Irp->MdlAddress); 885 } else 886 if (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_BUFFER_LOCKED) { 887 // Free buffer 888 #ifndef POST_LOCK_PAGES 889 MmPrint((" UDFGetCallersBuffer: MmGetSystemAddressForMdl(PtrIrpContext->PtrMdl) MDL=%x\n", PtrIrpContext->PtrMdl)); 890 ReturnedBuffer = MmGetSystemAddressForMdlSafe(PtrIrpContext->PtrMdl, NormalPagePriority); 891 #else //POST_LOCK_PAGES 892 if(PtrIrpContext->TransitionBuffer) { 893 MmPrint((" UDFGetCallersBuffer: TransitionBuffer\n")); 894 return PtrIrpContext->TransitionBuffer; 895 } 896 897 _SEH2_TRY { 898 MmPrint((" MmProbeAndLockPages()\n")); 899 MmProbeAndLockPages(PtrIrpContext->PtrMdl, Irp->RequestorMode, 900 ((PtrIrpContext->MajorFunction == IRP_MJ_READ) ? IoWriteAccess:IoReadAccess)); 901 #ifdef UDF_DBG 902 LockBufferCounter++; 903 #endif //UDF_DBG 904 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 905 //RC = STATUS_INVALID_USER_BUFFER; 906 BrutePoint(); 907 return NULL; 908 } _SEH2_END; 909 910 MmPrint((" MmGetSystemAddressForMdlSafer()\n")); 911 ReturnedBuffer = MmGetSystemAddressForMdlSafer(PtrIrpContext->PtrMdl); 912 #endif //POST_LOCK_PAGES 913 } else { 914 MmPrint((" UDFGetCallersBuffer: Irp->UserBuffer\n")); 915 ReturnedBuffer = Irp->UserBuffer; 916 } 917 918 return(ReturnedBuffer); 919 } // end UDFGetCallersBuffer() 920 921 /************************************************************************* 922 * 923 * Function: UDFLockCallersBuffer() 924 * 925 * Description: 926 * Obtain a MDL that describes the buffer. Lock pages for I/O 927 * 928 * Expected Interrupt Level (for execution) : 929 * 930 * IRQL_PASSIVE_LEVEL 931 * 932 * Return Value: STATUS_SUCCESS/Error 933 * 934 *************************************************************************/ 935 NTSTATUS 936 UDFLockCallersBuffer( 937 PtrUDFIrpContext PtrIrpContext, 938 PIRP Irp, 939 BOOLEAN IsReadOperation, 940 uint32 Length 941 ) 942 { 943 NTSTATUS RC = STATUS_SUCCESS; 944 PMDL PtrMdl = NULL; 945 946 UDFPrint(("UDFLockCallersBuffer: \n")); 947 948 ASSERT(Irp); 949 950 _SEH2_TRY { 951 // Is a MDL already present in the IRP 952 if (!(Irp->MdlAddress)) { 953 // Allocate a MDL 954 /* 955 if(!IsReadOperation) { 956 MmPrint((" Allocate TransitionBuffer\n")); 957 PtrIrpContext->TransitionBuffer = (PCHAR)DbgAllocatePool(NonPagedPool, Length); 958 if(!PtrIrpContext->TransitionBuffer) { 959 RC = STATUS_INSUFFICIENT_RESOURCES; 960 try_return(RC); 961 } 962 _SEH2_TRY { 963 RtlCopyMemory(PtrIrpContext->TransitionBuffer, Irp->UserBuffer, Length); 964 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 965 RC = STATUS_INVALID_USER_BUFFER; 966 } _SEH2_END; 967 } else*/ { 968 969 MmPrint((" IoAllocateMdl()\n")); 970 // if (!(PtrMdl = IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, NULL))) { 971 972 // This will place allocated Mdl to Irp 973 if (!(PtrMdl = IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, Irp))) { 974 RC = STATUS_INSUFFICIENT_RESOURCES; 975 try_return(RC); 976 } 977 MmPrint((" Alloc MDL=%x\n", PtrMdl)); 978 #ifdef UDF_DBG 979 BuildMdlCounter++; 980 #endif //UDF_DBG 981 } 982 // Probe and lock the pages described by the MDL 983 // We could encounter an exception doing so, swallow the exception 984 // NOTE: The exception could be due to an unexpected (from our 985 // perspective), invalidation of the virtual addresses that comprise 986 // the passed in buffer 987 #ifndef POST_LOCK_PAGES 988 _SEH2_TRY { 989 MmPrint((" MmProbeAndLockPages()\n")); 990 MmProbeAndLockPages(PtrMdl, Irp->RequestorMode, (IsReadOperation ? IoWriteAccess:IoReadAccess)); 991 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 992 MmPrint((" MmProbeAndLockPages() failed\n")); 993 Irp->MdlAddress = NULL; 994 RC = STATUS_INVALID_USER_BUFFER; 995 } _SEH2_END; 996 #endif //POST_LOCK_PAGES 997 998 if(NT_SUCCESS(RC)) { 999 PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_BUFFER_LOCKED; 1000 PtrIrpContext->PtrMdl = PtrMdl; 1001 } 1002 } else { 1003 MmPrint((" UDFLockCallersBuffer: do nothing, MDL=%x\n", Irp->MdlAddress)); 1004 UDFTouch(Irp->MdlAddress); 1005 } 1006 1007 try_exit: NOTHING; 1008 1009 } _SEH2_FINALLY { 1010 if (!NT_SUCCESS(RC) && PtrMdl) { 1011 MmPrint((" Free MDL=%x\n", PtrMdl)); 1012 IoFreeMdl(PtrMdl); 1013 } 1014 } _SEH2_END; 1015 1016 return(RC); 1017 } // end UDFLockCallersBuffer() 1018 1019 /************************************************************************* 1020 * 1021 * Function: UDFUnlockCallersBuffer() 1022 * 1023 * Description: 1024 * Obtain a MDL that describes the buffer. Lock pages for I/O 1025 * 1026 * Expected Interrupt Level (for execution) : 1027 * 1028 * IRQL_PASSIVE_LEVEL 1029 * 1030 * Return Value: STATUS_SUCCESS/Error 1031 * 1032 *************************************************************************/ 1033 NTSTATUS 1034 UDFUnlockCallersBuffer( 1035 PtrUDFIrpContext PtrIrpContext, 1036 PIRP Irp, 1037 PVOID SystemBuffer 1038 ) 1039 { 1040 NTSTATUS RC = STATUS_SUCCESS; 1041 1042 UDFPrint(("UDFUnlockCallersBuffer: \n")); 1043 1044 ASSERT(Irp); 1045 1046 _SEH2_TRY { 1047 // Is a nonPaged buffer already present in the IRP 1048 if (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_BUFFER_LOCKED) { 1049 1050 UDFPrint((" UDF_IRP_CONTEXT_BUFFER_LOCKED MDL=%x, Irp MDL=%x\n", PtrIrpContext->PtrMdl, Irp->MdlAddress)); 1051 if(PtrIrpContext->TransitionBuffer) { 1052 MmPrint((" UDFUnlockCallersBuffer: free TransitionBuffer\n")); 1053 DbgFreePool(PtrIrpContext->TransitionBuffer); 1054 PtrIrpContext->TransitionBuffer = NULL; 1055 PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_BUFFER_LOCKED; 1056 try_return(RC); 1057 } 1058 // Free buffer 1059 KeFlushIoBuffers( PtrIrpContext->PtrMdl, TRUE, FALSE ); 1060 // MmPrint((" IrpCtx->Mdl, MmUnmapLockedPages()\n")); 1061 // MmUnmapLockedPages(SystemBuffer, PtrIrpContext->PtrMdl); 1062 1063 // This will be done in IoCompleteIrp !!! 1064 1065 //MmPrint((" MmUnlockPages()\n")); 1066 //MmUnlockPages(PtrIrpContext->PtrMdl); 1067 1068 #ifdef UDF_DBG 1069 LockBufferCounter--; 1070 #endif //UDF_DBG 1071 1072 // This will be done in IoCompleteIrp !!! 1073 1074 //IoFreeMdl(PtrIrpContext->PtrMdl); 1075 1076 #ifdef UDF_DBG 1077 BuildMdlCounter--; 1078 #endif //UDF_DBG 1079 UDFTouch(PtrIrpContext->PtrMdl); 1080 PtrIrpContext->PtrMdl = NULL; 1081 PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_BUFFER_LOCKED; 1082 } else 1083 if(Irp->MdlAddress) { 1084 // MmPrint((" Irp->Mdl, MmUnmapLockedPages()\n")); 1085 // MmUnmapLockedPages(SystemBuffer, Irp->MdlAddress); 1086 UDFPrint((" UDF_IRP_CONTEXT_BUFFER_LOCKED MDL=%x, Irp MDL=%x\n", PtrIrpContext->PtrMdl, Irp->MdlAddress)); 1087 UDFTouch(Irp->MdlAddress); 1088 KeFlushIoBuffers( Irp->MdlAddress, 1089 ((IoGetCurrentIrpStackLocation(Irp))->MajorFunction) == IRP_MJ_READ, 1090 FALSE ); 1091 } else 1092 { ; } 1093 1094 try_exit: NOTHING; 1095 1096 } _SEH2_FINALLY { 1097 NOTHING; 1098 } _SEH2_END; 1099 1100 return(RC); 1101 } // end UDFUnlockCallersBuffer() 1102 1103 /************************************************************************* 1104 * 1105 * Function: UDFMdlComplete() 1106 * 1107 * Description: 1108 * Tell Cache Manager to release MDL (and possibly flush). 1109 * 1110 * Expected Interrupt Level (for execution) : 1111 * 1112 * IRQL_PASSIVE_LEVEL 1113 * 1114 * Return Value: None. 1115 * 1116 *************************************************************************/ 1117 VOID UDFMdlComplete( 1118 PtrUDFIrpContext PtrIrpContext, 1119 PIRP Irp, 1120 PIO_STACK_LOCATION IrpSp, 1121 BOOLEAN ReadCompletion) 1122 { 1123 NTSTATUS RC = STATUS_SUCCESS; 1124 PFILE_OBJECT FileObject = NULL; 1125 1126 UDFPrint(("UDFMdlComplete: \n")); 1127 1128 FileObject = IrpSp->FileObject; 1129 ASSERT(FileObject); 1130 1131 UDFTouch(Irp->MdlAddress); 1132 // Not much to do here. 1133 if (ReadCompletion) { 1134 MmPrint((" CcMdlReadComplete() MDL=%x\n", Irp->MdlAddress)); 1135 CcMdlReadComplete(FileObject, Irp->MdlAddress); 1136 } else { 1137 // The Cache Manager needs the byte offset in the I/O stack location. 1138 MmPrint((" CcMdlWriteComplete() MDL=%x\n", Irp->MdlAddress)); 1139 CcMdlWriteComplete(FileObject, &(IrpSp->Parameters.Write.ByteOffset), Irp->MdlAddress); 1140 } 1141 1142 // Clear the MDL address field in the IRP so the IoCompleteRequest() 1143 // does not __try to play around with the MDL. 1144 Irp->MdlAddress = NULL; 1145 1146 // Free up the Irp Context. 1147 UDFReleaseIrpContext(PtrIrpContext); 1148 1149 // Complete the IRP. 1150 Irp->IoStatus.Status = RC; 1151 Irp->IoStatus.Information = 0; 1152 IoCompleteRequest(Irp, IO_NO_INCREMENT); 1153 1154 return; 1155 } 1156