1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Kernel 4 * FILE: ntoskrnl/cache/fssup.c 5 * PURPOSE: Logging and configuration routines 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 * Art Yerkes 8 */ 9 10 /* INCLUDES *******************************************************************/ 11 12 #include <ntoskrnl.h> 13 #include "newcc.h" 14 #include "section/newmm.h" 15 #define NDEBUG 16 #include <debug.h> 17 18 /* GLOBALS ********************************************************************/ 19 20 PFSN_PREFETCHER_GLOBALS CcPfGlobals; 21 extern LONG CcOutstandingDeletes; 22 extern KEVENT CcpLazyWriteEvent; 23 extern KEVENT CcFinalizeEvent; 24 extern VOID NTAPI CcpUnmapThread(PVOID Unused); 25 extern VOID NTAPI CcpLazyWriteThread(PVOID Unused); 26 HANDLE CcUnmapThreadHandle, CcLazyWriteThreadHandle; 27 CLIENT_ID CcUnmapThreadId, CcLazyWriteThreadId; 28 FAST_MUTEX GlobalPageOperation; 29 30 /* 31 32 A note about private cache maps. 33 34 CcInitializeCacheMap and CcUninitializeCacheMap are not meant to be paired, 35 although they can work that way. 36 37 The actual operation I've gleaned from reading both jan kratchovil's writing 38 and real filesystems is this: 39 40 CcInitializeCacheMap means: 41 42 Make the indicated FILE_OBJECT have a private cache map if it doesn't already 43 and make it have a shared cache map if it doesn't already. 44 45 CcUninitializeCacheMap means: 46 47 Take away the private cache map from this FILE_OBJECT. If it's the last 48 private cache map corresponding to a specific shared cache map (the one that 49 was present in the FILE_OBJECT when it was created), then delete that too, 50 flusing all cached information. 51 52 Using these simple semantics, filesystems can do all the things they actually 53 do: 54 55 - Copy out the shared cache map pointer from a newly initialized file object 56 and store it in the fcb cache. 57 - Copy it back into any file object and call CcInitializeCacheMap to make 58 that file object be associated with the caching of all the other siblings. 59 - Call CcUninitializeCacheMap on a FILE_OBJECT many times, but have only the 60 first one count for each specific FILE_OBJECT. 61 - Have the actual last call to CcUninitializeCacheMap (that is, the one that 62 causes zero private cache maps to be associated with a shared cache map) to 63 delete the cache map and flush. 64 65 So private cache map here is a light weight structure that just remembers 66 what shared cache map it associates with. 67 68 */ 69 typedef struct _NOCC_PRIVATE_CACHE_MAP 70 { 71 LIST_ENTRY ListEntry; 72 PFILE_OBJECT FileObject; 73 PNOCC_CACHE_MAP Map; 74 } NOCC_PRIVATE_CACHE_MAP, *PNOCC_PRIVATE_CACHE_MAP; 75 76 LIST_ENTRY CcpAllSharedCacheMaps; 77 78 /* FUNCTIONS ******************************************************************/ 79 80 INIT_FUNCTION 81 BOOLEAN 82 NTAPI 83 CcInitializeCacheManager(VOID) 84 { 85 int i; 86 87 DPRINT("Initialize\n"); 88 for (i = 0; i < CACHE_NUM_SECTIONS; i++) 89 { 90 KeInitializeEvent(&CcCacheSections[i].ExclusiveWait, 91 SynchronizationEvent, 92 FALSE); 93 94 InitializeListHead(&CcCacheSections[i].ThisFileList); 95 } 96 97 InitializeListHead(&CcpAllSharedCacheMaps); 98 99 KeInitializeEvent(&CcDeleteEvent, SynchronizationEvent, FALSE); 100 KeInitializeEvent(&CcFinalizeEvent, SynchronizationEvent, FALSE); 101 KeInitializeEvent(&CcpLazyWriteEvent, SynchronizationEvent, FALSE); 102 103 CcCacheBitmap->Buffer = ((PULONG)&CcCacheBitmap[1]); 104 CcCacheBitmap->SizeOfBitMap = ROUND_UP(CACHE_NUM_SECTIONS, 32); 105 DPRINT1("Cache has %d entries\n", CcCacheBitmap->SizeOfBitMap); 106 ExInitializeFastMutex(&CcMutex); 107 108 return TRUE; 109 } 110 111 INIT_FUNCTION 112 VOID 113 NTAPI 114 CcPfInitializePrefetcher(VOID) 115 { 116 /* Notify debugger */ 117 DbgPrintEx(DPFLTR_PREFETCHER_ID, 118 DPFLTR_TRACE_LEVEL, 119 "CCPF: InitializePrefetecher()\n"); 120 121 /* Setup the Prefetcher Data */ 122 InitializeListHead(&CcPfGlobals.ActiveTraces); 123 InitializeListHead(&CcPfGlobals.CompletedTraces); 124 ExInitializeFastMutex(&CcPfGlobals.CompletedTracesLock); 125 126 /* FIXME: Setup the rest of the prefetecher */ 127 } 128 129 BOOLEAN 130 NTAPI 131 CcpAcquireFileLock(PNOCC_CACHE_MAP Map) 132 { 133 DPRINT("Calling AcquireForLazyWrite: %x\n", Map->LazyContext); 134 return Map->Callbacks.AcquireForLazyWrite(Map->LazyContext, TRUE); 135 } 136 137 VOID 138 NTAPI 139 CcpReleaseFileLock(PNOCC_CACHE_MAP Map) 140 { 141 DPRINT("Releasing Lazy Write %x\n", Map->LazyContext); 142 Map->Callbacks.ReleaseFromLazyWrite(Map->LazyContext); 143 } 144 145 /* 146 147 Cc functions are required to treat alternate streams of a file as the same 148 for the purpose of caching, meaning that we must be able to find the shared 149 cache map associated with the ``real'' stream associated with a stream file 150 object, if one exists. We do that by identifying a private cache map in 151 our gamut that has the same volume, device and fscontext as the stream file 152 object we're holding. It's heavy but it does work. This can probably be 153 improved, although there doesn't seem to be any real association between 154 a stream file object and a sibling file object in the file object struct 155 itself. 156 157 */ 158 159 /* Must have CcpLock() */ 160 PFILE_OBJECT CcpFindOtherStreamFileObject(PFILE_OBJECT FileObject) 161 { 162 PLIST_ENTRY Entry, Private; 163 for (Entry = CcpAllSharedCacheMaps.Flink; 164 Entry != &CcpAllSharedCacheMaps; 165 Entry = Entry->Flink) 166 { 167 /* 'Identical' test for other stream file object */ 168 PNOCC_CACHE_MAP Map = CONTAINING_RECORD(Entry, NOCC_CACHE_MAP, Entry); 169 for (Private = Map->PrivateCacheMaps.Flink; 170 Private != &Map->PrivateCacheMaps; 171 Private = Private->Flink) 172 { 173 PNOCC_PRIVATE_CACHE_MAP PrivateMap = CONTAINING_RECORD(Private, 174 NOCC_PRIVATE_CACHE_MAP, 175 ListEntry); 176 177 if (PrivateMap->FileObject->Flags & FO_STREAM_FILE && 178 PrivateMap->FileObject->DeviceObject == FileObject->DeviceObject && 179 PrivateMap->FileObject->Vpb == FileObject->Vpb && 180 PrivateMap->FileObject->FsContext == FileObject->FsContext && 181 PrivateMap->FileObject->FsContext2 == FileObject->FsContext2 && 182 1) 183 { 184 return PrivateMap->FileObject; 185 } 186 } 187 } 188 return 0; 189 } 190 191 /* Thanks: http://windowsitpro.com/Windows/Articles/ArticleID/3864/pg/2/2.html */ 192 193 VOID 194 NTAPI 195 CcInitializeCacheMap(IN PFILE_OBJECT FileObject, 196 IN PCC_FILE_SIZES FileSizes, 197 IN BOOLEAN PinAccess, 198 IN PCACHE_MANAGER_CALLBACKS Callbacks, 199 IN PVOID LazyWriteContext) 200 { 201 PNOCC_CACHE_MAP Map = FileObject->SectionObjectPointer->SharedCacheMap; 202 PNOCC_PRIVATE_CACHE_MAP PrivateCacheMap = FileObject->PrivateCacheMap; 203 204 CcpLock(); 205 /* We don't have a shared cache map. First find out if we have a sibling 206 stream file object we can take it from. */ 207 if (!Map && FileObject->Flags & FO_STREAM_FILE) 208 { 209 PFILE_OBJECT IdenticalStreamFileObject = CcpFindOtherStreamFileObject(FileObject); 210 if (IdenticalStreamFileObject) 211 Map = IdenticalStreamFileObject->SectionObjectPointer->SharedCacheMap; 212 if (Map) 213 { 214 DPRINT1("Linking SFO %x to previous SFO %x through cache map %x #\n", 215 FileObject, 216 IdenticalStreamFileObject, 217 Map); 218 } 219 } 220 /* We still don't have a shared cache map. We need to create one. */ 221 if (!Map) 222 { 223 DPRINT("Initializing file object for (%p) %wZ\n", 224 FileObject, 225 &FileObject->FileName); 226 227 Map = ExAllocatePool(NonPagedPool, sizeof(NOCC_CACHE_MAP)); 228 FileObject->SectionObjectPointer->SharedCacheMap = Map; 229 Map->FileSizes = *FileSizes; 230 Map->LazyContext = LazyWriteContext; 231 Map->ReadAheadGranularity = PAGE_SIZE; 232 RtlCopyMemory(&Map->Callbacks, Callbacks, sizeof(*Callbacks)); 233 234 /* For now ... */ 235 DPRINT("FileSizes->ValidDataLength %I64x\n", 236 FileSizes->ValidDataLength.QuadPart); 237 238 InitializeListHead(&Map->AssociatedBcb); 239 InitializeListHead(&Map->PrivateCacheMaps); 240 InsertTailList(&CcpAllSharedCacheMaps, &Map->Entry); 241 DPRINT("New Map %p\n", Map); 242 } 243 /* We don't have a private cache map. Link it with the shared cache map 244 to serve as a held reference. When the list in the shared cache map 245 is empty, we know we can delete it. */ 246 if (!PrivateCacheMap) 247 { 248 PrivateCacheMap = ExAllocatePool(NonPagedPool, 249 sizeof(*PrivateCacheMap)); 250 251 FileObject->PrivateCacheMap = PrivateCacheMap; 252 PrivateCacheMap->FileObject = FileObject; 253 ObReferenceObject(PrivateCacheMap->FileObject); 254 } 255 256 PrivateCacheMap->Map = Map; 257 InsertTailList(&Map->PrivateCacheMaps, &PrivateCacheMap->ListEntry); 258 259 CcpUnlock(); 260 } 261 262 /* 263 264 This function is used by NewCC's MM to determine whether any section objects 265 for a given file are not cache sections. If that's true, we're not allowed 266 to resize the file, although nothing actually prevents us from doing ;-) 267 268 */ 269 270 ULONG 271 NTAPI 272 CcpCountCacheSections(IN PNOCC_CACHE_MAP Map) 273 { 274 PLIST_ENTRY Entry; 275 ULONG Count; 276 277 for (Count = 0, Entry = Map->AssociatedBcb.Flink; 278 Entry != &Map->AssociatedBcb; 279 Entry = Entry->Flink, Count++); 280 281 return Count; 282 } 283 284 BOOLEAN 285 NTAPI 286 CcUninitializeCacheMap(IN PFILE_OBJECT FileObject, 287 IN OPTIONAL PLARGE_INTEGER TruncateSize, 288 IN OPTIONAL PCACHE_UNINITIALIZE_EVENT UninitializeEvent) 289 { 290 BOOLEAN LastMap = FALSE; 291 PNOCC_CACHE_MAP Map = (PNOCC_CACHE_MAP)FileObject->SectionObjectPointer->SharedCacheMap; 292 PNOCC_PRIVATE_CACHE_MAP PrivateCacheMap = FileObject->PrivateCacheMap; 293 294 DPRINT("Uninitializing file object for %wZ SectionObjectPointer %x\n", 295 &FileObject->FileName, 296 FileObject->SectionObjectPointer); 297 298 ASSERT(UninitializeEvent == NULL); 299 300 /* It may not be strictly necessary to flush here, but we do just for 301 kicks. */ 302 if (Map) 303 CcpFlushCache(Map, NULL, 0, NULL, FALSE); 304 305 CcpLock(); 306 /* We have a private cache map, so we've been initialized and haven't been 307 * uninitialized. */ 308 if (PrivateCacheMap) 309 { 310 ASSERT(!Map || Map == PrivateCacheMap->Map); 311 ASSERT(PrivateCacheMap->FileObject == FileObject); 312 313 RemoveEntryList(&PrivateCacheMap->ListEntry); 314 /* That was the last private cache map. It's time to delete all 315 cache stripes and all aspects of caching on the file. */ 316 if (IsListEmpty(&PrivateCacheMap->Map->PrivateCacheMaps)) 317 { 318 /* Get rid of all the cache stripes. */ 319 while (!IsListEmpty(&PrivateCacheMap->Map->AssociatedBcb)) 320 { 321 PNOCC_BCB Bcb = CONTAINING_RECORD(PrivateCacheMap->Map->AssociatedBcb.Flink, 322 NOCC_BCB, 323 ThisFileList); 324 325 DPRINT("Evicting cache stripe #%x\n", Bcb - CcCacheSections); 326 Bcb->RefCount = 1; 327 CcpDereferenceCache(Bcb - CcCacheSections, TRUE); 328 } 329 RemoveEntryList(&PrivateCacheMap->Map->Entry); 330 ExFreePool(PrivateCacheMap->Map); 331 FileObject->SectionObjectPointer->SharedCacheMap = NULL; 332 LastMap = TRUE; 333 } 334 ObDereferenceObject(PrivateCacheMap->FileObject); 335 FileObject->PrivateCacheMap = NULL; 336 ExFreePool(PrivateCacheMap); 337 } 338 CcpUnlock(); 339 340 DPRINT("Uninit complete\n"); 341 342 /* The return from CcUninitializeCacheMap means that 'caching was stopped'. */ 343 return LastMap; 344 } 345 346 /* 347 348 CcSetFileSizes is used to tell the cache manager that the file changed 349 size. In our case, we use the internal Mm method MmExtendCacheSection 350 to notify Mm that our section potentially changed size, which may mean 351 truncating off data. 352 353 */ 354 VOID 355 NTAPI 356 CcSetFileSizes(IN PFILE_OBJECT FileObject, 357 IN PCC_FILE_SIZES FileSizes) 358 { 359 PNOCC_CACHE_MAP Map = (PNOCC_CACHE_MAP)FileObject->SectionObjectPointer->SharedCacheMap; 360 PNOCC_BCB Bcb; 361 362 if (!Map) return; 363 Map->FileSizes = *FileSizes; 364 Bcb = Map->AssociatedBcb.Flink == &Map->AssociatedBcb ? 365 NULL : CONTAINING_RECORD(Map->AssociatedBcb.Flink, NOCC_BCB, ThisFileList); 366 if (!Bcb) return; 367 MmExtendCacheSection(Bcb->SectionObject, &FileSizes->FileSize, FALSE); 368 DPRINT("FileSizes->FileSize %x\n", FileSizes->FileSize.LowPart); 369 DPRINT("FileSizes->AllocationSize %x\n", FileSizes->AllocationSize.LowPart); 370 DPRINT("FileSizes->ValidDataLength %x\n", FileSizes->ValidDataLength.LowPart); 371 } 372 373 BOOLEAN 374 NTAPI 375 CcGetFileSizes(IN PFILE_OBJECT FileObject, 376 IN PCC_FILE_SIZES FileSizes) 377 { 378 PNOCC_CACHE_MAP Map = (PNOCC_CACHE_MAP)FileObject->SectionObjectPointer->SharedCacheMap; 379 if (!Map) return FALSE; 380 *FileSizes = Map->FileSizes; 381 return TRUE; 382 } 383 384 BOOLEAN 385 NTAPI 386 CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer, 387 IN OPTIONAL PLARGE_INTEGER FileOffset, 388 IN ULONG Length, 389 IN BOOLEAN UninitializeCacheMaps) 390 { 391 PNOCC_CACHE_MAP Map = (PNOCC_CACHE_MAP)SectionObjectPointer->SharedCacheMap; 392 if (!Map) return TRUE; 393 CcpFlushCache(Map, NULL, 0, NULL, TRUE); 394 return TRUE; 395 } 396 397 VOID 398 NTAPI 399 CcSetDirtyPageThreshold(IN PFILE_OBJECT FileObject, 400 IN ULONG DirtyPageThreshold) 401 { 402 UNIMPLEMENTED_DBGBREAK(); 403 } 404 405 /* 406 407 This could be implemented much more intelligently by mapping instances 408 of a CoW zero page into the affected regions. We just RtlZeroMemory 409 for now. 410 411 */ 412 BOOLEAN 413 NTAPI 414 CcZeroData(IN PFILE_OBJECT FileObject, 415 IN PLARGE_INTEGER StartOffset, 416 IN PLARGE_INTEGER EndOffset, 417 IN BOOLEAN Wait) 418 { 419 PNOCC_BCB Bcb = NULL; 420 PLIST_ENTRY ListEntry = NULL; 421 LARGE_INTEGER LowerBound = *StartOffset; 422 LARGE_INTEGER UpperBound = *EndOffset; 423 LARGE_INTEGER Target, End; 424 PVOID PinnedBcb, PinnedBuffer; 425 PNOCC_CACHE_MAP Map = FileObject->SectionObjectPointer->SharedCacheMap; 426 427 DPRINT("S %I64x E %I64x\n", 428 StartOffset->QuadPart, 429 EndOffset->QuadPart); 430 431 if (!Map) 432 { 433 NTSTATUS Status; 434 IO_STATUS_BLOCK IOSB; 435 PCHAR ZeroBuf = ExAllocatePool(PagedPool, PAGE_SIZE); 436 ULONG ToWrite; 437 438 if (!ZeroBuf) RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 439 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf, PAGE_SIZE); 440 RtlZeroMemory(ZeroBuf, PAGE_SIZE); 441 442 Target.QuadPart = PAGE_ROUND_DOWN(LowerBound.QuadPart); 443 End.QuadPart = PAGE_ROUND_UP(UpperBound.QuadPart); 444 445 // Handle leading page 446 if (LowerBound.QuadPart != Target.QuadPart) 447 { 448 ToWrite = MIN(UpperBound.QuadPart - LowerBound.QuadPart, 449 (PAGE_SIZE - LowerBound.QuadPart) & (PAGE_SIZE - 1)); 450 451 DPRINT("Zero last half %I64x %lx\n", 452 Target.QuadPart, 453 ToWrite); 454 455 Status = MiSimpleRead(FileObject, 456 &Target, 457 ZeroBuf, 458 PAGE_SIZE, 459 TRUE, 460 &IOSB); 461 462 if (!NT_SUCCESS(Status)) 463 { 464 ExFreePool(ZeroBuf); 465 RtlRaiseStatus(Status); 466 } 467 468 DPRINT1("RtlZeroMemory(%p, %lx)\n", 469 ZeroBuf + LowerBound.QuadPart - Target.QuadPart, 470 ToWrite); 471 472 RtlZeroMemory(ZeroBuf + LowerBound.QuadPart - Target.QuadPart, 473 ToWrite); 474 475 Status = MiSimpleWrite(FileObject, 476 &Target, 477 ZeroBuf, 478 MIN(PAGE_SIZE, 479 UpperBound.QuadPart-Target.QuadPart), 480 &IOSB); 481 482 if (!NT_SUCCESS(Status)) 483 { 484 ExFreePool(ZeroBuf); 485 RtlRaiseStatus(Status); 486 } 487 Target.QuadPart += PAGE_SIZE; 488 } 489 490 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf, PAGE_SIZE); 491 RtlZeroMemory(ZeroBuf, PAGE_SIZE); 492 493 while (UpperBound.QuadPart - Target.QuadPart > PAGE_SIZE) 494 { 495 DPRINT("Zero full page %I64x\n", 496 Target.QuadPart); 497 498 Status = MiSimpleWrite(FileObject, 499 &Target, 500 ZeroBuf, 501 PAGE_SIZE, 502 &IOSB); 503 504 if (!NT_SUCCESS(Status)) 505 { 506 ExFreePool(ZeroBuf); 507 RtlRaiseStatus(Status); 508 } 509 Target.QuadPart += PAGE_SIZE; 510 } 511 512 if (UpperBound.QuadPart > Target.QuadPart) 513 { 514 ToWrite = UpperBound.QuadPart - Target.QuadPart; 515 DPRINT("Zero first half %I64x %lx\n", 516 Target.QuadPart, 517 ToWrite); 518 519 Status = MiSimpleRead(FileObject, 520 &Target, 521 ZeroBuf, 522 PAGE_SIZE, 523 TRUE, 524 &IOSB); 525 526 if (!NT_SUCCESS(Status)) 527 { 528 ExFreePool(ZeroBuf); 529 RtlRaiseStatus(Status); 530 } 531 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf, ToWrite); 532 RtlZeroMemory(ZeroBuf, ToWrite); 533 Status = MiSimpleWrite(FileObject, 534 &Target, 535 ZeroBuf, 536 MIN(PAGE_SIZE, 537 UpperBound.QuadPart-Target.QuadPart), 538 &IOSB); 539 if (!NT_SUCCESS(Status)) 540 { 541 ExFreePool(ZeroBuf); 542 RtlRaiseStatus(Status); 543 } 544 Target.QuadPart += PAGE_SIZE; 545 } 546 547 ExFreePool(ZeroBuf); 548 return TRUE; 549 } 550 551 CcpLock(); 552 ListEntry = Map->AssociatedBcb.Flink; 553 554 while (ListEntry != &Map->AssociatedBcb) 555 { 556 Bcb = CONTAINING_RECORD(ListEntry, NOCC_BCB, ThisFileList); 557 CcpReferenceCache(Bcb - CcCacheSections); 558 559 if (Bcb->FileOffset.QuadPart + Bcb->Length >= LowerBound.QuadPart && 560 Bcb->FileOffset.QuadPart < UpperBound.QuadPart) 561 { 562 DPRINT("Bcb #%x (@%I64x)\n", 563 Bcb - CcCacheSections, 564 Bcb->FileOffset.QuadPart); 565 566 Target.QuadPart = MAX(Bcb->FileOffset.QuadPart, 567 LowerBound.QuadPart); 568 569 End.QuadPart = MIN(Map->FileSizes.ValidDataLength.QuadPart, 570 UpperBound.QuadPart); 571 572 End.QuadPart = MIN(End.QuadPart, 573 Bcb->FileOffset.QuadPart + Bcb->Length); 574 575 CcpUnlock(); 576 577 if (!CcPreparePinWrite(FileObject, 578 &Target, 579 End.QuadPart - Target.QuadPart, 580 TRUE, 581 Wait, 582 &PinnedBcb, 583 &PinnedBuffer)) 584 { 585 return FALSE; 586 } 587 588 ASSERT(PinnedBcb == Bcb); 589 590 CcpLock(); 591 ListEntry = ListEntry->Flink; 592 /* Return from pin state */ 593 CcpUnpinData(PinnedBcb, TRUE); 594 } 595 596 CcpUnpinData(Bcb, TRUE); 597 } 598 599 CcpUnlock(); 600 601 return TRUE; 602 } 603 604 PFILE_OBJECT 605 NTAPI 606 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointer) 607 { 608 PFILE_OBJECT Result = NULL; 609 PNOCC_CACHE_MAP Map = SectionObjectPointer->SharedCacheMap; 610 CcpLock(); 611 if (!IsListEmpty(&Map->AssociatedBcb)) 612 { 613 PNOCC_BCB Bcb = CONTAINING_RECORD(Map->AssociatedBcb.Flink, 614 NOCC_BCB, 615 ThisFileList); 616 617 Result = MmGetFileObjectForSection((PROS_SECTION_OBJECT)Bcb->SectionObject); 618 } 619 CcpUnlock(); 620 return Result; 621 } 622 623 PFILE_OBJECT 624 NTAPI 625 CcGetFileObjectFromBcb(PVOID Bcb) 626 { 627 PNOCC_BCB RealBcb = (PNOCC_BCB)Bcb; 628 DPRINT("BCB #%x\n", RealBcb - CcCacheSections); 629 return MmGetFileObjectForSection((PROS_SECTION_OBJECT)RealBcb->SectionObject); 630 } 631 632 /* EOF */ 633