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: Flush.cpp 9 * 10 * Module: UDF File System Driver (Kernel mode execution only) 11 * 12 * Description: 13 * Contains code to handle the "Flush Buffers" 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_FLUSH 21 22 23 24 /************************************************************************* 25 * 26 * Function: UDFFlush() 27 * 28 * Description: 29 * The I/O Manager will invoke this routine to handle a flush buffers 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 UDFFlush( 43 PDEVICE_OBJECT DeviceObject, // the logical volume device object 44 PIRP Irp) // I/O Request Packet 45 { 46 NTSTATUS RC = STATUS_SUCCESS; 47 PtrUDFIrpContext PtrIrpContext = NULL; 48 BOOLEAN AreWeTopLevel = FALSE; 49 50 UDFPrint(("UDFFlush: \n")); 51 52 FsRtlEnterFileSystem(); 53 ASSERT(DeviceObject); 54 ASSERT(Irp); 55 56 // set the top level context 57 AreWeTopLevel = UDFIsIrpTopLevel(Irp); 58 ASSERT(!UDFIsFSDevObj(DeviceObject)); 59 60 _SEH2_TRY { 61 62 // get an IRP context structure and issue the request 63 PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject); 64 if(PtrIrpContext) { 65 RC = UDFCommonFlush(PtrIrpContext, Irp); 66 } else { 67 RC = STATUS_INSUFFICIENT_RESOURCES; 68 Irp->IoStatus.Status = RC; 69 Irp->IoStatus.Information = 0; 70 // complete the IRP 71 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 72 } 73 74 } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) { 75 76 RC = UDFExceptionHandler(PtrIrpContext, Irp); 77 78 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC); 79 } _SEH2_END; 80 81 if (AreWeTopLevel) { 82 IoSetTopLevelIrp(NULL); 83 } 84 85 FsRtlExitFileSystem(); 86 87 return(RC); 88 } // end UDFFlush() 89 90 91 92 /************************************************************************* 93 * 94 * Function: UDFCommonFlush() 95 * 96 * Description: 97 * The actual work is performed here. This routine may be invoked in one' 98 * of the two possible contexts: 99 * (a) in the context of a system worker thread 100 * (b) in the context of the original caller 101 * 102 * Expected Interrupt Level (for execution) : 103 * 104 * IRQL_PASSIVE_LEVEL 105 * 106 * Return Value: STATUS_SUCCESS/Error 107 * 108 *************************************************************************/ 109 NTSTATUS 110 UDFCommonFlush( 111 PtrUDFIrpContext PtrIrpContext, 112 PIRP Irp 113 ) 114 { 115 NTSTATUS RC = STATUS_SUCCESS; 116 PIO_STACK_LOCATION IrpSp = NULL; 117 PFILE_OBJECT FileObject = NULL; 118 PtrUDFFCB Fcb = NULL; 119 PtrUDFCCB Ccb = NULL; 120 PVCB Vcb = NULL; 121 PtrUDFNTRequiredFCB NtReqFcb = NULL; 122 BOOLEAN AcquiredVCB = FALSE; 123 BOOLEAN AcquiredFCB = FALSE; 124 BOOLEAN PostRequest = FALSE; 125 BOOLEAN CanWait = TRUE; 126 127 UDFPrint(("UDFCommonFlush: \n")); 128 129 _SEH2_TRY { 130 131 // Get some of the parameters supplied to us 132 CanWait = ((PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE); 133 // If we cannot wait, post the request immediately since a flush is inherently blocking/synchronous. 134 if (!CanWait) { 135 PostRequest = TRUE; 136 try_return(RC); 137 } 138 139 // First, get a pointer to the current I/O stack location 140 IrpSp = IoGetCurrentIrpStackLocation(Irp); 141 ASSERT(IrpSp); 142 143 FileObject = IrpSp->FileObject; 144 ASSERT(FileObject); 145 146 // Get the FCB and CCB pointers 147 Ccb = (PtrUDFCCB)(FileObject->FsContext2); 148 ASSERT(Ccb); 149 Fcb = Ccb->Fcb; 150 ASSERT(Fcb); 151 NtReqFcb = Fcb->NTRequiredFCB; 152 153 // Check the type of object passed-in. That will determine the course of 154 // action we take. 155 if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || (Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)) { 156 157 if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) { 158 Vcb = (PVCB)(Fcb); 159 } else { 160 Vcb = Fcb->Vcb; 161 } 162 Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; 163 164 #ifdef UDF_DELAYED_CLOSE 165 UDFCloseAllDelayed(Vcb); 166 #endif //UDF_DELAYED_CLOSE 167 168 UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); 169 AcquiredVCB = TRUE; 170 // The caller wishes to flush all files for the mounted 171 // logical volume. The flush volume routine below should simply 172 // walk through all of the open file streams, acquire the 173 // VCB resource, and request the flush operation from the Cache 174 // Manager. Basically, the sequence of operations listed below 175 // for a single file should be executed on all open files. 176 177 UDFFlushLogicalVolume(PtrIrpContext, Irp, Vcb, 0); 178 179 UDFReleaseResource(&(Vcb->VCBResource)); 180 AcquiredVCB = FALSE; 181 182 try_return(RC); 183 } else 184 if (!(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { 185 // This is a regular file. 186 Vcb = Fcb->Vcb; 187 ASSERT(Vcb); 188 if(!ExIsResourceAcquiredExclusiveLite(&(Vcb->VCBResource)) && 189 !ExIsResourceAcquiredSharedLite(&(Vcb->VCBResource))) { 190 UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); 191 AcquiredVCB = TRUE; 192 } 193 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 194 UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), TRUE); 195 AcquiredFCB = TRUE; 196 197 // Request the Cache Manager to perform a flush operation. 198 // Further, instruct the Cache Manager that we wish to flush the 199 // entire file stream. 200 UDFFlushAFile(Fcb, Ccb, &(Irp->IoStatus), 0); 201 RC = Irp->IoStatus.Status; 202 203 // Some log-based FSD implementations may wish to flush their 204 // log files at this time. Finally, we should update the time-stamp 205 // values for the file stream appropriately. This would involve 206 // obtaining the current time and modifying the appropriate directory 207 // entry fields. 208 } else { 209 Vcb = Fcb->Vcb; 210 } 211 212 try_exit: NOTHING; 213 214 } _SEH2_FINALLY { 215 216 if (AcquiredFCB) { 217 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); 218 UDFReleaseResource(&(NtReqFcb->MainResource)); 219 AcquiredFCB = FALSE; 220 } 221 if (AcquiredVCB) { 222 UDFReleaseResource(&(Vcb->VCBResource)); 223 AcquiredVCB = FALSE; 224 } 225 226 if(!_SEH2_AbnormalTermination()) { 227 if (PostRequest) { 228 // Nothing to lock now. 229 BrutePoint(); 230 RC = UDFPostRequest(PtrIrpContext, Irp); 231 } else { 232 // Some applications like this request very much 233 // (ex. WinWord). But it's not a good idea for CD-R/RW media 234 if(Vcb->FlushMedia) { 235 PIO_STACK_LOCATION PtrNextIoStackLocation = NULL; 236 NTSTATUS RC1 = STATUS_SUCCESS; 237 238 // Send the request down at this point. 239 // To do this, we must set the next IRP stack location, and 240 // maybe set a completion routine. 241 // Be careful about marking the IRP pending if the lower level 242 // driver returned pending and we do have a completion routine! 243 PtrNextIoStackLocation = IoGetNextIrpStackLocation(Irp); 244 *PtrNextIoStackLocation = *IrpSp; 245 246 // Set the completion routine to "eat-up" any 247 // STATUS_INVALID_DEVICE_REQUEST error code returned by the lower 248 // level driver. 249 IoSetCompletionRoutine(Irp, UDFFlushCompletion, NULL, TRUE, TRUE, TRUE); 250 251 RC1 = IoCallDriver(Vcb->TargetDeviceObject, Irp); 252 253 RC = ((RC1 == STATUS_INVALID_DEVICE_REQUEST) ? RC : RC1); 254 255 // Release the IRP context at this time. 256 UDFReleaseIrpContext(PtrIrpContext); 257 } else { 258 Irp->IoStatus.Status = RC; 259 Irp->IoStatus.Information = 0; 260 // Free up the Irp Context 261 UDFReleaseIrpContext(PtrIrpContext); 262 // complete the IRP 263 IoCompleteRequest(Irp, IO_DISK_INCREMENT); 264 } 265 } 266 } 267 } _SEH2_END; 268 269 return(RC); 270 } // end UDFCommonFlush() 271 272 273 /************************************************************************* 274 * 275 * Function: UDFFlushAFile() 276 * 277 * Description: 278 * Tell the Cache Manager to perform a flush. 279 * 280 * Expected Interrupt Level (for execution) : 281 * 282 * IRQL_PASSIVE_LEVEL 283 * 284 * Return Value: None 285 * 286 *************************************************************************/ 287 ULONG 288 UDFFlushAFile( 289 IN PtrUDFFCB Fcb, 290 IN PtrUDFCCB Ccb, 291 OUT PIO_STATUS_BLOCK PtrIoStatus, 292 IN ULONG FlushFlags 293 ) 294 { 295 BOOLEAN SetArchive = FALSE; 296 // BOOLEAN PurgeCache = FALSE; 297 ULONG ret_val = 0; 298 299 UDFPrint(("UDFFlushAFile: \n")); 300 if(!Fcb) 301 return 0; 302 303 _SEH2_TRY { 304 if(Fcb->Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) 305 return 0; 306 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 307 BrutePoint(); 308 } _SEH2_END; 309 #ifndef UDF_READ_ONLY_BUILD 310 // Flush Security if required 311 _SEH2_TRY { 312 UDFWriteSecurity(Fcb->Vcb, Fcb, &(Fcb->NTRequiredFCB->SecurityDesc)); 313 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 314 BrutePoint(); 315 } _SEH2_END; 316 #endif //UDF_READ_ONLY_BUILD 317 // Flush SDir if any 318 _SEH2_TRY { 319 if(UDFHasAStreamDir(Fcb->FileInfo) && 320 Fcb->FileInfo->Dloc->SDirInfo && 321 !UDFIsSDirDeleted(Fcb->FileInfo->Dloc->SDirInfo) ) { 322 ret_val |= 323 UDFFlushADirectory(Fcb->Vcb, Fcb->FileInfo->Dloc->SDirInfo, PtrIoStatus, FlushFlags); 324 } 325 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 326 BrutePoint(); 327 } _SEH2_END; 328 // Flush File 329 _SEH2_TRY { 330 if((Fcb->CachedOpenHandleCount || !Fcb->OpenHandleCount) && 331 Fcb->NTRequiredFCB->SectionObject.DataSectionObject) { 332 if(!(Fcb->NTRequiredFCB->NtReqFCBFlags & UDF_NTREQ_FCB_DELETED) 333 && 334 ((Fcb->NTRequiredFCB->NtReqFCBFlags & UDF_NTREQ_FCB_MODIFIED) || 335 (Ccb && !(Ccb->CCBFlags & UDF_CCB_FLUSHED)) )) { 336 MmPrint((" CcFlushCache()\n")); 337 CcFlushCache(&(Fcb->NTRequiredFCB->SectionObject), NULL, 0, PtrIoStatus); 338 } 339 // notice, that we should purge cache 340 // we can't do it now, because it may cause last Close 341 // request & thus, structure deallocation 342 // PurgeCache = TRUE; 343 344 #ifndef UDF_READ_ONLY_BUILD 345 if(Ccb) { 346 if( (Ccb->FileObject->Flags & FO_FILE_MODIFIED) && 347 !(Ccb->CCBFlags & UDF_CCB_WRITE_TIME_SET)) { 348 if(Fcb->Vcb->CompatFlags & UDF_VCB_IC_UPDATE_MODIFY_TIME) { 349 LONGLONG NtTime; 350 KeQuerySystemTime((PLARGE_INTEGER)&NtTime); 351 UDFSetFileXTime(Fcb->FileInfo, NULL, NULL, NULL, &NtTime); 352 Fcb->NTRequiredFCB->LastWriteTime.QuadPart = NtTime; 353 } 354 SetArchive = TRUE; 355 Ccb->FileObject->Flags &= ~FO_FILE_MODIFIED; 356 } 357 if(Ccb->FileObject->Flags & FO_FILE_SIZE_CHANGED) { 358 LONGLONG ASize = UDFGetFileAllocationSize(Fcb->Vcb, Fcb->FileInfo); 359 UDFSetFileSizeInDirNdx(Fcb->Vcb, Fcb->FileInfo, &ASize); 360 Ccb->FileObject->Flags &= ~FO_FILE_SIZE_CHANGED; 361 } 362 } 363 #endif //UDF_READ_ONLY_BUILD 364 } 365 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 366 BrutePoint(); 367 } _SEH2_END; 368 369 _SEH2_TRY { 370 #ifndef UDF_READ_ONLY_BUILD 371 if(SetArchive && 372 (Fcb->Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT)) { 373 ULONG Attr; 374 PDIR_INDEX_ITEM DirNdx; 375 DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index); 376 // Archive bit 377 Attr = UDFAttributesToNT(DirNdx, Fcb->FileInfo->Dloc->FileEntry); 378 if(!(Attr & FILE_ATTRIBUTE_ARCHIVE)) 379 UDFAttributesToUDF(DirNdx, Fcb->FileInfo->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE); 380 } 381 #endif //UDF_READ_ONLY_BUILD 382 UDFFlushFile__( Fcb->Vcb, Fcb->FileInfo, FlushFlags); 383 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 384 BrutePoint(); 385 } _SEH2_END; 386 387 /* if(PurgeCache) { 388 _SEH2_TRY { 389 MmPrint((" CcPurgeCacheSection()\n")); 390 CcPurgeCacheSection( &(Fcb->NTRequiredFCB->SectionObject), NULL, 0, FALSE ); 391 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 392 BrutePoint(); 393 } _SEH2_END; 394 }*/ 395 396 return ret_val; 397 } // end UDFFlushAFile() 398 399 /************************************************************************* 400 * 401 * Function: UDFFlushADirectory() 402 * 403 * Description: 404 * Tell the Cache Manager to perform a flush for all files 405 * in current directory & all subdirectories and flush all metadata 406 * 407 * Expected Interrupt Level (for execution) : 408 * 409 * IRQL_PASSIVE_LEVEL 410 * 411 * Return Value: None 412 * 413 *************************************************************************/ 414 ULONG 415 UDFFlushADirectory( 416 IN PVCB Vcb, 417 IN PUDF_FILE_INFO FI, 418 OUT PIO_STATUS_BLOCK PtrIoStatus, 419 IN ULONG FlushFlags 420 ) 421 { 422 UDFPrint(("UDFFlushADirectory: \n")); 423 // PDIR_INDEX_HDR hDI; 424 PDIR_INDEX_ITEM DI; 425 // BOOLEAN Referenced = FALSE; 426 ULONG ret_val = 0; 427 428 if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) 429 return 0; 430 431 if(!FI || !FI->Dloc || !FI->Dloc->DirIndex) goto SkipFlushDir; 432 // hDI = FI->Dloc->DirIndex; 433 434 // Flush Security if required 435 _SEH2_TRY { 436 UDFWriteSecurity(Vcb, FI->Fcb, &(FI->Fcb->NTRequiredFCB->SecurityDesc)); 437 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 438 BrutePoint(); 439 } _SEH2_END; 440 // Flush SDir if any 441 _SEH2_TRY { 442 if(UDFHasAStreamDir(FI) && 443 FI->Dloc->SDirInfo && 444 !UDFIsSDirDeleted(FI->Dloc->SDirInfo) ) { 445 ret_val |= 446 UDFFlushADirectory(Vcb, FI->Dloc->SDirInfo, PtrIoStatus, FlushFlags); 447 } 448 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 449 BrutePoint(); 450 } _SEH2_END; 451 452 // Flush Dir Tree 453 _SEH2_TRY { 454 UDF_DIR_SCAN_CONTEXT ScanContext; 455 PUDF_FILE_INFO tempFI; 456 457 if(UDFDirIndexInitScan(FI, &ScanContext, 2)) { 458 while((DI = UDFDirIndexScan(&ScanContext, &tempFI))) { 459 // Flush Dir entry 460 _SEH2_TRY { 461 if(!tempFI) continue; 462 if(UDFIsADirectory(tempFI)) { 463 UDFFlushADirectory(Vcb, tempFI, PtrIoStatus, FlushFlags); 464 } else { 465 UDFFlushAFile(tempFI->Fcb, NULL, PtrIoStatus, FlushFlags); 466 } 467 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 468 BrutePoint(); 469 } _SEH2_END; 470 if(UDFFlushIsBreaking(Vcb, FlushFlags)) { 471 ret_val |= UDF_FLUSH_FLAGS_INTERRUPTED; 472 break; 473 } 474 } 475 } 476 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 477 BrutePoint(); 478 } _SEH2_END; 479 SkipFlushDir: 480 // Flush Dir 481 _SEH2_TRY { 482 UDFFlushFile__( Vcb, FI, FlushFlags ); 483 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 484 BrutePoint(); 485 } _SEH2_END; 486 487 return ret_val; 488 } // end UDFFlushADirectory() 489 490 /************************************************************************* 491 * 492 * Function: UDFFlushLogicalVolume() 493 * 494 * Description: 495 * Flush everything beginning from root directory. 496 * Vcb must be previously acquired exclusively. 497 * 498 * Expected Interrupt Level (for execution) : 499 * 500 * IRQL_PASSIVE_LEVEL 501 * 502 * Return Value: None 503 * 504 *************************************************************************/ 505 ULONG 506 UDFFlushLogicalVolume( 507 IN PtrUDFIrpContext PtrIrpContext, 508 IN PIRP Irp, 509 IN PVCB Vcb, 510 IN ULONG FlushFlags 511 ) 512 { 513 ULONG ret_val = 0; 514 #ifndef UDF_READ_ONLY_BUILD 515 IO_STATUS_BLOCK IoStatus; 516 517 UDFPrint(("UDFFlushLogicalVolume: \n")); 518 519 _SEH2_TRY { 520 if(Vcb->VCBFlags & (UDF_VCB_FLAGS_RAW_DISK/* | 521 UDF_VCB_FLAGS_MEDIA_READ_ONLY*/)) 522 return 0; 523 if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) 524 return 0; 525 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) 526 return 0; 527 528 // NOTE: This function may also be invoked internally as part of 529 // processing a shutdown request. 530 ASSERT(Vcb->RootDirFCB); 531 ret_val |= UDFFlushADirectory(Vcb, Vcb->RootDirFCB->FileInfo, &IoStatus, FlushFlags); 532 533 // if(UDFFlushIsBreaking(Vcb, FlushFlags)) 534 // return; 535 // flush internal cache 536 if(FlushFlags & UDF_FLUSH_FLAGS_LITE) { 537 UDFPrint((" Lite flush, keep Modified=%d.\n", Vcb->Modified)); 538 } else { 539 if(Vcb->VerifyOnWrite) { 540 UDFPrint(("UDF: Flushing cache for verify\n")); 541 //WCacheFlushAll__(&(Vcb->FastCache), Vcb); 542 WCacheFlushBlocks__(&(Vcb->FastCache), Vcb, 0, Vcb->LastLBA); 543 UDFVFlush(Vcb); 544 } 545 // umount (this is internal operation, NT will "dismount" volume later) 546 UDFUmount__(Vcb); 547 548 UDFPreClrModified(Vcb); 549 WCacheFlushAll__(&(Vcb->FastCache), Vcb); 550 UDFClrModified(Vcb); 551 } 552 553 } _SEH2_FINALLY { 554 ; 555 } _SEH2_END; 556 #endif //UDF_READ_ONLY_BUILD 557 558 return ret_val; 559 } // end UDFFlushLogicalVolume() 560 561 562 /************************************************************************* 563 * 564 * Function: UDFFlushCompletion() 565 * 566 * Description: 567 * Eat up any bad errors. 568 * 569 * Expected Interrupt Level (for execution) : 570 * 571 * IRQL_PASSIVE_LEVEL 572 * 573 * Return Value: None 574 * 575 *************************************************************************/ 576 NTSTATUS 577 NTAPI 578 UDFFlushCompletion( 579 PDEVICE_OBJECT PtrDeviceObject, 580 PIRP Irp, 581 PVOID Context 582 ) 583 { 584 // NTSTATUS RC = STATUS_SUCCESS; 585 586 UDFPrint(("UDFFlushCompletion: \n")); 587 588 if (Irp->PendingReturned) { 589 IoMarkIrpPending(Irp); 590 } 591 592 if (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST) { 593 // cannot do much here, can we? 594 Irp->IoStatus.Status = STATUS_SUCCESS; 595 } 596 597 return(STATUS_SUCCESS); 598 } // end UDFFlushCompletion() 599 600 601 /* 602 Check if we should break FlushTree process 603 */ 604 BOOLEAN 605 UDFFlushIsBreaking( 606 IN PVCB Vcb, 607 IN ULONG FlushFlags 608 ) 609 { 610 BOOLEAN ret_val = FALSE; 611 // if(!(FlushFlags & UDF_FLUSH_FLAGS_BREAKABLE)) 612 return FALSE; 613 UDFAcquireResourceExclusive(&(Vcb->FlushResource),TRUE); 614 ret_val = (Vcb->VCBFlags & UDF_VCB_FLAGS_FLUSH_BREAK_REQ) ? TRUE : FALSE; 615 Vcb->VCBFlags &= ~UDF_VCB_FLAGS_FLUSH_BREAK_REQ; 616 UDFReleaseResource(&(Vcb->FlushResource)); 617 return ret_val; 618 } // end UDFFlushIsBreaking() 619 620 /* 621 Signal FlushTree break request. Note, this is 622 treated as recommendation only 623 */ 624 VOID 625 UDFFlushTryBreak( 626 IN PVCB Vcb 627 ) 628 { 629 UDFAcquireResourceExclusive(&(Vcb->FlushResource),TRUE); 630 Vcb->VCBFlags |= UDF_VCB_FLAGS_FLUSH_BREAK_REQ; 631 UDFReleaseResource(&(Vcb->FlushResource)); 632 } // end UDFFlushTryBreak() 633