1 /*++ 2 3 Copyright (c) 1990-2000 Microsoft Corporation 4 5 Module Name: 6 7 Cache.c 8 9 Abstract: 10 11 This module implements the cache management routines for the Cdfs 12 FSD and FSP, by calling the Common Cache Manager. 13 14 15 --*/ 16 17 #include "cdprocs.h" 18 19 // 20 // The Bug check file id for this module 21 // 22 23 #define BugCheckFileId (CDFS_BUG_CHECK_CACHESUP) 24 25 // 26 // Local debug trace level 27 // 28 29 #ifdef ALLOC_PRAGMA 30 #pragma alloc_text(PAGE, CdCompleteMdl) 31 #pragma alloc_text(PAGE, CdCreateInternalStream) 32 #pragma alloc_text(PAGE, CdDeleteInternalStream) 33 #pragma alloc_text(PAGE, CdPurgeVolume) 34 #endif 35 36 37 VOID 38 CdCreateInternalStream ( 39 _In_ PIRP_CONTEXT IrpContext, 40 _In_ PVCB Vcb, 41 _Inout_ PFCB Fcb, 42 _In_ PUNICODE_STRING Name 43 ) 44 45 /*++ 46 47 Routine Description: 48 49 This function creates an internal stream file for interaction 50 with the cache manager. The Fcb here can be for either a 51 directory stream or for a path table stream. 52 53 Arguments: 54 55 Vcb - Vcb for this volume. 56 57 Fcb - Points to the Fcb for this file. It is either an Index or 58 Path Table Fcb. 59 60 Return Value: 61 62 None. 63 64 --*/ 65 66 { 67 PFILE_OBJECT StreamFile = NULL; 68 BOOLEAN DecrementReference = FALSE; 69 70 BOOLEAN CleanupDirContext = FALSE; 71 BOOLEAN UpdateFcbSizes = FALSE; 72 73 DIRENT Dirent = {0}; 74 DIRENT_ENUM_CONTEXT DirContext = {0}; 75 76 PAGED_CODE(); 77 78 ASSERT_IRP_CONTEXT( IrpContext ); 79 ASSERT_FCB( Fcb ); 80 81 // 82 // We may only have the Fcb shared. Lock the Fcb and do a 83 // safe test to see if we need to really create the file object. 84 // 85 86 CdLockFcb( IrpContext, Fcb ); 87 88 if (Fcb->FileObject != NULL) { 89 90 CdUnlockFcb( IrpContext, Fcb ); 91 return; 92 } 93 94 // 95 // Use a try-finally to facilitate cleanup. 96 // 97 98 _SEH2_TRY { 99 100 // 101 // Create the internal stream. The Vpb should be pointing at our volume 102 // device object at this point. 103 // 104 105 StreamFile = IoCreateStreamFileObjectLite( NULL, Vcb->Vpb->RealDevice ); 106 107 if (StreamFile == NULL) { 108 109 CdRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); 110 } 111 112 // 113 // Initialize the fields of the file object. 114 // 115 116 StreamFile->ReadAccess = TRUE; 117 StreamFile->WriteAccess = FALSE; 118 StreamFile->DeleteAccess = FALSE; 119 120 StreamFile->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject; 121 122 // 123 // Set the file object type and increment the Vcb counts. 124 // 125 126 CdSetFileObject( IrpContext, 127 StreamFile, 128 StreamFileOpen, 129 Fcb, 130 NULL ); 131 132 // 133 // We'll give stream file objects a name to aid IO profiling etc. We 134 // NULL this in CdDeleteInternalStream before OB deletes the file object, 135 // and before CdRemovePrefix is called (which frees Fcb names). 136 // 137 138 StreamFile->FileName = *Name; 139 140 // 141 // We will reference the current Fcb twice to keep it from going 142 // away in the error path. Otherwise if we dereference it 143 // below in the finally clause a close could cause the Fcb to 144 // be deallocated. 145 // 146 147 CdLockVcb( IrpContext, Vcb ); 148 CdIncrementReferenceCounts( IrpContext, Fcb, 2, 0 ); 149 CdUnlockVcb( IrpContext, Vcb ); 150 DecrementReference = TRUE; 151 152 // 153 // Initialize the cache map for the file. 154 // 155 156 CcInitializeCacheMap( StreamFile, 157 (PCC_FILE_SIZES)&Fcb->AllocationSize, 158 TRUE, 159 &CdData.CacheManagerCallbacks, 160 Fcb ); 161 162 // 163 // Go ahead and store the stream file into the Fcb. 164 // 165 166 Fcb->FileObject = StreamFile; 167 StreamFile = NULL; 168 169 // 170 // If this is the first file object for a directory then we need to 171 // read the self entry for this directory and update the sizes 172 // in the Fcb. We know that the Fcb has been initialized so 173 // that we have a least one sector available to read. 174 // 175 176 if (!FlagOn( Fcb->FcbState, FCB_STATE_INITIALIZED )) { 177 178 ULONG NewDataLength; 179 180 // 181 // Initialize the search structures. 182 // 183 184 CdInitializeDirContext( IrpContext, &DirContext ); 185 CdInitializeDirent( IrpContext, &Dirent ); 186 CleanupDirContext = TRUE; 187 188 // 189 // Read the dirent from disk and transfer the data to the 190 // in-memory dirent. 191 // 192 193 CdLookupDirent( IrpContext, 194 Fcb, 195 Fcb->StreamOffset, 196 &DirContext ); 197 198 CdUpdateDirentFromRawDirent( IrpContext, Fcb, &DirContext, &Dirent ); 199 200 // 201 // Verify that this really for the self entry. We do this by 202 // updating the name in the dirent and then checking that it matches 203 // one of the hard coded names. 204 // 205 206 CdUpdateDirentName( IrpContext, &Dirent, FALSE ); 207 208 if (Dirent.CdFileName.FileName.Buffer != CdUnicodeSelfArray) { 209 210 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 211 } 212 213 // 214 // If the data sizes are different then update the header 215 // and Mcb for this Fcb. 216 // 217 218 NewDataLength = BlockAlign( Vcb, Dirent.DataLength + Fcb->StreamOffset ); 219 220 if (NewDataLength == 0) { 221 222 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); 223 } 224 225 if (NewDataLength != Fcb->FileSize.QuadPart) { 226 227 Fcb->AllocationSize.QuadPart = 228 Fcb->FileSize.QuadPart = 229 Fcb->ValidDataLength.QuadPart = NewDataLength; 230 231 CcSetFileSizes( Fcb->FileObject, (PCC_FILE_SIZES) &Fcb->AllocationSize ); 232 233 CdTruncateAllocation( IrpContext, Fcb, 0 ); 234 CdAddInitialAllocation( IrpContext, 235 Fcb, 236 Dirent.StartingOffset, 237 NewDataLength ); 238 239 UpdateFcbSizes = TRUE; 240 } 241 242 // 243 // Check for the existence flag and transform to hidden. 244 // 245 246 if (FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_HIDDEN )) { 247 248 SetFlag( Fcb->FileAttributes, FILE_ATTRIBUTE_HIDDEN ); 249 } 250 251 // 252 // Convert the time to NT time. 253 // 254 255 CdConvertCdTimeToNtTime( IrpContext, 256 Dirent.CdTime, 257 (PLARGE_INTEGER) &Fcb->CreationTime ); 258 259 // 260 // Update the Fcb flags to indicate we have read the 261 // self entry. 262 // 263 264 SetFlag( Fcb->FcbState, FCB_STATE_INITIALIZED ); 265 266 // 267 // If we updated the sizes then we want to purge the file. Go 268 // ahead and unpin and then purge the first page. 269 // 270 271 CdCleanupDirContext( IrpContext, &DirContext ); 272 CdCleanupDirent( IrpContext, &Dirent ); 273 CleanupDirContext = FALSE; 274 275 if (UpdateFcbSizes) { 276 277 CcPurgeCacheSection( &Fcb->FcbNonpaged->SegmentObject, 278 NULL, 279 0, 280 FALSE ); 281 } 282 } 283 284 } _SEH2_FINALLY { 285 286 // 287 // Cleanup any dirent structures we may have used. 288 // 289 290 if (CleanupDirContext) { 291 292 CdCleanupDirContext( IrpContext, &DirContext ); 293 CdCleanupDirent( IrpContext, &Dirent ); 294 } 295 296 // 297 // If we raised then we need to dereference the file object. 298 // 299 300 if (StreamFile != NULL) { 301 302 // 303 // Null the name pointer, since the stream file object never actually 304 // 'owns' the names, we just point it to existing ones. 305 // 306 307 StreamFile->FileName.Buffer = NULL; 308 StreamFile->FileName.MaximumLength = StreamFile->FileName.Length = 0; 309 310 ObDereferenceObject( StreamFile ); 311 Fcb->FileObject = NULL; 312 } 313 314 // 315 // Dereference and unlock the Fcb. 316 // 317 318 if (DecrementReference) { 319 320 CdLockVcb( IrpContext, Vcb ); 321 CdDecrementReferenceCounts( IrpContext, Fcb, 1, 0 ); 322 CdUnlockVcb( IrpContext, Vcb ); 323 } 324 325 CdUnlockFcb( IrpContext, Fcb ); 326 } _SEH2_END; 327 328 return; 329 } 330 331 332 VOID 333 CdDeleteInternalStream ( 334 _In_ PIRP_CONTEXT IrpContext, 335 _Inout_ PFCB Fcb 336 ) 337 338 /*++ 339 340 Routine Description: 341 342 This function creates an internal stream file for interaction 343 with the cache manager. The Fcb here can be for either a 344 directory stream or for a path table stream. 345 346 Arguments: 347 348 Fcb - Points to the Fcb for this file. It is either an Index or 349 Path Table Fcb. 350 351 Return Value: 352 353 None. 354 355 --*/ 356 357 { 358 PFILE_OBJECT FileObject; 359 360 PAGED_CODE(); 361 362 UNREFERENCED_PARAMETER( IrpContext ); 363 364 ASSERT_IRP_CONTEXT( IrpContext ); 365 ASSERT_FCB( Fcb ); 366 367 // 368 // Lock the Fcb. 369 // 370 371 CdLockFcb( IrpContext, Fcb ); 372 373 // 374 // Capture the file object. 375 // 376 377 FileObject = Fcb->FileObject; 378 Fcb->FileObject = NULL; 379 380 // 381 // It is now safe to unlock the Fcb. 382 // 383 384 CdUnlockFcb( IrpContext, Fcb ); 385 386 // 387 // Dereference the file object if present. 388 // 389 390 if (FileObject != NULL) { 391 392 if (FileObject->PrivateCacheMap != NULL) { 393 394 CcUninitializeCacheMap( FileObject, NULL, NULL ); 395 } 396 397 // 398 // Null the name pointer, since the stream file object never actually 399 // 'owns' the names, we just point it to existing ones. 400 // 401 402 FileObject->FileName.Buffer = NULL; 403 FileObject->FileName.MaximumLength = FileObject->FileName.Length = 0; 404 405 ObDereferenceObject( FileObject ); 406 } 407 } 408 409 410 NTSTATUS 411 CdCompleteMdl ( 412 _In_ PIRP_CONTEXT IrpContext, 413 _Inout_ PIRP Irp 414 ) 415 416 /*++ 417 418 Routine Description: 419 420 This routine performs the function of completing Mdl reads. 421 It should be called only from CdFsdRead. 422 423 Arguments: 424 425 Irp - Supplies the originating Irp. 426 427 Return Value: 428 429 NTSTATUS - Will always be STATUS_SUCCESS. 430 431 --*/ 432 433 { 434 PFILE_OBJECT FileObject; 435 436 PAGED_CODE(); 437 438 // 439 // Do completion processing. 440 // 441 442 FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; 443 444 CcMdlReadComplete( FileObject, Irp->MdlAddress ); 445 446 // 447 // Mdl is now deallocated. 448 // 449 450 Irp->MdlAddress = NULL; 451 452 // 453 // Complete the request and exit right away. 454 // 455 456 CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); 457 458 return STATUS_SUCCESS; 459 } 460 461 462 463 _Requires_lock_held_(_Global_critical_region_) 464 NTSTATUS 465 CdPurgeVolume ( 466 _In_ PIRP_CONTEXT IrpContext, 467 _In_ PVCB Vcb, 468 _In_ BOOLEAN DismountUnderway 469 ) 470 471 /*++ 472 473 Routine Description: 474 475 This routine is called to purge the volume. The purpose is to make all the stale file 476 objects in the system go away in order to lock the volume. 477 478 The Vcb is already acquired exclusively. We will lock out all file operations by 479 acquiring the global file resource. Then we will walk through all of the Fcb's and 480 perform the purge. 481 482 Arguments: 483 484 Vcb - Vcb for the volume to purge. 485 486 DismountUnderway - Indicates that we are trying to delete all of the objects. 487 We will purge the Path Table and VolumeDasd and dereference all 488 internal streams. 489 490 Return Value: 491 492 NTSTATUS - The first failure of the purge operation. 493 494 --*/ 495 496 { 497 NTSTATUS Status = STATUS_SUCCESS; 498 499 PVOID RestartKey = NULL; 500 PFCB ThisFcb = NULL; 501 PFCB NextFcb; 502 503 BOOLEAN RemovedFcb; 504 505 PAGED_CODE(); 506 507 ASSERT_EXCLUSIVE_VCB( Vcb); 508 509 // 510 // Force any remaining Fcb's in the delayed close queue to be closed. 511 // 512 513 CdFspClose( Vcb ); 514 515 // 516 // Acquire the global file resource. 517 // 518 519 CdAcquireAllFiles( IrpContext, Vcb ); 520 521 // 522 // Loop through each Fcb in the Fcb Table and perform the flush. 523 // 524 525 while (TRUE) { 526 527 // 528 // Lock the Vcb to lookup the next Fcb. 529 // 530 531 CdLockVcb( IrpContext, Vcb ); 532 NextFcb = CdGetNextFcb( IrpContext, Vcb, &RestartKey ); 533 534 // 535 // Reference the NextFcb if present. 536 // 537 538 if (NextFcb != NULL) { 539 540 NextFcb->FcbReference += 1; 541 } 542 543 // 544 // If the last Fcb is present then decrement reference count and call teardown 545 // to see if it should be removed. 546 // 547 548 if (ThisFcb != NULL) { 549 550 ThisFcb->FcbReference -= 1; 551 552 CdUnlockVcb( IrpContext, Vcb ); 553 554 CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); 555 556 } else { 557 558 CdUnlockVcb( IrpContext, Vcb ); 559 } 560 561 // 562 // Break out of the loop if no more Fcb's. 563 // 564 565 if (NextFcb == NULL) { 566 567 break; 568 } 569 570 // 571 // Move to the next Fcb. 572 // 573 574 ThisFcb = NextFcb; 575 576 // 577 // If there is a image section then see if that can be closed. 578 // 579 580 if (ThisFcb->FcbNonpaged->SegmentObject.ImageSectionObject != NULL) { 581 582 MmFlushImageSection( &ThisFcb->FcbNonpaged->SegmentObject, MmFlushForWrite ); 583 } 584 585 // 586 // If there is a data section then purge this. If there is an image 587 // section then we won't be able to. Remember this if it is our first 588 // error. 589 // 590 591 if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && 592 !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, 593 NULL, 594 0, 595 FALSE ) && 596 (Status == STATUS_SUCCESS)) { 597 598 Status = STATUS_UNABLE_TO_DELETE_SECTION; 599 } 600 601 // 602 // Dereference the internal stream if dismounting. 603 // 604 605 if (DismountUnderway && 606 (SafeNodeType( ThisFcb ) != CDFS_NTC_FCB_DATA) && 607 (ThisFcb->FileObject != NULL)) { 608 609 CdDeleteInternalStream( IrpContext, ThisFcb ); 610 } 611 } 612 613 // 614 // Now look at the path table and volume Dasd Fcb's. 615 // 616 617 if (DismountUnderway) { 618 619 if (Vcb->PathTableFcb != NULL) { 620 621 ThisFcb = Vcb->PathTableFcb; 622 InterlockedIncrement( (LONG*)&Vcb->PathTableFcb->FcbReference ); 623 624 if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && 625 !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, 626 NULL, 627 0, 628 FALSE ) && 629 (Status == STATUS_SUCCESS)) { 630 631 Status = STATUS_UNABLE_TO_DELETE_SECTION; 632 } 633 634 CdDeleteInternalStream( IrpContext, ThisFcb ); 635 636 InterlockedDecrement( (LONG*)&ThisFcb->FcbReference ); 637 638 CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); 639 } 640 641 if (Vcb->VolumeDasdFcb != NULL) { 642 643 ThisFcb = Vcb->VolumeDasdFcb; 644 InterlockedIncrement( (LONG*)&ThisFcb->FcbReference ); 645 646 if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) && 647 !CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject, 648 NULL, 649 0, 650 FALSE ) && 651 (Status == STATUS_SUCCESS)) { 652 653 Status = STATUS_UNABLE_TO_DELETE_SECTION; 654 } 655 656 InterlockedDecrement( (LONG*)&ThisFcb->FcbReference ); 657 658 CdTeardownStructures( IrpContext, ThisFcb, &RemovedFcb ); 659 } 660 } 661 662 // 663 // Release all of the files. 664 // 665 666 CdReleaseAllFiles( IrpContext, Vcb ); 667 668 return Status; 669 } 670 671 672