Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3733 lines
110 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. fssup.c
  5. Abstract:
  6. This module implements the File System support routines for the
  7. Cache subsystem.
  8. Author:
  9. Tom Miller [TomM] 4-May-1990
  10. Revision History:
  11. --*/
  12. #include "cc.h"
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (CACHE_BUG_CHECK_FSSUP)
  17. //
  18. // Define our debug constant
  19. //
  20. #define me 0x00000001
  21. //
  22. // For your debugging pleasure, if the flag doesn't move! (Currently not used)
  23. //
  24. #define IsSyscacheFile(FO) (((FO) != NULL) && \
  25. (*(PUSHORT)(FO)->FsContext == 0X705) && \
  26. FlagOn(*(PULONG)((PCHAR)(FO)->FsContext + 0x48), 0x80000000))
  27. extern POBJECT_TYPE IoFileObjectType;
  28. extern ULONG MmLargeSystemCache;
  29. VOID
  30. CcUnmapAndPurge(
  31. IN PSHARED_CACHE_MAP SharedCacheMap
  32. );
  33. VOID
  34. CcDeleteMbcb(
  35. IN PSHARED_CACHE_MAP SharedCacheMap
  36. );
  37. VOID
  38. CcDeleteBcbs (
  39. IN PSHARED_CACHE_MAP SharedCacheMap
  40. );
  41. VOID
  42. CcPurgeAndClearCacheSection (
  43. IN PSHARED_CACHE_MAP SharedCacheMap,
  44. IN PLARGE_INTEGER FileOffset
  45. );
  46. #ifdef ALLOC_PRAGMA
  47. #pragma alloc_text(INIT,CcInitializeCacheManager)
  48. #pragma alloc_text(PAGE,CcZeroData)
  49. #endif
  50. BOOLEAN
  51. CcInitializeCacheManager (
  52. )
  53. /*++
  54. Routine Description:
  55. This routine must be called during system initialization before the
  56. first call to any file system, to allow the Cache Manager to initialize
  57. its global data structures. This routine has no dependencies on other
  58. system components being initialized.
  59. Arguments:
  60. None
  61. Return Value:
  62. TRUE if initialization was successful
  63. --*/
  64. {
  65. CLONG i;
  66. ULONG Index;
  67. PGENERAL_LOOKASIDE Lookaside;
  68. USHORT NumberOfItems;
  69. PKPRCB Prcb;
  70. PWORK_QUEUE_ITEM WorkItem;
  71. #ifdef CCDBG_LOCK
  72. KeInitializeSpinLock( &CcDebugTraceLock );
  73. #endif
  74. #if DBG
  75. CcBcbCount = 0;
  76. InitializeListHead( &CcBcbList );
  77. #endif
  78. //
  79. // Figure out the timeout clock tick for the lazy writer.
  80. //
  81. CcIdleDelayTick = LAZY_WRITER_IDLE_DELAY / KeQueryTimeIncrement();
  82. //
  83. // Initialize shared cache map list structures
  84. //
  85. InitializeListHead( &CcCleanSharedCacheMapList );
  86. InitializeListHead( &CcDirtySharedCacheMapList.SharedCacheMapLinks );
  87. CcDirtySharedCacheMapList.Flags = IS_CURSOR;
  88. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  89. &CcLazyWriterCursor.SharedCacheMapLinks );
  90. CcLazyWriterCursor.Flags = IS_CURSOR;
  91. //
  92. // Initialize worker thread structures
  93. //
  94. InitializeListHead( &CcIdleWorkerThreadList );
  95. InitializeListHead( &CcExpressWorkQueue );
  96. InitializeListHead( &CcRegularWorkQueue );
  97. InitializeListHead( &CcPostTickWorkQueue );
  98. //
  99. // Set the number of worker threads based on the system size.
  100. //
  101. CcCapturedSystemSize = MmQuerySystemSize();
  102. if (CcNumberWorkerThreads == 0) {
  103. switch (CcCapturedSystemSize) {
  104. case MmSmallSystem:
  105. CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
  106. CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
  107. CcAggressiveZeroThreshold = 1;
  108. break;
  109. case MmMediumSystem:
  110. CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
  111. CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4;
  112. CcAggressiveZeroThreshold = 2;
  113. break;
  114. case MmLargeSystem:
  115. CcNumberWorkerThreads = ExCriticalWorkerThreads - 2;
  116. CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4 +
  117. MmNumberOfPhysicalPages / 8;
  118. CcAggressiveZeroThreshold = 4;
  119. break;
  120. default:
  121. CcNumberWorkerThreads = 1;
  122. CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
  123. }
  124. if (MmSystemCacheWs.MaximumWorkingSetSize > ((4*1024*1024)/PAGE_SIZE)) {
  125. CcDirtyPageThreshold = (ULONG)(MmSystemCacheWs.MaximumWorkingSetSize -
  126. ((2*1024*1024)/PAGE_SIZE));
  127. }
  128. CcDirtyPageTarget = CcDirtyPageThreshold / 2 +
  129. CcDirtyPageThreshold / 4;
  130. }
  131. CcAggressiveZeroCount = 0;
  132. //
  133. // Now allocate and initialize the above number of worker thread
  134. // items.
  135. //
  136. for (i = 0; i < CcNumberWorkerThreads; i++) {
  137. WorkItem = ExAllocatePoolWithTag( NonPagedPool, sizeof(WORK_QUEUE_ITEM), 'qWcC' );
  138. if (WorkItem == NULL) {
  139. CcBugCheck( 0, 0, 0 );
  140. }
  141. //
  142. // Initialize the work queue item and insert in our queue
  143. // of potential worker threads.
  144. //
  145. ExInitializeWorkItem( WorkItem, CcWorkerThread, WorkItem );
  146. InsertTailList( &CcIdleWorkerThreadList, &WorkItem->List );
  147. }
  148. //
  149. // Initialize the Lazy Writer thread structure, and start him up.
  150. //
  151. RtlZeroMemory( &LazyWriter, sizeof(LAZY_WRITER) );
  152. InitializeListHead( &LazyWriter.WorkQueue );
  153. //
  154. // Initialize the Scan Dpc and Timer.
  155. //
  156. KeInitializeDpc( &LazyWriter.ScanDpc, &CcScanDpc, NULL );
  157. KeInitializeTimer( &LazyWriter.ScanTimer );
  158. //
  159. // Now initialize the lookaside list for allocating Work Queue entries.
  160. //
  161. switch ( CcCapturedSystemSize ) {
  162. //
  163. // ~512 bytes
  164. //
  165. case MmSmallSystem :
  166. NumberOfItems = 32;
  167. break;
  168. //
  169. // ~1k bytes
  170. //
  171. case MmMediumSystem :
  172. NumberOfItems = 64;
  173. break;
  174. //
  175. // ~2k bytes
  176. //
  177. case MmLargeSystem :
  178. NumberOfItems = 128;
  179. if (MmIsThisAnNtAsSystem()) {
  180. NumberOfItems += 128;
  181. }
  182. break;
  183. }
  184. ExInitializeSystemLookasideList( &CcTwilightLookasideList,
  185. NonPagedPool,
  186. sizeof( WORK_QUEUE_ENTRY ),
  187. 'kWcC',
  188. NumberOfItems,
  189. &ExSystemLookasideListHead );
  190. //
  191. // Initialize the per processor nonpaged lookaside lists and descriptors.
  192. //
  193. for (Index = 0; Index < (ULONG)KeNumberProcessors; Index += 1) {
  194. Prcb = KiProcessorBlock[Index];
  195. //
  196. // Initialize the large IRP per processor lookaside pointers.
  197. //
  198. Prcb->PPLookasideList[LookasideTwilightList].L = &CcTwilightLookasideList;
  199. Lookaside = ExAllocatePoolWithTag( NonPagedPool,
  200. sizeof(GENERAL_LOOKASIDE),
  201. 'KWcC');
  202. if (Lookaside != NULL) {
  203. ExInitializeSystemLookasideList( Lookaside,
  204. NonPagedPool,
  205. sizeof( WORK_QUEUE_ENTRY ),
  206. 'KWcC',
  207. NumberOfItems,
  208. &ExSystemLookasideListHead );
  209. } else {
  210. Lookaside = &CcTwilightLookasideList;
  211. }
  212. Prcb->PPLookasideList[LookasideTwilightList].P = Lookaside;
  213. }
  214. //
  215. // Initialize the Deferred Write List.
  216. //
  217. KeInitializeSpinLock( &CcDeferredWriteSpinLock );
  218. InitializeListHead( &CcDeferredWrites );
  219. //
  220. // Initialize the Vacbs.
  221. //
  222. CcInitializeVacbs();
  223. return TRUE;
  224. }
  225. VOID
  226. CcInitializeCacheMap (
  227. IN PFILE_OBJECT FileObject,
  228. IN PCC_FILE_SIZES FileSizes,
  229. IN BOOLEAN PinAccess,
  230. IN PCACHE_MANAGER_CALLBACKS Callbacks,
  231. IN PVOID LazyWriteContext
  232. )
  233. /*++
  234. Routine Description:
  235. This routine is intended to be called by File Systems only. It
  236. initializes the cache maps for data caching. It should be called
  237. every time a file is opened or created, and NO_INTERMEDIATE_BUFFERING
  238. was specified as FALSE.
  239. Arguments:
  240. FileObject - A pointer to the newly-created file object.
  241. FileSizes - A pointer to AllocationSize, FileSize and ValidDataLength
  242. for the file. ValidDataLength should contain MAXLONGLONG if
  243. valid data length tracking and callbacks are not desired.
  244. PinAccess - FALSE if file will be used exclusively for Copy and Mdl
  245. access, or TRUE if file will be used for Pin access.
  246. (Files for Pin access are not limited in size as the caller
  247. must access multiple areas of the file at once.)
  248. Callbacks - Structure of callbacks used by the Lazy Writer
  249. LazyWriteContext - Parameter to be passed in to above routine.
  250. Return Value:
  251. None. If an error occurs, this routine will Raise the status.
  252. --*/
  253. {
  254. KIRQL OldIrql;
  255. PSHARED_CACHE_MAP SharedCacheMap;
  256. PVOID CacheMapToFree = NULL;
  257. CC_FILE_SIZES LocalSizes;
  258. LOGICAL WeSetBeingCreated = FALSE;
  259. LOGICAL SharedListOwned = FALSE;
  260. LOGICAL MustUninitialize = FALSE;
  261. LOGICAL WeCreated = FALSE;
  262. PPRIVATE_CACHE_MAP PrivateCacheMap;
  263. NTSTATUS Status = STATUS_SUCCESS;
  264. DebugTrace(+1, me, "CcInitializeCacheMap:\n", 0 );
  265. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  266. DebugTrace( 0, me, " FileSizes = %08lx\n", FileSizes );
  267. //
  268. // Make a local copy of the passed in file sizes before acquiring
  269. // the spin lock.
  270. //
  271. LocalSizes = *FileSizes;
  272. //
  273. // If no FileSize was given, set to one byte before maximizing below.
  274. //
  275. if (LocalSizes.AllocationSize.QuadPart == 0) {
  276. LocalSizes.AllocationSize.LowPart += 1;
  277. }
  278. //
  279. // If caller has Write access or will allow write, then round
  280. // size to next create modulo. (***Temp*** there may be too many
  281. // apps that end up allowing shared write, thanks to our Dos heritage,
  282. // to keep that part of the check in.)
  283. //
  284. if (FileObject->WriteAccess /*|| FileObject->SharedWrite */) {
  285. LocalSizes.AllocationSize.QuadPart = LocalSizes.AllocationSize.QuadPart + (LONGLONG)(DEFAULT_CREATE_MODULO - 1);
  286. LocalSizes.AllocationSize.LowPart &= ~(DEFAULT_CREATE_MODULO - 1);
  287. } else {
  288. LocalSizes.AllocationSize.QuadPart = LocalSizes.AllocationSize.QuadPart + (LONGLONG)(VACB_MAPPING_GRANULARITY - 1);
  289. LocalSizes.AllocationSize.LowPart &= ~(VACB_MAPPING_GRANULARITY - 1);
  290. }
  291. //
  292. // Do the allocate of the SharedCacheMap, based on an unsafe test,
  293. // while not holding a spinlock. If the allocation fails, it's ok
  294. // to fail the request even though the test was unsafe.
  295. //
  296. if (FileObject->SectionObjectPointer->SharedCacheMap == NULL) {
  297. restart:
  298. ASSERT (CacheMapToFree == NULL);
  299. SharedCacheMap = ExAllocatePoolWithTag( NonPagedPool, sizeof(SHARED_CACHE_MAP), 'cScC' );
  300. if (SharedCacheMap == NULL) {
  301. DebugTrace( 0, 0, "Failed to allocate SharedCacheMap\n", 0 );
  302. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  303. }
  304. //
  305. // Stash a copy of it so we can free it in the error path below.
  306. //
  307. CacheMapToFree = SharedCacheMap;
  308. //
  309. // Zero the SharedCacheMap and fill in the nonzero portions later.
  310. //
  311. RtlZeroMemory( SharedCacheMap, sizeof(SHARED_CACHE_MAP) );
  312. #if OPEN_COUNT_LOG
  313. SharedCacheMap->OpenCountLog.Size = sizeof(SharedCacheMap->OpenCountLog.Log)/sizeof(CC_OPEN_COUNT_LOG_ENTRY);
  314. #endif
  315. //
  316. // Now initialize the Shared Cache Map.
  317. //
  318. SharedCacheMap->NodeTypeCode = CACHE_NTC_SHARED_CACHE_MAP;
  319. SharedCacheMap->NodeByteSize = sizeof(SHARED_CACHE_MAP);
  320. SharedCacheMap->FileObject = FileObject;
  321. SharedCacheMap->FileSize = LocalSizes.FileSize;
  322. SharedCacheMap->ValidDataLength = LocalSizes.ValidDataLength;
  323. SharedCacheMap->ValidDataGoal = LocalSizes.ValidDataLength;
  324. // SharedCacheMap->Section set below
  325. //
  326. // Initialize the spin locks.
  327. //
  328. KeInitializeSpinLock( &SharedCacheMap->ActiveVacbSpinLock );
  329. KeInitializeSpinLock( &SharedCacheMap->BcbSpinLock );
  330. ExInitializePushLock( &SharedCacheMap->VacbPushLock );
  331. if (PinAccess) {
  332. SetFlag(SharedCacheMap->Flags, PIN_ACCESS);
  333. }
  334. //
  335. // If this file has FO_SEQUENTIAL_ONLY set, then remember that
  336. // in the SharedCacheMap.
  337. //
  338. if (FlagOn(FileObject->Flags, FO_SEQUENTIAL_ONLY)) {
  339. SetFlag(SharedCacheMap->Flags, ONLY_SEQUENTIAL_ONLY_SEEN);
  340. }
  341. //
  342. // Do the round-robin allocation of the spinlock for the shared
  343. // cache map. Note the manipulation of the next
  344. // counter is safe, since we have the CcMasterSpinLock
  345. // exclusive.
  346. //
  347. InitializeListHead( &SharedCacheMap->BcbList );
  348. SharedCacheMap->Callbacks = Callbacks;
  349. SharedCacheMap->LazyWriteContext = LazyWriteContext;
  350. //
  351. // Initialize listhead for all PrivateCacheMaps
  352. //
  353. InitializeListHead( &SharedCacheMap->PrivateList );
  354. }
  355. //
  356. // Serialize Creation/Deletion of all Shared CacheMaps
  357. //
  358. SharedListOwned = TRUE;
  359. CcAcquireMasterLock( &OldIrql );
  360. //
  361. // Check for second initialization of same file object
  362. //
  363. if (FileObject->PrivateCacheMap != NULL) {
  364. DebugTrace( 0, 0, "CacheMap already initialized\n", 0 );
  365. CcReleaseMasterLock( OldIrql );
  366. if (CacheMapToFree != NULL) {
  367. ExFreePool(CacheMapToFree);
  368. }
  369. DebugTrace(-1, me, "CcInitializeCacheMap -> VOID\n", 0 );
  370. return;
  371. }
  372. //
  373. // Get current Shared Cache Map pointer indirectly off of the file object.
  374. // (The actual pointer is typically in a file system data structure, such
  375. // as an Fcb.)
  376. //
  377. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  378. //
  379. // If there is no SharedCacheMap, then we must create a section and
  380. // the SharedCacheMap structure.
  381. //
  382. if (SharedCacheMap == NULL) {
  383. //
  384. // Insert the new SharedCacheMap.
  385. //
  386. if (CacheMapToFree == NULL) {
  387. CcReleaseMasterLock( OldIrql );
  388. SharedListOwned = FALSE;
  389. goto restart;
  390. }
  391. SharedCacheMap = CacheMapToFree;
  392. CacheMapToFree = NULL;
  393. //
  394. // Insert the new Shared Cache Map in the global list
  395. //
  396. InsertTailList( &CcCleanSharedCacheMapList,
  397. &SharedCacheMap->SharedCacheMapLinks );
  398. WeCreated = TRUE;
  399. //
  400. // Finally, store the pointer to the Shared Cache Map back
  401. // via the indirect pointer in the File Object.
  402. //
  403. FileObject->SectionObjectPointer->SharedCacheMap = SharedCacheMap;
  404. //
  405. // We must reference this file object so that it cannot go away
  406. // until we do CcUninitializeCacheMap below. Note we cannot
  407. // find or rely on the FileObject that Memory Management has,
  408. // although normally it will be this same one anyway.
  409. //
  410. ObReferenceObject ( FileObject );
  411. } else {
  412. //
  413. // If this file has FO_SEQUENTIAL_ONLY clear, then remember that
  414. // in the SharedCacheMap.
  415. //
  416. if (!FlagOn(FileObject->Flags, FO_SEQUENTIAL_ONLY)) {
  417. ClearFlag(SharedCacheMap->Flags, ONLY_SEQUENTIAL_ONLY_SEEN);
  418. }
  419. }
  420. //
  421. // If this file is opened for random access, remember this in
  422. // the SharedCacheMap.
  423. //
  424. if (FlagOn(FileObject->Flags, FO_RANDOM_ACCESS)) {
  425. SetFlag(SharedCacheMap->Flags, RANDOM_ACCESS_SEEN);
  426. }
  427. //
  428. // Make sure that no one is trying to lazy delete it in the case
  429. // that the Cache Map was already there.
  430. //
  431. ClearFlag(SharedCacheMap->Flags, TRUNCATE_REQUIRED);
  432. //
  433. // In case there has been a CcUnmapAndPurge call, we check here if we
  434. // if we need to recreate the section and map it.
  435. //
  436. if ((SharedCacheMap->Vacbs == NULL) &&
  437. !FlagOn(SharedCacheMap->Flags, BEING_CREATED)) {
  438. //
  439. // Increment the OpenCount on the CacheMap.
  440. //
  441. CcIncrementOpenCount( SharedCacheMap, 'onnI' );
  442. //
  443. // We still want anyone else to wait.
  444. //
  445. SetFlag(SharedCacheMap->Flags, BEING_CREATED);
  446. //
  447. // If there is a create event, then this must be the path where we
  448. // we were only unmapped. We will just clear it here again in case
  449. // someone needs to wait again this time too.
  450. //
  451. if (SharedCacheMap->CreateEvent != NULL) {
  452. KeInitializeEvent( SharedCacheMap->CreateEvent,
  453. NotificationEvent,
  454. FALSE );
  455. }
  456. //
  457. // Release global resource
  458. //
  459. CcReleaseMasterLock( OldIrql );
  460. SharedListOwned = FALSE;
  461. //
  462. // Signify we have incremented the open count.
  463. //
  464. MustUninitialize = TRUE;
  465. //
  466. // Signify we have marked BEING_CREATED in the CacheMap flags.
  467. //
  468. WeSetBeingCreated = TRUE;
  469. //
  470. // We have to test this, because the section may only be unmapped.
  471. //
  472. if (SharedCacheMap->Section == NULL) {
  473. //
  474. // Call MM to create a section for this file, for the calculated
  475. // section size. Note that we have the choice in this service to
  476. // pass in a FileHandle or a FileObject pointer, but not both.
  477. // Use the pointer as it results in much faster performance.
  478. //
  479. DebugTrace( 0, mm, "MmCreateSection:\n", 0 );
  480. DebugTrace2(0, mm, " MaximumSize = %08lx, %08lx\n",
  481. LocalSizes.AllocationSize.LowPart,
  482. LocalSizes.AllocationSize.HighPart );
  483. DebugTrace( 0, mm, " FileObject = %08lx\n", FileObject );
  484. SharedCacheMap->Status = MmCreateSection( &SharedCacheMap->Section,
  485. SECTION_MAP_READ
  486. | SECTION_MAP_WRITE
  487. | SECTION_QUERY,
  488. NULL,
  489. &LocalSizes.AllocationSize,
  490. PAGE_READWRITE,
  491. SEC_COMMIT,
  492. NULL,
  493. FileObject );
  494. DebugTrace( 0, mm, " <Section = %08lx\n", SharedCacheMap->Section );
  495. if (!NT_SUCCESS( SharedCacheMap->Status )){
  496. DebugTrace( 0, 0, "Error from MmCreateSection = %08lx\n",
  497. SharedCacheMap->Status );
  498. SharedCacheMap->Section = NULL;
  499. Status = FsRtlNormalizeNtstatus( SharedCacheMap->Status,
  500. STATUS_UNEXPECTED_MM_CREATE_ERR );
  501. goto exitfinally;
  502. }
  503. ObDeleteCapturedInsertInfo(SharedCacheMap->Section);
  504. //
  505. // If this is a stream file object, then no user can map it,
  506. // and we should keep the modified page writer out of it.
  507. //
  508. if (!FlagOn(((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->Flags2,
  509. FSRTL_FLAG2_DO_MODIFIED_WRITE) &&
  510. (FileObject->FsContext2 == NULL)) {
  511. MmDisableModifiedWriteOfSection( FileObject->SectionObjectPointer );
  512. CcAcquireMasterLock( &OldIrql );
  513. SetFlag(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED);
  514. CcReleaseMasterLock( OldIrql );
  515. }
  516. //
  517. // Create the Vacb array.
  518. //
  519. Status = CcCreateVacbArray( SharedCacheMap, LocalSizes.AllocationSize );
  520. if (!NT_SUCCESS(Status)) {
  521. goto exitfinally;
  522. }
  523. }
  524. //
  525. // If the section already exists, we still have to call MM to
  526. // extend, in case it is not large enough.
  527. //
  528. else {
  529. if ( LocalSizes.AllocationSize.QuadPart > SharedCacheMap->SectionSize.QuadPart ) {
  530. DebugTrace( 0, mm, "MmExtendSection:\n", 0 );
  531. DebugTrace( 0, mm, " Section = %08lx\n", SharedCacheMap->Section );
  532. DebugTrace2(0, mm, " Size = %08lx, %08lx\n",
  533. LocalSizes.AllocationSize.LowPart,
  534. LocalSizes.AllocationSize.HighPart );
  535. Status = MmExtendSection( SharedCacheMap->Section,
  536. &LocalSizes.AllocationSize,
  537. TRUE );
  538. if (!NT_SUCCESS(Status)) {
  539. DebugTrace( 0, 0, "Error from MmExtendSection, Status = %08lx\n",
  540. Status );
  541. Status = FsRtlNormalizeNtstatus( Status,
  542. STATUS_UNEXPECTED_MM_EXTEND_ERR );
  543. goto exitfinally;
  544. }
  545. }
  546. //
  547. // Extend the Vacb array.
  548. //
  549. Status = CcExtendVacbArray( SharedCacheMap, LocalSizes.AllocationSize );
  550. if (!NT_SUCCESS(Status)) {
  551. goto exitfinally;
  552. }
  553. }
  554. //
  555. // Now show that we are all done and resume any waiters.
  556. //
  557. CcAcquireMasterLock( &OldIrql );
  558. ClearFlag(SharedCacheMap->Flags, BEING_CREATED);
  559. if (SharedCacheMap->CreateEvent != NULL) {
  560. KeSetEvent( SharedCacheMap->CreateEvent, 0, FALSE );
  561. }
  562. CcReleaseMasterLock( OldIrql );
  563. WeSetBeingCreated = FALSE;
  564. }
  565. //
  566. // Else if the section is already there, we make sure it is large
  567. // enough by calling CcExtendCacheSection.
  568. //
  569. else {
  570. //
  571. // If the SharedCacheMap is currently being created we have
  572. // to optionally create and wait on an event for it. Note that
  573. // the only safe time to delete the event is in
  574. // CcUninitializeCacheMap, because we otherwise have no way of
  575. // knowing when everyone has reached the KeWaitForSingleObject.
  576. //
  577. if (FlagOn(SharedCacheMap->Flags, BEING_CREATED)) {
  578. if (SharedCacheMap->CreateEvent == NULL) {
  579. SharedCacheMap->CreateEvent = (PKEVENT)ExAllocatePoolWithTag( NonPagedPool,
  580. sizeof(KEVENT),
  581. 'vEcC' );
  582. if (SharedCacheMap->CreateEvent == NULL) {
  583. DebugTrace( 0, 0, "Failed to allocate CreateEvent\n", 0 );
  584. CcReleaseMasterLock( OldIrql );
  585. SharedListOwned = FALSE;
  586. Status = STATUS_INSUFFICIENT_RESOURCES;
  587. goto exitfinally;
  588. }
  589. KeInitializeEvent( SharedCacheMap->CreateEvent,
  590. NotificationEvent,
  591. FALSE );
  592. }
  593. //
  594. // Increment the OpenCount on the CacheMap.
  595. //
  596. CcIncrementOpenCount( SharedCacheMap, 'ecnI' );
  597. //
  598. // Release global resource before waiting
  599. //
  600. CcReleaseMasterLock( OldIrql );
  601. SharedListOwned = FALSE;
  602. MustUninitialize = TRUE;
  603. DebugTrace( 0, 0, "Waiting on CreateEvent\n", 0 );
  604. KeWaitForSingleObject( SharedCacheMap->CreateEvent,
  605. Executive,
  606. KernelMode,
  607. FALSE,
  608. (PLARGE_INTEGER)NULL);
  609. //
  610. // If the real creator got an error, then we must bomb
  611. // out too.
  612. //
  613. if (!NT_SUCCESS(SharedCacheMap->Status)) {
  614. Status = FsRtlNormalizeNtstatus( SharedCacheMap->Status,
  615. STATUS_UNEXPECTED_MM_CREATE_ERR );
  616. goto exitfinally;
  617. }
  618. }
  619. else {
  620. PCACHE_UNINITIALIZE_EVENT CUEvent, EventNext;
  621. //
  622. // Increment the OpenCount on the CacheMap.
  623. //
  624. CcIncrementOpenCount( SharedCacheMap, 'esnI' );
  625. //
  626. // If there is a process waiting on an uninitialize on this
  627. // cache map to complete, let the thread that is waiting go,
  628. // since the uninitialize is now complete.
  629. //
  630. CUEvent = SharedCacheMap->UninitializeEvent;
  631. while (CUEvent != NULL) {
  632. EventNext = CUEvent->Next;
  633. KeSetEvent(&CUEvent->Event, 0, FALSE);
  634. CUEvent = EventNext;
  635. }
  636. SharedCacheMap->UninitializeEvent = NULL;
  637. //
  638. // Release global resource
  639. //
  640. CcReleaseMasterLock( OldIrql );
  641. SharedListOwned = FALSE;
  642. MustUninitialize = TRUE;
  643. }
  644. }
  645. if (CacheMapToFree != NULL) {
  646. ExFreePool( CacheMapToFree );
  647. CacheMapToFree = NULL;
  648. }
  649. //
  650. // Now allocate (if local one already in use) and initialize
  651. // the Private Cache Map.
  652. //
  653. PrivateCacheMap = &SharedCacheMap->PrivateCacheMap;
  654. //
  655. // See if we should allocate a PrivateCacheMap while not holding
  656. // a spinlock.
  657. //
  658. if (PrivateCacheMap->NodeTypeCode != 0) {
  659. restart2:
  660. CacheMapToFree = ExAllocatePoolWithTag( NonPagedPool, sizeof(PRIVATE_CACHE_MAP), 'cPcC' );
  661. if (CacheMapToFree == NULL) {
  662. DebugTrace( 0, 0, "Failed to allocate PrivateCacheMap\n", 0 );
  663. Status = STATUS_INSUFFICIENT_RESOURCES;
  664. goto exitfinally;
  665. }
  666. }
  667. //
  668. // Insert the new PrivateCacheMap in the list off the SharedCacheMap.
  669. //
  670. SharedListOwned = TRUE;
  671. CcAcquireMasterLock( &OldIrql );
  672. //
  673. // Now make sure there is still no PrivateCacheMap, and if so just get out.
  674. //
  675. if (FileObject->PrivateCacheMap == NULL) {
  676. //
  677. // Is the local one already in use?
  678. //
  679. if (PrivateCacheMap->NodeTypeCode != 0) {
  680. //
  681. // Use the one allocated above, if there is one, else go to pool now.
  682. //
  683. if (CacheMapToFree == NULL) {
  684. CcReleaseMasterLock( OldIrql );
  685. SharedListOwned = FALSE;
  686. goto restart2;
  687. }
  688. PrivateCacheMap = CacheMapToFree;
  689. CacheMapToFree = NULL;
  690. }
  691. RtlZeroMemory( PrivateCacheMap, sizeof(PRIVATE_CACHE_MAP) );
  692. PrivateCacheMap->NodeTypeCode = CACHE_NTC_PRIVATE_CACHE_MAP;
  693. PrivateCacheMap->FileObject = FileObject;
  694. PrivateCacheMap->ReadAheadMask = PAGE_SIZE - 1;
  695. //
  696. // Initialize the spin lock.
  697. //
  698. KeInitializeSpinLock( &PrivateCacheMap->ReadAheadSpinLock );
  699. InsertTailList( &SharedCacheMap->PrivateList, &PrivateCacheMap->PrivateLinks );
  700. FileObject->PrivateCacheMap = PrivateCacheMap;
  701. } else {
  702. //
  703. // We raced with another initializer for the same fileobject and must
  704. // drop our (to this point speculative) opencount.
  705. //
  706. ASSERT( SharedCacheMap->OpenCount > 1 );
  707. CcDecrementOpenCount( SharedCacheMap, 'rpnI' );
  708. SharedCacheMap = NULL;
  709. }
  710. MustUninitialize = FALSE;
  711. exitfinally:
  712. //
  713. // See if we got an error and must uninitialize the SharedCacheMap
  714. //
  715. if (MustUninitialize) {
  716. if (!SharedListOwned) {
  717. CcAcquireMasterLock( &OldIrql );
  718. }
  719. if (WeSetBeingCreated) {
  720. if (SharedCacheMap->CreateEvent != NULL) {
  721. KeSetEvent( SharedCacheMap->CreateEvent, 0, FALSE );
  722. }
  723. ClearFlag(SharedCacheMap->Flags, BEING_CREATED);
  724. }
  725. //
  726. // Now release our open count.
  727. //
  728. CcDecrementOpenCount( SharedCacheMap, 'umnI' );
  729. if ((SharedCacheMap->OpenCount == 0) &&
  730. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  731. (SharedCacheMap->DirtyPages == 0)) {
  732. //
  733. // It is neccesary to eliminate the structure now. We should
  734. // be guaranteed that our dereference will not result in close
  735. // due to the caller's reference on the fileobject, unlike the
  736. // comment in the original code, below, would indicate.
  737. //
  738. // Not removing this structure can result in problems if the file
  739. // is also mapped and the mapped page writer extends VDL. An FS
  740. // will use CcSetFileSizes and cause us to issue a recursive flush
  741. // of the same range, resulting in a self-colliding page flush and
  742. // a deadlock.
  743. //
  744. // We also think that file extension/truncation in the interim
  745. // (if the section create failed) would result in an inconsistent
  746. // "resurrected" cache map if we managed to use the one we have
  747. // now. Note CcSetFileSizes aborts if the section is NULL.
  748. //
  749. CcDeleteSharedCacheMap( SharedCacheMap, OldIrql, FALSE );
  750. #if 0
  751. //
  752. // On PinAccess it is safe and necessary to eliminate
  753. // the structure immediately.
  754. //
  755. if (PinAccess) {
  756. CcDeleteSharedCacheMap( SharedCacheMap, OldIrql, FALSE );
  757. //
  758. // If it is not PinAccess, we must lazy delete, because
  759. // we could get into a deadlock trying to acquire the
  760. // stream exclusive when we dereference the file object.
  761. //
  762. } else {
  763. //
  764. // Move it to the dirty list so the lazy write scan will
  765. // see it.
  766. //
  767. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  768. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  769. &SharedCacheMap->SharedCacheMapLinks );
  770. //
  771. // Make sure the Lazy Writer will wake up, because we
  772. // want him to delete this SharedCacheMap.
  773. //
  774. LazyWriter.OtherWork = TRUE;
  775. if (!LazyWriter.ScanActive) {
  776. CcScheduleLazyWriteScan( FALSE );
  777. }
  778. CcReleaseMasterLock( OldIrql );
  779. }
  780. #endif
  781. } else {
  782. CcReleaseMasterLock( OldIrql );
  783. }
  784. SharedListOwned = FALSE;
  785. //
  786. // If we did not create this SharedCacheMap, then there is a
  787. // possibility that it is in the dirty list. Once we are sure
  788. // we have the spinlock, just make sure it is in the clean list
  789. // if there are no dirty bytes and the open count is nonzero.
  790. // (The latter test is almost guaranteed, of course, but we check
  791. // it to be safe.)
  792. //
  793. } else if (!WeCreated &&
  794. (SharedCacheMap != NULL)) {
  795. if (!SharedListOwned) {
  796. CcAcquireMasterLock( &OldIrql );
  797. SharedListOwned = TRUE;
  798. }
  799. if ((SharedCacheMap->DirtyPages == 0) &&
  800. (SharedCacheMap->OpenCount != 0)) {
  801. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  802. InsertTailList( &CcCleanSharedCacheMapList,
  803. &SharedCacheMap->SharedCacheMapLinks );
  804. }
  805. }
  806. //
  807. // Release global resource
  808. //
  809. if (SharedListOwned) {
  810. CcReleaseMasterLock( OldIrql );
  811. }
  812. if (CacheMapToFree != NULL) {
  813. ExFreePool(CacheMapToFree);
  814. }
  815. if (!NT_SUCCESS(Status)) {
  816. DebugTrace(-1, me, "CcInitializeCacheMap -> RAISING EXCEPTION\n", 0 );
  817. ExRaiseStatus(Status);
  818. }
  819. DebugTrace(-1, me, "CcInitializeCacheMap -> VOID\n", 0 );
  820. return;
  821. }
  822. BOOLEAN
  823. CcUninitializeCacheMap (
  824. IN PFILE_OBJECT FileObject,
  825. IN PLARGE_INTEGER TruncateSize OPTIONAL,
  826. IN PCACHE_UNINITIALIZE_EVENT UninitializeEvent OPTIONAL
  827. )
  828. /*++
  829. Routine Description:
  830. This routine uninitializes the previously initialized Shared and Private
  831. Cache Maps. This routine is only intended to be called by File Systems.
  832. It should be called when the File System receives a cleanup call on the
  833. File Object.
  834. A File System which supports data caching must always call this routine
  835. whenever it closes a file, whether the caller opened the file with
  836. NO_INTERMEDIATE_BUFFERING as FALSE or not. This is because the final
  837. cleanup of a file related to truncation or deletion of the file, can
  838. only occur on the last close, whether the last closer cached the file
  839. or not. When CcUnitializeCacheMap is called on a file object for which
  840. CcInitializeCacheMap was never called, the call has a benign effect
  841. iff no one has truncated or deleted the file; otherwise the necessary
  842. cleanup relating to the truncate or close is performed.
  843. In summary, CcUnitializeCacheMap does the following:
  844. If the caller had Write or Delete access, the cache is flushed.
  845. (This could change with lazy writing.)
  846. If a Cache Map was initialized on this File Object, it is
  847. unitialized (unmap any views, delete section, and delete
  848. Cache Map structures).
  849. On the last Cleanup, if the file has been deleted, the
  850. Section is forced closed. If the file has been truncated, then
  851. the truncated pages are purged from the cache.
  852. Arguments:
  853. FileObject - File Object which was previously supplied to
  854. CcInitializeCacheMap.
  855. TruncateSize - If specified, the file was truncated to the specified
  856. size, and the cache should be purged accordingly.
  857. UninitializeEvent - If specified, then the provided event will be set
  858. to the signalled state when the actual flush is
  859. completed. This is only of interest to file systems
  860. that require that they be notified when a cache flush
  861. operation has completed. Due to network protocol
  862. restrictions, it is critical that network file
  863. systems know exactly when a cache flush operation
  864. completes, by specifying this event, they can be
  865. notified when the cache section is finally purged
  866. if the section is "lazy-deleted".
  867. ReturnValue:
  868. FALSE if Section was not closed.
  869. TRUE if Section was closed.
  870. --*/
  871. {
  872. KIRQL OldIrql;
  873. PSHARED_CACHE_MAP SharedCacheMap;
  874. ULONG ActivePage;
  875. ULONG PageIsDirty;
  876. PVACB ActiveVacb = NULL;
  877. BOOLEAN SectionClosed = FALSE;
  878. PPRIVATE_CACHE_MAP PrivateCacheMap;
  879. DebugTrace(+1, me, "CcUninitializeCacheMap:\n", 0 );
  880. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  881. DebugTrace( 0, me, " &TruncateSize = %08lx\n", TruncateSize );
  882. //
  883. // Serialize Creation/Deletion of all Shared CacheMaps
  884. //
  885. CcAcquireMasterLock( &OldIrql );
  886. //
  887. // Get pointer to SharedCacheMap via File Object.
  888. //
  889. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  890. PrivateCacheMap = FileObject->PrivateCacheMap;
  891. //
  892. // Decrement Open Count on SharedCacheMap, if we did a cached open.
  893. // Also unmap PrivateCacheMap if it is mapped and deallocate it.
  894. //
  895. if (PrivateCacheMap != NULL) {
  896. ASSERT( PrivateCacheMap->FileObject == FileObject );
  897. CcDecrementOpenCount( SharedCacheMap, 'ninU' );
  898. //
  899. // Remove PrivateCacheMap from list in SharedCacheMap.
  900. //
  901. RemoveEntryList( &PrivateCacheMap->PrivateLinks );
  902. //
  903. // Free local or allocated PrivateCacheMap
  904. //
  905. if (PrivateCacheMap == &SharedCacheMap->PrivateCacheMap) {
  906. PrivateCacheMap->NodeTypeCode = 0;
  907. PrivateCacheMap = NULL;
  908. }
  909. FileObject->PrivateCacheMap = (PPRIVATE_CACHE_MAP)NULL;
  910. }
  911. //
  912. // Now if we have a SharedCacheMap whose Open Count went to 0, we
  913. // have some additional cleanup.
  914. //
  915. if (SharedCacheMap != NULL) {
  916. //
  917. // If a Truncate Size was specified, then remember that we want to
  918. // truncate the FileSize and purge the unneeded pages when OpenCount
  919. // goes to 0.
  920. //
  921. if (ARGUMENT_PRESENT(TruncateSize)) {
  922. if ( (TruncateSize->QuadPart == 0) && (SharedCacheMap->FileSize.QuadPart != 0) ) {
  923. SetFlag(SharedCacheMap->Flags, TRUNCATE_REQUIRED);
  924. } else if (IsListEmpty(&SharedCacheMap->PrivateList)) {
  925. //
  926. // If this is the last guy, I can drop the file size down
  927. // now.
  928. //
  929. SharedCacheMap->FileSize = *TruncateSize;
  930. }
  931. }
  932. //
  933. // If other file objects are still using this SharedCacheMap,
  934. // then we are done now.
  935. //
  936. if (SharedCacheMap->OpenCount != 0) {
  937. DebugTrace(-1, me, "SharedCacheMap OpenCount != 0\n", 0);
  938. //
  939. // If the caller specified an event to be set when
  940. // the cache uninitialize is completed, set the event
  941. // now, because the uninitialize is complete for this file.
  942. // (Note, we make him wait if he is the last guy.)
  943. //
  944. if (ARGUMENT_PRESENT(UninitializeEvent)) {
  945. if (!IsListEmpty(&SharedCacheMap->PrivateList)) {
  946. KeSetEvent(&UninitializeEvent->Event, 0, FALSE);
  947. } else {
  948. UninitializeEvent->Next = SharedCacheMap->UninitializeEvent;
  949. SharedCacheMap->UninitializeEvent = UninitializeEvent;
  950. }
  951. }
  952. CcReleaseMasterLock( OldIrql );
  953. //
  954. // Free PrivateCacheMap now that we no longer have the spinlock.
  955. //
  956. if (PrivateCacheMap != NULL) {
  957. ExFreePool( PrivateCacheMap );
  958. }
  959. DebugTrace(-1, me, "CcUnitializeCacheMap -> %02lx\n", FALSE );
  960. return FALSE;
  961. }
  962. //
  963. // Remove the private write flag synchronously. Even though a
  964. // private writer is also opening the file exclusively, the
  965. // shared cache map is not going away synchronously and we
  966. // cannot let a non private writer re-reference the scm in
  967. // this state. Their data will never be written!
  968. //
  969. if (FlagOn(SharedCacheMap->Flags, PRIVATE_WRITE)) {
  970. ClearFlag(SharedCacheMap->Flags, PRIVATE_WRITE | DISABLE_WRITE_BEHIND);
  971. MmEnableModifiedWriteOfSection( FileObject->SectionObjectPointer );
  972. }
  973. //
  974. // The private cache map list better be empty!
  975. //
  976. ASSERT(IsListEmpty(&SharedCacheMap->PrivateList));
  977. //
  978. // Set the "uninitialize complete" in the shared cache map
  979. // so that CcDeleteSharedCacheMap will delete it.
  980. //
  981. if (ARGUMENT_PRESENT(UninitializeEvent)) {
  982. UninitializeEvent->Next = SharedCacheMap->UninitializeEvent;
  983. SharedCacheMap->UninitializeEvent = UninitializeEvent;
  984. }
  985. //
  986. // We are in the process of deleting this cache map. If the
  987. // Lazy Writer is active or the Bcb list is not empty or the Lazy
  988. // Writer will hit this SharedCacheMap because we are purging
  989. // the file to 0, then get out and let the Lazy Writer clean
  990. // up.
  991. //
  992. if ((!FlagOn(SharedCacheMap->Flags, PIN_ACCESS) &&
  993. !ARGUMENT_PRESENT(UninitializeEvent))
  994. ||
  995. FlagOn(SharedCacheMap->Flags, WRITE_QUEUED)
  996. ||
  997. (SharedCacheMap->DirtyPages != 0)) {
  998. //
  999. // Move it to the dirty list so the lazy write scan will
  1000. // see it.
  1001. //
  1002. if (!FlagOn(SharedCacheMap->Flags, WRITE_QUEUED)) {
  1003. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1004. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  1005. &SharedCacheMap->SharedCacheMapLinks );
  1006. }
  1007. //
  1008. // Make sure the Lazy Writer will wake up, because we
  1009. // want him to delete this SharedCacheMap.
  1010. //
  1011. LazyWriter.OtherWork = TRUE;
  1012. if (!LazyWriter.ScanActive) {
  1013. CcScheduleLazyWriteScan( FALSE );
  1014. }
  1015. //
  1016. // Get the active Vacb if we are going to lazy delete, to
  1017. // free it for someone who can use it.
  1018. //
  1019. GetActiveVacbAtDpcLevel( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  1020. DebugTrace(-1, me, "SharedCacheMap has Bcbs and not purging to 0\n", 0);
  1021. CcReleaseMasterLock( OldIrql );
  1022. ASSERT (SectionClosed == FALSE);
  1023. }
  1024. else {
  1025. //
  1026. // Now we can delete the SharedCacheMap. If there are any Bcbs,
  1027. // then we must be truncating to 0, and they will also be deleted.
  1028. // On return the Shared Cache Map List Spinlock will be released.
  1029. //
  1030. CcDeleteSharedCacheMap( SharedCacheMap, OldIrql, FALSE );
  1031. SectionClosed = TRUE;
  1032. }
  1033. }
  1034. //
  1035. // No Shared Cache Map. To make the file go away, we still need to
  1036. // purge the section, if one exists. (And we still need to release
  1037. // our global list first to avoid deadlocks.)
  1038. //
  1039. else {
  1040. if (ARGUMENT_PRESENT(TruncateSize) &&
  1041. ( TruncateSize->QuadPart == 0 ) &&
  1042. (*(PCHAR *)FileObject->SectionObjectPointer != NULL)) {
  1043. CcReleaseMasterLock( OldIrql );
  1044. DebugTrace( 0, mm, "MmPurgeSection:\n", 0 );
  1045. DebugTrace( 0, mm, " SectionObjectPointer = %08lx\n",
  1046. FileObject->SectionObjectPointer );
  1047. DebugTrace2(0, mm, " Offset = %08lx\n",
  1048. TruncateSize->LowPart,
  1049. TruncateSize->HighPart );
  1050. //
  1051. // 0 Length means to purge from the TruncateSize on.
  1052. //
  1053. CcPurgeCacheSection( FileObject->SectionObjectPointer,
  1054. TruncateSize,
  1055. 0,
  1056. FALSE );
  1057. }
  1058. else {
  1059. CcReleaseMasterLock( OldIrql );
  1060. }
  1061. //
  1062. // If the caller specified an event to be set when
  1063. // the cache uninitialize is completed, set the event
  1064. // now, because the uninitialize is complete for this file.
  1065. //
  1066. if (ARGUMENT_PRESENT(UninitializeEvent)) {
  1067. KeSetEvent(&UninitializeEvent->Event, 0, FALSE);
  1068. }
  1069. }
  1070. //
  1071. // Free the active vacb, if we found one.
  1072. //
  1073. if (ActiveVacb != NULL) {
  1074. CcFreeActiveVacb( ActiveVacb->SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  1075. }
  1076. //
  1077. // Free PrivateCacheMap now that we no longer have the spinlock.
  1078. //
  1079. if (PrivateCacheMap != NULL) {
  1080. ExFreePool( PrivateCacheMap );
  1081. }
  1082. DebugTrace(-1, me, "CcUnitializeCacheMap -> %02lx\n", SectionClosed );
  1083. return SectionClosed;
  1084. }
  1085. //
  1086. // Internal support routine.
  1087. //
  1088. VOID
  1089. CcDeleteBcbs (
  1090. IN PSHARED_CACHE_MAP SharedCacheMap
  1091. )
  1092. /*++
  1093. Routine Description:
  1094. This routine may be called to delete all Bcbs for a stream.
  1095. External synchronization must be acquired to guarantee no
  1096. active pin on any bcb.
  1097. Arguments:
  1098. SharedCacheMap - Pointer to SharedCacheMap.
  1099. Return Value:
  1100. None.
  1101. --*/
  1102. {
  1103. KIRQL OldIrql;
  1104. PLIST_ENTRY NextEntry;
  1105. PBCB Bcb;
  1106. //
  1107. // If there are Bcbs, then empty the list. None of them can be pinned now!
  1108. // Either the file is being truncated, in which case synchronization with
  1109. // the lazy writer must have been externally acheived, or the file is being
  1110. // closed down and nothing should be able to get a fresh reference on this
  1111. // shared cache map.
  1112. //
  1113. NextEntry = SharedCacheMap->BcbList.Flink;
  1114. while (NextEntry != &SharedCacheMap->BcbList) {
  1115. Bcb = (PBCB)CONTAINING_RECORD( NextEntry,
  1116. BCB,
  1117. BcbLinks );
  1118. NextEntry = Bcb->BcbLinks.Flink;
  1119. //
  1120. // Skip over the pendaflex entries, only removing true Bcbs
  1121. // so that level teardown doesn't need to special case unhooking
  1122. // the pendaflex. This has the side benefit of dramatically
  1123. // reducing write traffic to memory on teardown of large files.
  1124. //
  1125. if (Bcb->NodeTypeCode == CACHE_NTC_BCB) {
  1126. ASSERT( Bcb->PinCount == 0 );
  1127. RemoveEntryList( &Bcb->BcbLinks );
  1128. //
  1129. // For large metadata streams we unlock the Vacb level when
  1130. // removing. We do not need spinlocks since no other thread
  1131. // can be accessing this list when we are deleting the
  1132. // SharedCacheMap.
  1133. //
  1134. CcUnlockVacbLevel( SharedCacheMap, Bcb->FileOffset.QuadPart );
  1135. //
  1136. // There is a small window where the data could still be mapped
  1137. // if (for example) the Lazy Writer collides with a CcCopyWrite
  1138. // in the foreground, and then someone calls CcUninitializeCacheMap
  1139. // while the Lazy Writer is active. This is because the Lazy
  1140. // Writer biases the pin count. Deal with that here.
  1141. //
  1142. if (Bcb->BaseAddress != NULL) {
  1143. CcFreeVirtualAddress( Bcb->Vacb );
  1144. }
  1145. #if LIST_DBG
  1146. //
  1147. // Debug routines used to remove Bcbs from the global list
  1148. //
  1149. OldIrql = KeAcquireQueuedSpinLock( LockQueueBcbLock );
  1150. if (Bcb->CcBcbLinks.Flink != NULL) {
  1151. RemoveEntryList( &Bcb->CcBcbLinks );
  1152. CcBcbCount -= 1;
  1153. }
  1154. KeReleaseQueuedSpinLock( LockQueueBcbLock, OldIrql );
  1155. #endif
  1156. //
  1157. // If the Bcb is dirty, we have to synchronize with the Lazy Writer
  1158. // and reduce the total number of dirty.
  1159. //
  1160. CcAcquireMasterLock( &OldIrql );
  1161. if (Bcb->Dirty) {
  1162. CcDeductDirtyPages( SharedCacheMap, Bcb->ByteLength >> PAGE_SHIFT );
  1163. }
  1164. CcReleaseMasterLock( OldIrql );
  1165. CcDeallocateBcb( Bcb );
  1166. }
  1167. }
  1168. }
  1169. //
  1170. // Internal support routine.
  1171. //
  1172. VOID
  1173. FASTCALL
  1174. CcDeleteSharedCacheMap (
  1175. IN PSHARED_CACHE_MAP SharedCacheMap,
  1176. IN KIRQL ListIrql,
  1177. IN ULONG ReleaseFile
  1178. )
  1179. /*++
  1180. Routine Description:
  1181. The specified SharedCacheMap is removed from the global list of
  1182. SharedCacheMap's and deleted with all of its related structures.
  1183. Other objects which were referenced in CcInitializeCacheMap are
  1184. dereferenced here.
  1185. NOTE: The CcMasterSpinLock must already be acquired
  1186. on entry. It is released on return.
  1187. Arguments:
  1188. SharedCacheMap - Pointer to Cache Map to delete
  1189. ListIrql - priority to restore to when releasing shared cache map list
  1190. ReleaseFile - Supplied as nonzero if file was acquired exclusive and
  1191. should be released.
  1192. ReturnValue:
  1193. None.
  1194. --*/
  1195. {
  1196. LIST_ENTRY LocalList;
  1197. PFILE_OBJECT FileObject;
  1198. PVACB ActiveVacb;
  1199. ULONG ActivePage;
  1200. ULONG PageIsDirty;
  1201. DebugTrace(+1, me, "CcDeleteSharedCacheMap:\n", 0 );
  1202. DebugTrace( 0, me, " SharedCacheMap = %08lx\n", SharedCacheMap );
  1203. //
  1204. // Remove it from the global list and clear the pointer to it via
  1205. // the File Object.
  1206. //
  1207. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1208. //
  1209. // Zero pointer to SharedCacheMap. Once we have cleared the pointer,
  1210. // we can/must release the global list to avoid deadlocks.
  1211. //
  1212. FileObject = SharedCacheMap->FileObject;
  1213. FileObject->SectionObjectPointer->SharedCacheMap = (PSHARED_CACHE_MAP)NULL;
  1214. SetFlag( SharedCacheMap->Flags, WRITE_QUEUED );
  1215. //
  1216. // The OpenCount is 0, but we still need to flush out any dangling
  1217. // cache read or writes.
  1218. //
  1219. if ((SharedCacheMap->VacbActiveCount != 0) || (SharedCacheMap->NeedToZero != NULL)) {
  1220. //
  1221. // We will put it in a local list and set a flag
  1222. // to keep the Lazy Writer away from it, so that we can rip it out
  1223. // below if someone manages to sneak in and set something dirty, etc.
  1224. // If the file system does not synchronize cleanup calls with an
  1225. // exclusive on the stream, then this case is possible.
  1226. //
  1227. InitializeListHead( &LocalList );
  1228. InsertTailList( &LocalList, &SharedCacheMap->SharedCacheMapLinks );
  1229. //
  1230. // If there is an active Vacb, then nuke it now (before waiting!).
  1231. //
  1232. GetActiveVacbAtDpcLevel( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  1233. CcReleaseMasterLock( ListIrql );
  1234. //
  1235. // No point in saying the page is dirty (which can cause an allocation
  1236. // failure), since we are deleting this SharedCacheMap anyway.
  1237. //
  1238. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, FALSE );
  1239. while (SharedCacheMap->VacbActiveCount != 0) {
  1240. CcWaitOnActiveCount( SharedCacheMap );
  1241. }
  1242. //
  1243. // Now in case we hit the rare path where someone moved the
  1244. // SharedCacheMap again, do a remove again now. It may be
  1245. // from our local list or it may be from the dirty list,
  1246. // but who cares? The important thing is to remove it in
  1247. // the case it was the dirty list, since we will delete it
  1248. // below.
  1249. //
  1250. CcAcquireMasterLock( &ListIrql );
  1251. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1252. }
  1253. CcReleaseMasterLock( ListIrql );
  1254. //
  1255. // If there are Bcbs, then empty the list.
  1256. //
  1257. // I really wonder how often we have Bcbs at teardown. This is
  1258. // a lot of work that could be avoided otherwise.
  1259. //
  1260. if (!IsListEmpty( &SharedCacheMap->BcbList )) {
  1261. CcDeleteBcbs( SharedCacheMap );
  1262. }
  1263. //
  1264. // Call local routine to unmap, and purge if necessary.
  1265. //
  1266. CcUnmapAndPurge( SharedCacheMap );
  1267. //
  1268. // Now release the file now that the purge is done.
  1269. //
  1270. if (ReleaseFile) {
  1271. FsRtlReleaseFile( SharedCacheMap->FileObject );
  1272. }
  1273. //
  1274. // Dereference our pointer to the Section and FileObject
  1275. // (We have to test the Section pointer since CcInitializeCacheMap
  1276. // calls this routine for error recovery. Release our global
  1277. // resource before dereferencing the FileObject to avoid deadlocks.
  1278. //
  1279. if (SharedCacheMap->Section != NULL) {
  1280. ObDereferenceObject( SharedCacheMap->Section );
  1281. }
  1282. ObDereferenceObject( FileObject );
  1283. //
  1284. // If there is an Mbcb, deduct any dirty pages and deallocate.
  1285. //
  1286. if (SharedCacheMap->Mbcb != NULL) {
  1287. CcDeleteMbcb( SharedCacheMap );
  1288. }
  1289. //
  1290. // If there was an uninitialize event specified for this shared cache
  1291. // map, then set it to the signalled state, indicating that we are
  1292. // removing the section and deleting the shared cache map.
  1293. //
  1294. if (SharedCacheMap->UninitializeEvent != NULL) {
  1295. PCACHE_UNINITIALIZE_EVENT CUEvent, EventNext;
  1296. CUEvent = SharedCacheMap->UninitializeEvent;
  1297. while (CUEvent != NULL) {
  1298. EventNext = CUEvent->Next;
  1299. KeSetEvent(&CUEvent->Event, 0, FALSE);
  1300. CUEvent = EventNext;
  1301. }
  1302. }
  1303. //
  1304. // Now delete the Vacb vector.
  1305. //
  1306. if ((SharedCacheMap->Vacbs != &SharedCacheMap->InitialVacbs[0])
  1307. &&
  1308. (SharedCacheMap->Vacbs != NULL)) {
  1309. //
  1310. // If there are Vacb levels, then the Vacb Array better be in an empty state.
  1311. //
  1312. ASSERT((SharedCacheMap->SectionSize.QuadPart <= VACB_SIZE_OF_FIRST_LEVEL) ||
  1313. !IsVacbLevelReferenced( SharedCacheMap, SharedCacheMap->Vacbs, 1 ));
  1314. ExFreePool( SharedCacheMap->Vacbs );
  1315. }
  1316. //
  1317. // If an event had to be allocated for this SharedCacheMap,
  1318. // deallocate it.
  1319. //
  1320. if ((SharedCacheMap->CreateEvent != NULL) && (SharedCacheMap->CreateEvent != &SharedCacheMap->Event)) {
  1321. ExFreePool( SharedCacheMap->CreateEvent );
  1322. }
  1323. if ((SharedCacheMap->WaitOnActiveCount != NULL) && (SharedCacheMap->WaitOnActiveCount != &SharedCacheMap->Event)) {
  1324. ExFreePool( SharedCacheMap->WaitOnActiveCount );
  1325. }
  1326. //
  1327. // Deallocate the storeage for the SharedCacheMap.
  1328. //
  1329. ExFreePool( SharedCacheMap );
  1330. DebugTrace(-1, me, "CcDeleteSharedCacheMap -> VOID\n", 0 );
  1331. return;
  1332. }
  1333. VOID
  1334. CcSetFileSizes (
  1335. IN PFILE_OBJECT FileObject,
  1336. IN PCC_FILE_SIZES FileSizes
  1337. )
  1338. /*++
  1339. Routine Description:
  1340. This routine must be called whenever a file has been extended to reflect
  1341. this extension in the cache maps and underlying section. Calling this
  1342. routine has a benign effect if the current size of the section is
  1343. already greater than or equal to the new AllocationSize.
  1344. This routine must also be called whenever the FileSize for a file changes
  1345. to reflect these changes in the Cache Manager.
  1346. This routine seems rather large, but in the normal case it only acquires
  1347. a spinlock, updates some fields, and exits. Less often it will either
  1348. extend the section, or truncate/purge the file, but it would be unexpected
  1349. to do both. On the other hand, the idea of this routine is that it does
  1350. "everything" required when AllocationSize or FileSize change.
  1351. Arguments:
  1352. FileObject - A file object for which CcInitializeCacheMap has been
  1353. previously called.
  1354. FileSizes - A pointer to AllocationSize, FileSize and ValidDataLength
  1355. for the file. AllocationSize is ignored if it is not larger
  1356. than the current section size (i.e., it is ignored unless it
  1357. has grown). ValidDataLength is not used.
  1358. Return Value:
  1359. None
  1360. --*/
  1361. {
  1362. LARGE_INTEGER NewSectionSize;
  1363. LARGE_INTEGER NewFileSize;
  1364. LARGE_INTEGER NewValidDataLength;
  1365. IO_STATUS_BLOCK IoStatus;
  1366. PSHARED_CACHE_MAP SharedCacheMap;
  1367. NTSTATUS Status;
  1368. KIRQL OldIrql;
  1369. PVACB ActiveVacb;
  1370. ULONG ActivePage;
  1371. ULONG PageIsDirty;
  1372. DebugTrace(+1, me, "CcSetFileSizes:\n", 0 );
  1373. DebugTrace( 0, me, " FileObject = %08lx\n", FileObject );
  1374. DebugTrace( 0, me, " FileSizes = %08lx\n", FileSizes );
  1375. //
  1376. // Make a local copy of the new file size and section size.
  1377. //
  1378. NewSectionSize = FileSizes->AllocationSize;
  1379. NewFileSize = FileSizes->FileSize;
  1380. NewValidDataLength = FileSizes->ValidDataLength;
  1381. //
  1382. // Serialize Creation/Deletion of all Shared CacheMaps
  1383. //
  1384. CcAcquireMasterLock( &OldIrql );
  1385. //
  1386. // Get pointer to SharedCacheMap via File Object.
  1387. //
  1388. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  1389. //
  1390. // If the file is not cached, just get out.
  1391. //
  1392. if ((SharedCacheMap == NULL) || (SharedCacheMap->Section == NULL)) {
  1393. CcReleaseMasterLock( OldIrql );
  1394. //
  1395. // Let's try to purge the file incase this is a truncate. In the
  1396. // vast majority of cases when there is no shared cache map, there
  1397. // is no data section either, so this call will eventually be
  1398. // no-oped in Mm.
  1399. //
  1400. // First flush the first page we are keeping, if it has data, before
  1401. // we throw it away.
  1402. //
  1403. if (NewFileSize.LowPart & (PAGE_SIZE - 1)) {
  1404. MmFlushSection( FileObject->SectionObjectPointer, &NewFileSize, 1, &IoStatus, FALSE );
  1405. }
  1406. CcPurgeCacheSection( FileObject->SectionObjectPointer,
  1407. &NewFileSize,
  1408. 0,
  1409. FALSE );
  1410. DebugTrace(-1, me, "CcSetFileSizes -> VOID\n", 0 );
  1411. return;
  1412. }
  1413. //
  1414. // Make call a Noop if file is not mapped, or section already big enough.
  1415. //
  1416. if ( NewSectionSize.QuadPart > SharedCacheMap->SectionSize.QuadPart ) {
  1417. //
  1418. // Increment open count to make sure the SharedCacheMap stays around,
  1419. // then release the spinlock so that we can call Mm.
  1420. //
  1421. CcIncrementOpenCount( SharedCacheMap, '1fSS' );
  1422. CcReleaseMasterLock( OldIrql );
  1423. //
  1424. // Round new section size to pages.
  1425. //
  1426. NewSectionSize.QuadPart = NewSectionSize.QuadPart + (LONGLONG)(DEFAULT_EXTEND_MODULO - 1);
  1427. NewSectionSize.LowPart &= ~(DEFAULT_EXTEND_MODULO - 1);
  1428. //
  1429. // Call MM to extend the section.
  1430. //
  1431. DebugTrace( 0, mm, "MmExtendSection:\n", 0 );
  1432. DebugTrace( 0, mm, " Section = %08lx\n", SharedCacheMap->Section );
  1433. DebugTrace2(0, mm, " Size = %08lx, %08lx\n",
  1434. NewSectionSize.LowPart, NewSectionSize.HighPart );
  1435. Status = MmExtendSection( SharedCacheMap->Section, &NewSectionSize, TRUE );
  1436. if (NT_SUCCESS(Status)) {
  1437. //
  1438. // Extend the Vacb array.
  1439. //
  1440. Status = CcExtendVacbArray( SharedCacheMap, NewSectionSize );
  1441. }
  1442. else {
  1443. DebugTrace( 0, 0, "Error from MmExtendSection, Status = %08lx\n",
  1444. Status );
  1445. Status = FsRtlNormalizeNtstatus( Status,
  1446. STATUS_UNEXPECTED_MM_EXTEND_ERR );
  1447. }
  1448. //
  1449. // Serialize again to decrement the open count.
  1450. //
  1451. CcAcquireMasterLock( &OldIrql );
  1452. CcDecrementOpenCount( SharedCacheMap, '1fSF' );
  1453. if ((SharedCacheMap->OpenCount == 0) &&
  1454. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  1455. (SharedCacheMap->DirtyPages == 0)) {
  1456. //
  1457. // Move to the dirty list.
  1458. //
  1459. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1460. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  1461. &SharedCacheMap->SharedCacheMapLinks );
  1462. //
  1463. // Make sure the Lazy Writer will wake up, because we
  1464. // want him to delete this SharedCacheMap.
  1465. //
  1466. LazyWriter.OtherWork = TRUE;
  1467. if (!LazyWriter.ScanActive) {
  1468. CcScheduleLazyWriteScan( FALSE );
  1469. }
  1470. }
  1471. //
  1472. // If section or VACB extension failed, raise an
  1473. // exception to our caller.
  1474. //
  1475. if (!NT_SUCCESS(Status)) {
  1476. CcReleaseMasterLock( OldIrql );
  1477. ExRaiseStatus( Status );
  1478. }
  1479. //
  1480. // It is now very unlikely that we have any more work to do, but since
  1481. // the spinlock is already held, check again if we are cached.
  1482. //
  1483. //
  1484. // Get pointer to SharedCacheMap via File Object.
  1485. //
  1486. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  1487. //
  1488. // If the file is not cached, just get out.
  1489. //
  1490. if (SharedCacheMap == NULL) {
  1491. CcReleaseMasterLock( OldIrql );
  1492. DebugTrace(-1, me, "CcSetFileSizes -> VOID\n", 0 );
  1493. return;
  1494. }
  1495. }
  1496. //
  1497. // If we are shrinking either of these two sizes, then we must free the
  1498. // active page, since it may be locked.
  1499. //
  1500. CcIncrementOpenCount( SharedCacheMap, '2fSS' );
  1501. if ( ( NewFileSize.QuadPart < SharedCacheMap->ValidDataGoal.QuadPart ) ||
  1502. ( NewFileSize.QuadPart < SharedCacheMap->FileSize.QuadPart )) {
  1503. GetActiveVacbAtDpcLevel( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  1504. if ((ActiveVacb != NULL) || (SharedCacheMap->NeedToZero != NULL)) {
  1505. CcReleaseMasterLock( OldIrql );
  1506. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  1507. //
  1508. // Serialize again to reduce ValidDataLength. It cannot change
  1509. // because the caller must have the file exclusive.
  1510. //
  1511. CcAcquireMasterLock( &OldIrql );
  1512. }
  1513. }
  1514. //
  1515. // If the section did not grow, see if the file system supports
  1516. // ValidDataLength, then update the valid data length in the file system.
  1517. //
  1518. if ( SharedCacheMap->ValidDataLength.QuadPart != MAXLONGLONG ) {
  1519. if ( NewFileSize.QuadPart < SharedCacheMap->ValidDataLength.QuadPart ) {
  1520. SharedCacheMap->ValidDataLength = NewFileSize;
  1521. }
  1522. //
  1523. // Update our notion of ValidDataGoal (how far the file has been
  1524. // written in the cache) with caller's ValidDataLength. (Our
  1525. // ValidDataLength controls when we issue ValidDataLength callbacks.)
  1526. //
  1527. SharedCacheMap->ValidDataGoal = NewValidDataLength;
  1528. }
  1529. //
  1530. // On truncate, be nice guys and actually purge away user data from
  1531. // the cache. However, the PinAccess check is important to avoid deadlocks
  1532. // in Ntfs.
  1533. //
  1534. // It is also important to check the Vacb Active count. The caller
  1535. // must have the file exclusive, therefore, no one else can be actively
  1536. // doing anything in the file. Normally the Active count will be zero
  1537. // (like in a normal call from Set File Info), and we can go ahead and
  1538. // truncate. However, if the active count is nonzero, chances are this
  1539. // very thread has something pinned or mapped, and we will deadlock if
  1540. // we try to purge and wait for the count to go zero. A rare case of
  1541. // this which deadlocked DaveC on Christmas Day of 1992, is where Ntfs
  1542. // was trying to convert an attribute from resident to nonresident - which
  1543. // is a good example of a case where the purge was not needed.
  1544. //
  1545. if ( (NewFileSize.QuadPart < SharedCacheMap->FileSize.QuadPart ) &&
  1546. !FlagOn(SharedCacheMap->Flags, PIN_ACCESS) &&
  1547. (SharedCacheMap->VacbActiveCount == 0)) {
  1548. //
  1549. // Release the spinlock so that we can call Mm.
  1550. //
  1551. CcReleaseMasterLock( OldIrql );
  1552. //
  1553. // If we are actually truncating to zero (a size which has particular
  1554. // meaning to the Lazy Writer scan!) then we must reset the Mbcb/Bcbs,
  1555. // if there are any, so that we do not keep dirty pages around forever.
  1556. //
  1557. if (NewFileSize.QuadPart == 0) {
  1558. if (SharedCacheMap->Mbcb != NULL) {
  1559. CcDeleteMbcb( SharedCacheMap );
  1560. }
  1561. if (!IsListEmpty( &SharedCacheMap->BcbList )) {
  1562. CcDeleteBcbs( SharedCacheMap );
  1563. }
  1564. }
  1565. CcPurgeAndClearCacheSection( SharedCacheMap, &NewFileSize );
  1566. //
  1567. // Serialize again to decrement the open count.
  1568. //
  1569. CcAcquireMasterLock( &OldIrql );
  1570. }
  1571. CcDecrementOpenCount( SharedCacheMap, '2fSF' );
  1572. SharedCacheMap->FileSize = NewFileSize;
  1573. if ((SharedCacheMap->OpenCount == 0) &&
  1574. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  1575. (SharedCacheMap->DirtyPages == 0)) {
  1576. //
  1577. // Move to the dirty list.
  1578. //
  1579. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1580. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  1581. &SharedCacheMap->SharedCacheMapLinks );
  1582. //
  1583. // Make sure the Lazy Writer will wake up, because we
  1584. // want him to delete this SharedCacheMap.
  1585. //
  1586. LazyWriter.OtherWork = TRUE;
  1587. if (!LazyWriter.ScanActive) {
  1588. CcScheduleLazyWriteScan( FALSE );
  1589. }
  1590. }
  1591. CcReleaseMasterLock( OldIrql );
  1592. DebugTrace(-1, me, "CcSetFileSizes -> VOID\n", 0 );
  1593. return;
  1594. }
  1595. VOID
  1596. CcPurgeAndClearCacheSection (
  1597. IN PSHARED_CACHE_MAP SharedCacheMap,
  1598. IN PLARGE_INTEGER FileOffset
  1599. )
  1600. /*++
  1601. Routine Description:
  1602. This routine calls CcPurgeCacheSection after zeroing the end any
  1603. partial page at the start of the range. If the file is not cached
  1604. it flushes this page before the purge.
  1605. Arguments:
  1606. SectionObjectPointer - A pointer to the Section Object Pointers
  1607. structure in the nonpaged Fcb.
  1608. FileOffset - Offset from which file should be purged - rounded down
  1609. to page boundary. If NULL, purge the entire file.
  1610. ReturnValue:
  1611. FALSE - if the section was not successfully purged
  1612. TRUE - if the section was successfully purged
  1613. --*/
  1614. {
  1615. ULONG TempLength, Length;
  1616. LARGE_INTEGER LocalFileOffset;
  1617. IO_STATUS_BLOCK IoStatus;
  1618. PVOID TempVa;
  1619. PVACB Vacb;
  1620. //
  1621. // Awareness is indicated by the lowbit of the fileoffset pointer.
  1622. // Non-awareness of a private write stream results in a no-op.
  1623. //
  1624. if (FlagOn( SharedCacheMap->Flags, PRIVATE_WRITE )) {
  1625. if (((ULONG_PTR)FileOffset & 1) == 0) {
  1626. return;
  1627. }
  1628. FileOffset = (PLARGE_INTEGER)((ULONG_PTR)FileOffset ^ 1);
  1629. }
  1630. //
  1631. // If a range was specified, then we have to see if we need to
  1632. // save any user data before purging.
  1633. //
  1634. if ((FileOffset->LowPart & (PAGE_SIZE - 1)) != 0) {
  1635. //
  1636. // Switch to LocalFileOffset. We do it this way because we
  1637. // still pass it on as an optional parameter.
  1638. //
  1639. LocalFileOffset = *FileOffset;
  1640. FileOffset = &LocalFileOffset;
  1641. //
  1642. // If the file is cached, then we can actually zero the data to
  1643. // be purged in memory, and not purge those pages. This is a huge
  1644. // savings, because sometimes the flushes in the other case cause
  1645. // us to kill lots of stack, time and I/O doing CcZeroData in especially
  1646. // large user-mapped files.
  1647. //
  1648. if ((SharedCacheMap->Section != NULL) &&
  1649. (SharedCacheMap->Vacbs != NULL)) {
  1650. //
  1651. // First zero the first page we are keeping, if it has data, and
  1652. // adjust FileOffset and Length to allow it to stay.
  1653. //
  1654. TempLength = PAGE_SIZE - (FileOffset->LowPart & (PAGE_SIZE - 1));
  1655. TempVa = CcGetVirtualAddress( SharedCacheMap, *FileOffset, &Vacb, &Length );
  1656. //
  1657. // Do not map and zero the page if we are not reducing our notion
  1658. // of Valid Data, because that does two bad things. First
  1659. // CcSetDirtyInMask will arbitrarily smash up ValidDataGoal
  1660. // (causing a potential invalid CcSetValidData call). Secondly,
  1661. // if the Lazy Writer writes the last page ahead of another flush
  1662. // through MM, then the file system will never see a write from
  1663. // MM, and will not include the last page in ValidDataLength on
  1664. // disk.
  1665. //
  1666. RtlZeroMemory( TempVa, TempLength );
  1667. if (FileOffset->QuadPart <= SharedCacheMap->ValidDataGoal.QuadPart) {
  1668. //
  1669. // Make sure the Lazy Writer writes it.
  1670. //
  1671. CcSetDirtyInMask( SharedCacheMap, FileOffset, TempLength );
  1672. //
  1673. // Otherwise, we are mapped, so make sure at least that Mm
  1674. // knows the page is dirty since we zeroed it.
  1675. //
  1676. } else {
  1677. MmSetAddressRangeModified( TempVa, 1 );
  1678. }
  1679. FileOffset->QuadPart += (LONGLONG)TempLength;
  1680. //
  1681. // If we get any kind of error, like failing to read the page from
  1682. // the network, just charge on. Note that we only read it in order
  1683. // to zero it and avoid the flush below, so if we cannot read it
  1684. // there is really no stale data problem.
  1685. //
  1686. CcFreeVirtualAddress( Vacb );
  1687. } else {
  1688. //
  1689. // First flush the first page we are keeping, if it has data, before
  1690. // we throw it away.
  1691. //
  1692. MmFlushSection( SharedCacheMap->FileObject->SectionObjectPointer, FileOffset, 1, &IoStatus, FALSE );
  1693. }
  1694. }
  1695. CcPurgeCacheSection( SharedCacheMap->FileObject->SectionObjectPointer,
  1696. FileOffset,
  1697. 0,
  1698. FALSE );
  1699. }
  1700. BOOLEAN
  1701. CcPurgeCacheSection (
  1702. IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
  1703. IN PLARGE_INTEGER FileOffset,
  1704. IN ULONG Length,
  1705. IN BOOLEAN UninitializeCacheMaps
  1706. )
  1707. /*++
  1708. Routine Description:
  1709. This routine may be called to force a purge of the cache section,
  1710. even if it is cached. Note, if a user has the file mapped, then the purge
  1711. will *not* take effect, and this must be considered part of normal application
  1712. interaction. The purpose of purge is to throw away potentially nonzero
  1713. data, so that it will be read in again and presumably zeroed. This is
  1714. not really a security issue, but rather an effort to not confuse the
  1715. application when it sees nonzero data. We cannot help the fact that
  1716. a user-mapped view forces us to hang on to stale data.
  1717. This routine is intended to be called whenever previously written
  1718. data is being truncated from the file, and the file is not being
  1719. deleted.
  1720. The file must be acquired exclusive in order to call this routine.
  1721. Arguments:
  1722. SectionObjectPointer - A pointer to the Section Object Pointers
  1723. structure in the nonpaged Fcb.
  1724. FileOffset - Offset from which file should be purged - rounded down
  1725. to page boundary. If NULL, purge the entire file.
  1726. Length - Defines the length of the byte range to purge, starting at
  1727. FileOffset. This parameter is ignored if FileOffset is
  1728. specified as NULL. If FileOffset is specified and Length
  1729. is 0, then purge from FileOffset to the end of the file.
  1730. UninitializeCacheMaps - If TRUE, we should uninitialize all the private
  1731. cache maps before purging the data.
  1732. ReturnValue:
  1733. FALSE - if the section was not successfully purged
  1734. TRUE - if the section was successfully purged
  1735. --*/
  1736. {
  1737. KIRQL OldIrql;
  1738. PSHARED_CACHE_MAP SharedCacheMap;
  1739. PPRIVATE_CACHE_MAP PrivateCacheMap;
  1740. ULONG ActivePage;
  1741. ULONG PageIsDirty;
  1742. BOOLEAN PurgeWorked = TRUE;
  1743. PVACB Vacb = NULL;
  1744. DebugTrace(+1, me, "CcPurgeCacheSection:\n", 0 );
  1745. DebugTrace( 0, mm, " SectionObjectPointer = %08lx\n", SectionObjectPointer );
  1746. DebugTrace2(0, me, " FileOffset = %08lx, %08lx\n",
  1747. ARGUMENT_PRESENT(FileOffset) ? FileOffset->LowPart
  1748. : 0,
  1749. ARGUMENT_PRESENT(FileOffset) ? FileOffset->HighPart
  1750. : 0 );
  1751. DebugTrace( 0, me, " Length = %08lx\n", Length );
  1752. //
  1753. // If you want us to uninitialize cache maps, the RtlZeroMemory paths
  1754. // below depend on actually having to purge something after zeroing.
  1755. //
  1756. ASSERT(!UninitializeCacheMaps || (Length == 0) || (Length >= PAGE_SIZE * 2));
  1757. //
  1758. // Serialize Creation/Deletion of all Shared CacheMaps
  1759. //
  1760. CcAcquireMasterLock( &OldIrql );
  1761. //
  1762. // Get pointer to SharedCacheMap via File Object.
  1763. //
  1764. SharedCacheMap = SectionObjectPointer->SharedCacheMap;
  1765. //
  1766. // Increment open count to make sure the SharedCacheMap stays around,
  1767. // then release the spinlock so that we can call Mm.
  1768. //
  1769. if (SharedCacheMap != NULL) {
  1770. //
  1771. // Awareness is indicated by the lowbit of the fileoffset pointer.
  1772. // Non-awareness of a private write stream results in a no-op.
  1773. //
  1774. if (FlagOn( SharedCacheMap->Flags, PRIVATE_WRITE )) {
  1775. if (((ULONG_PTR)FileOffset & 1) == 0) {
  1776. CcReleaseMasterLock( OldIrql );
  1777. return TRUE;
  1778. }
  1779. FileOffset = (PLARGE_INTEGER)((ULONG_PTR)FileOffset ^ 1);
  1780. }
  1781. CcIncrementOpenCount( SharedCacheMap, 'scPS' );
  1782. //
  1783. // If there is an active Vacb, then nuke it now (before waiting!).
  1784. //
  1785. GetActiveVacbAtDpcLevel( SharedCacheMap, Vacb, ActivePage, PageIsDirty );
  1786. }
  1787. CcReleaseMasterLock( OldIrql );
  1788. if (Vacb != NULL) {
  1789. CcFreeActiveVacb( SharedCacheMap, Vacb, ActivePage, PageIsDirty );
  1790. }
  1791. //
  1792. // Increment open count to make sure the SharedCacheMap stays around,
  1793. // then release the spinlock so that we can call Mm.
  1794. //
  1795. if (SharedCacheMap != NULL) {
  1796. //
  1797. // Now loop to make sure that no one is currently caching the file.
  1798. //
  1799. if (UninitializeCacheMaps) {
  1800. while (!IsListEmpty( &SharedCacheMap->PrivateList )) {
  1801. PrivateCacheMap = CONTAINING_RECORD( SharedCacheMap->PrivateList.Flink,
  1802. PRIVATE_CACHE_MAP,
  1803. PrivateLinks );
  1804. CcUninitializeCacheMap( PrivateCacheMap->FileObject, NULL, NULL );
  1805. }
  1806. }
  1807. //
  1808. // Now, let's unmap and purge here.
  1809. //
  1810. // We still need to wait for any dangling cache read or writes.
  1811. //
  1812. // In fact we have to loop and wait because the lazy writer can
  1813. // sneak in and do an CcGetVirtualAddressIfMapped, and we are not
  1814. // synchronized.
  1815. //
  1816. while ((SharedCacheMap->Vacbs != NULL) &&
  1817. !CcUnmapVacbArray( SharedCacheMap, FileOffset, Length, FALSE )) {
  1818. CcWaitOnActiveCount( SharedCacheMap );
  1819. }
  1820. }
  1821. //
  1822. // Purge failures are extremely rare if there are no user mapped sections.
  1823. // However, it is possible that we will get one from our own mapping, if
  1824. // the file is being lazy deleted from a previous open. For that case
  1825. // we wait here until the purge succeeds, so that we are not left with
  1826. // old user file data. Although Length is actually invariant in this loop,
  1827. // we do need to keep checking that we are allowed to truncate in case a
  1828. // user maps the file during a delay.
  1829. //
  1830. while (!(PurgeWorked = MmPurgeSection(SectionObjectPointer,
  1831. FileOffset,
  1832. Length,
  1833. (BOOLEAN)((SharedCacheMap !=NULL) &&
  1834. ARGUMENT_PRESENT(FileOffset)))) &&
  1835. (Length == 0) &&
  1836. MmCanFileBeTruncated(SectionObjectPointer, FileOffset)) {
  1837. (VOID)KeDelayExecutionThread( KernelMode, FALSE, &CcCollisionDelay );
  1838. }
  1839. //
  1840. // Reduce the open count on the SharedCacheMap if there was one.
  1841. //
  1842. if (SharedCacheMap != NULL) {
  1843. //
  1844. // Serialize again to decrement the open count.
  1845. //
  1846. CcAcquireMasterLock( &OldIrql );
  1847. CcDecrementOpenCount( SharedCacheMap, 'scPF' );
  1848. if ((SharedCacheMap->OpenCount == 0) &&
  1849. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  1850. (SharedCacheMap->DirtyPages == 0)) {
  1851. //
  1852. // Move to the dirty list.
  1853. //
  1854. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  1855. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  1856. &SharedCacheMap->SharedCacheMapLinks );
  1857. //
  1858. // Make sure the Lazy Writer will wake up, because we
  1859. // want him to delete this SharedCacheMap.
  1860. //
  1861. LazyWriter.OtherWork = TRUE;
  1862. if (!LazyWriter.ScanActive) {
  1863. CcScheduleLazyWriteScan( FALSE );
  1864. }
  1865. }
  1866. CcReleaseMasterLock( OldIrql );
  1867. }
  1868. DebugTrace(-1, me, "CcPurgeCacheSection -> %02lx\n", PurgeWorked );
  1869. return PurgeWorked;
  1870. }
  1871. //
  1872. // Internal support routine.
  1873. //
  1874. VOID
  1875. CcUnmapAndPurge(
  1876. IN PSHARED_CACHE_MAP SharedCacheMap
  1877. )
  1878. /*++
  1879. Routine Description:
  1880. This routine may be called to unmap and purge a section, causing Memory
  1881. Management to throw the pages out and reset his notion of file size.
  1882. Arguments:
  1883. SharedCacheMap - Pointer to SharedCacheMap of section to purge.
  1884. Return Value:
  1885. None.
  1886. --*/
  1887. {
  1888. PFILE_OBJECT FileObject;
  1889. FileObject = SharedCacheMap->FileObject;
  1890. //
  1891. // Unmap all Vacbs
  1892. //
  1893. if (SharedCacheMap->Vacbs != NULL) {
  1894. (VOID)CcUnmapVacbArray( SharedCacheMap, NULL, 0, FALSE );
  1895. }
  1896. //
  1897. // Now that the file is unmapped, we can purge the truncated
  1898. // pages from memory, if TRUNCATE_REQUIRED. Note that since the
  1899. // entire section is being purged (FileSize == NULL), the purge
  1900. // and subsequent delete of the SharedCacheMap should drop
  1901. // all references on the section and file object clearing the
  1902. // way for the Close Call and actual file delete to occur
  1903. // immediately.
  1904. //
  1905. if (FlagOn(SharedCacheMap->Flags, TRUNCATE_REQUIRED)) {
  1906. DebugTrace( 0, mm, "MmPurgeSection:\n", 0 );
  1907. DebugTrace( 0, mm, " SectionObjectPointer = %08lx\n",
  1908. FileObject->SectionObjectPointer );
  1909. DebugTrace2(0, mm, " Offset = %08lx\n",
  1910. SharedCacheMap->FileSize.LowPart,
  1911. SharedCacheMap->FileSize.HighPart );
  1912. CcPurgeCacheSection( FileObject->SectionObjectPointer,
  1913. NULL,
  1914. 0,
  1915. FALSE );
  1916. }
  1917. }
  1918. VOID
  1919. CcDeleteMbcb(
  1920. IN PSHARED_CACHE_MAP SharedCacheMap
  1921. )
  1922. /*++
  1923. Routine Description:
  1924. This routine may be called to reset the Mbcb for a stream to say
  1925. there are no dirty pages, and free all auxillary allocation.
  1926. Arguments:
  1927. SharedCacheMap - Pointer to SharedCacheMap.
  1928. Return Value:
  1929. None.
  1930. --*/
  1931. {
  1932. PMBCB Mbcb;
  1933. PBITMAP_RANGE BitmapRange;
  1934. KLOCK_QUEUE_HANDLE LockHandle;
  1935. ULONG DoDrain = FALSE;
  1936. PLIST_ENTRY NextEntry;
  1937. LIST_ENTRY BitmapRangesToFree;
  1938. InitializeListHead( &BitmapRangesToFree );
  1939. KeAcquireInStackQueuedSpinLock( &SharedCacheMap->BcbSpinLock, &LockHandle );
  1940. Mbcb = SharedCacheMap->Mbcb;
  1941. //
  1942. // Is there an Mbcb?
  1943. //
  1944. if (Mbcb != NULL) {
  1945. //
  1946. // First deduct the dirty pages we are getting rid of.
  1947. //
  1948. CcAcquireMasterLockAtDpcLevel();
  1949. CcDeductDirtyPages( SharedCacheMap, Mbcb->DirtyPages );
  1950. CcReleaseMasterLockFromDpcLevel();
  1951. //
  1952. // Now loop through all of the ranges.
  1953. //
  1954. while (!IsListEmpty(&Mbcb->BitmapRanges)) {
  1955. //
  1956. // Get next range and remove it from the list.
  1957. //
  1958. BitmapRange = (PBITMAP_RANGE)CONTAINING_RECORD( Mbcb->BitmapRanges.Flink,
  1959. BITMAP_RANGE,
  1960. Links );
  1961. RemoveEntryList( &BitmapRange->Links );
  1962. //
  1963. // If there is a bitmap, and it is not the initial embedded one, then
  1964. // delete it.
  1965. //
  1966. if ((BitmapRange->Bitmap != NULL) &&
  1967. (BitmapRange->Bitmap != (PULONG)&Mbcb->BitmapRange2)) {
  1968. DoDrain = TRUE;
  1969. //
  1970. // Usually the bitmap is all zeros at this point, but it may not be.
  1971. //
  1972. if (BitmapRange->DirtyPages != 0) {
  1973. RtlZeroMemory( BitmapRange->Bitmap, MBCB_BITMAP_BLOCK_SIZE );
  1974. }
  1975. CcAcquireVacbLockAtDpcLevel();
  1976. CcDeallocateVacbLevel( (PVACB *)BitmapRange->Bitmap, FALSE );
  1977. CcReleaseVacbLockFromDpcLevel();
  1978. }
  1979. //
  1980. // If the range is not one of the initial embedded ranges, then delete it.
  1981. //
  1982. if ((BitmapRange < (PBITMAP_RANGE)Mbcb) &&
  1983. (BitmapRange > (PBITMAP_RANGE)((PCHAR)Mbcb + sizeof(MBCB)))) {
  1984. InsertTailList( &BitmapRangesToFree, &BitmapRange->Links );
  1985. }
  1986. }
  1987. //
  1988. // Zero the pointer and get out.
  1989. //
  1990. SharedCacheMap->Mbcb = NULL;
  1991. KeReleaseInStackQueuedSpinLock( &LockHandle );
  1992. //
  1993. // Free all the pool now that no locks are held.
  1994. //
  1995. while (!IsListEmpty(&BitmapRangesToFree)) {
  1996. NextEntry = RemoveHeadList( &BitmapRangesToFree );
  1997. BitmapRange = CONTAINING_RECORD ( NextEntry,
  1998. BITMAP_RANGE,
  1999. Links );
  2000. ExFreePool( BitmapRange );
  2001. }
  2002. //
  2003. // Now delete the Mbcb.
  2004. //
  2005. CcDeallocateBcb( (PBCB)Mbcb );
  2006. } else {
  2007. KeReleaseInStackQueuedSpinLock( &LockHandle );
  2008. }
  2009. if (DoDrain) {
  2010. CcDrainVacbLevelZone();
  2011. }
  2012. }
  2013. VOID
  2014. CcSetDirtyPageThreshold (
  2015. IN PFILE_OBJECT FileObject,
  2016. IN ULONG DirtyPageThreshold
  2017. )
  2018. /*++
  2019. Routine Description:
  2020. This routine may be called to set a dirty page threshold for this
  2021. stream. The write throttling will kick in whenever the file system
  2022. attempts to exceed the dirty page threshold for this file.
  2023. Arguments:
  2024. FileObject - Supplies file object for the stream
  2025. DirtyPageThreshold - Supplies the dirty page threshold for this stream,
  2026. or 0 for no threshold.
  2027. Return Value:
  2028. None
  2029. Environment:
  2030. The caller must guarantee exclusive access to the FsRtl header flags,
  2031. for example, by calling this routine once during create of the structure
  2032. containing the header. Then it would call the routine again when actually
  2033. caching the stream.
  2034. --*/
  2035. {
  2036. PSHARED_CACHE_MAP SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  2037. if (SharedCacheMap != NULL) {
  2038. SharedCacheMap->DirtyPageThreshold = DirtyPageThreshold;
  2039. }
  2040. //
  2041. // Test the flag before setting, in case the caller is no longer properly
  2042. // synchronized.
  2043. //
  2044. if (!FlagOn(((PFSRTL_COMMON_FCB_HEADER)(FileObject->FsContext))->Flags,
  2045. FSRTL_FLAG_LIMIT_MODIFIED_PAGES)) {
  2046. SetFlag(((PFSRTL_COMMON_FCB_HEADER)(FileObject->FsContext))->Flags,
  2047. FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
  2048. }
  2049. }
  2050. VOID
  2051. CcZeroEndOfLastPage (
  2052. IN PFILE_OBJECT FileObject
  2053. )
  2054. /*++
  2055. Routine Description:
  2056. This routine is only called by Mm before mapping a user view to
  2057. a section. If there is an uninitialized page at the end of the
  2058. file, we zero it by freeing that page.
  2059. Parameters:
  2060. FileObject - File object for section to be mapped
  2061. Return Value:
  2062. None
  2063. --*/
  2064. {
  2065. PSHARED_CACHE_MAP SharedCacheMap;
  2066. ULONG ActivePage;
  2067. ULONG PageIsDirty;
  2068. KIRQL OldIrql;
  2069. PVOID NeedToZero = NULL;
  2070. PVACB ActiveVacb = NULL;
  2071. IO_STATUS_BLOCK Iosb;
  2072. BOOLEAN PurgeResult;
  2073. //
  2074. // See if we have an active Vacb, that we need to free.
  2075. //
  2076. FsRtlAcquireFileExclusive( FileObject );
  2077. CcAcquireMasterLock( &OldIrql );
  2078. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  2079. if (SharedCacheMap != NULL) {
  2080. //
  2081. // See if there is an active vacb.
  2082. //
  2083. if ((SharedCacheMap->ActiveVacb != NULL) || ((NeedToZero = SharedCacheMap->NeedToZero) != NULL)) {
  2084. CcIncrementOpenCount( SharedCacheMap, 'peZS' );
  2085. GetActiveVacbAtDpcLevel( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  2086. }
  2087. }
  2088. CcReleaseMasterLock( OldIrql );
  2089. //
  2090. // Remember in FsRtl header there is a user section.
  2091. // If this is an advanced header then also acquire the mutex to access
  2092. // this field.
  2093. //
  2094. if (FlagOn( ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->Flags,
  2095. FSRTL_FLAG_ADVANCED_HEADER )) {
  2096. ExAcquireFastMutex( ((PFSRTL_ADVANCED_FCB_HEADER)FileObject->FsContext)->FastMutex );
  2097. SetFlag( ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->Flags,
  2098. FSRTL_FLAG_USER_MAPPED_FILE );
  2099. ExReleaseFastMutex( ((PFSRTL_ADVANCED_FCB_HEADER)FileObject->FsContext)->FastMutex );
  2100. } else {
  2101. SetFlag( ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->Flags,
  2102. FSRTL_FLAG_USER_MAPPED_FILE );
  2103. }
  2104. //
  2105. // Free the active vacb now so we don't deadlock if we have to purge
  2106. //
  2107. if ((ActiveVacb != NULL) || (NeedToZero != NULL)) {
  2108. CcFreeActiveVacb( SharedCacheMap, ActiveVacb, ActivePage, PageIsDirty );
  2109. }
  2110. if (FlagOn( ((PFSRTL_COMMON_FCB_HEADER)FileObject->FsContext)->Flags2, FSRTL_FLAG2_PURGE_WHEN_MAPPED )) {
  2111. if (FileObject->SectionObjectPointer->SharedCacheMap) {
  2112. ASSERT( ((PSHARED_CACHE_MAP)(FileObject->SectionObjectPointer->SharedCacheMap))->VacbActiveCount == 0 );
  2113. }
  2114. CcFlushCache( FileObject->SectionObjectPointer, NULL, 0, &Iosb );
  2115. PurgeResult = CcPurgeCacheSection( FileObject->SectionObjectPointer, NULL, 0, FALSE );
  2116. if (FileObject->SectionObjectPointer->SharedCacheMap) {
  2117. ASSERT( ((PSHARED_CACHE_MAP)(FileObject->SectionObjectPointer->SharedCacheMap))->VacbActiveCount == 0 );
  2118. }
  2119. }
  2120. FsRtlReleaseFile( FileObject );
  2121. //
  2122. // If the file is cached and we have a Vacb to free, we need to
  2123. // use the lazy writer callback to synchronize so no one will be
  2124. // extending valid data.
  2125. //
  2126. if ((ActiveVacb != NULL) || (NeedToZero != NULL)) {
  2127. //
  2128. // Serialize again to decrement the open count.
  2129. //
  2130. CcAcquireMasterLock( &OldIrql );
  2131. CcDecrementOpenCount( SharedCacheMap, 'peZF' );
  2132. if ((SharedCacheMap->OpenCount == 0) &&
  2133. !FlagOn(SharedCacheMap->Flags, WRITE_QUEUED) &&
  2134. (SharedCacheMap->DirtyPages == 0)) {
  2135. //
  2136. // Move to the dirty list.
  2137. //
  2138. RemoveEntryList( &SharedCacheMap->SharedCacheMapLinks );
  2139. InsertTailList( &CcDirtySharedCacheMapList.SharedCacheMapLinks,
  2140. &SharedCacheMap->SharedCacheMapLinks );
  2141. //
  2142. // Make sure the Lazy Writer will wake up, because we
  2143. // want him to delete this SharedCacheMap.
  2144. //
  2145. LazyWriter.OtherWork = TRUE;
  2146. if (!LazyWriter.ScanActive) {
  2147. CcScheduleLazyWriteScan( FALSE );
  2148. }
  2149. }
  2150. CcReleaseMasterLock( OldIrql );
  2151. }
  2152. }
  2153. BOOLEAN
  2154. CcZeroData (
  2155. IN PFILE_OBJECT FileObject,
  2156. IN PLARGE_INTEGER StartOffset,
  2157. IN PLARGE_INTEGER EndOffset,
  2158. IN BOOLEAN Wait
  2159. )
  2160. /*++
  2161. Routine Description:
  2162. This routine attempts to zero the specified file data and deliver the
  2163. correct I/O status.
  2164. If the caller does not want to block (such as for disk I/O), then
  2165. Wait should be supplied as FALSE. If Wait was supplied as FALSE and
  2166. it is currently impossible to zero all of the requested data without
  2167. blocking, then this routine will return FALSE. However, if the
  2168. required space is immediately accessible in the cache and no blocking is
  2169. required, this routine zeros the data and returns TRUE.
  2170. If the caller supplies Wait as TRUE, then this routine is guaranteed
  2171. to zero the data and return TRUE. If the correct space is immediately
  2172. accessible in the cache, then no blocking will occur. Otherwise,
  2173. the necessary work will be initiated to read and/or free cache data,
  2174. and the caller will be blocked until the data can be received.
  2175. File system Fsd's should typically supply Wait = TRUE if they are
  2176. processing a synchronous I/O requests, or Wait = FALSE if they are
  2177. processing an asynchronous request.
  2178. File system threads should supply Wait = TRUE.
  2179. IMPORTANT NOTE: File systems which call this routine must be prepared
  2180. to handle a special form of a write call where the Mdl is already
  2181. supplied. Namely, if Irp->MdlAddress is supplied, the file system
  2182. must check the low order bit of Irp->MdlAddress->ByteOffset. If it
  2183. is set, that means that the Irp was generated in this routine and
  2184. the file system must do two things:
  2185. Decrement Irp->MdlAddress->ByteOffset and Irp->UserBuffer
  2186. Clear Irp->MdlAddress immediately prior to completing the
  2187. request, as this routine expects to reuse the Mdl and
  2188. ultimately deallocate the Mdl itself.
  2189. Arguments:
  2190. FileObject - pointer to the FileObject for which a range of bytes
  2191. is to be zeroed. This FileObject may either be for
  2192. a cached file or a noncached file. If the file is
  2193. not cached, then WriteThrough must be TRUE and
  2194. StartOffset and EndOffset must be on sector boundaries.
  2195. StartOffset - Start offset in file to be zeroed.
  2196. EndOffset - End offset in file to be zeroed.
  2197. Wait - FALSE if caller may not block, TRUE otherwise (see description
  2198. above)
  2199. Return Value:
  2200. FALSE - if Wait was supplied as FALSE and the data was not zeroed.
  2201. TRUE - if the data has been zeroed.
  2202. Raises:
  2203. STATUS_INSUFFICIENT_RESOURCES - If a pool allocation failure occurs.
  2204. This can only occur if Wait was specified as TRUE. (If Wait is
  2205. specified as FALSE, and an allocation failure occurs, this
  2206. routine simply returns FALSE.)
  2207. --*/
  2208. {
  2209. PSHARED_CACHE_MAP SharedCacheMap;
  2210. PVOID CacheBuffer;
  2211. LARGE_INTEGER FOffset;
  2212. LARGE_INTEGER ToGo;
  2213. ULONG ZeroBytes, ZeroTransfer;
  2214. ULONG SectorMask;
  2215. ULONG i;
  2216. BOOLEAN WriteThrough;
  2217. BOOLEAN AggressiveZero = FALSE;
  2218. ULONG SavedState = 0;
  2219. ULONG MaxZerosInCache = MAX_ZEROS_IN_CACHE;
  2220. ULONG NumberOfColors = 1;
  2221. PBCB Bcb = NULL;
  2222. PCHAR Zeros = NULL;
  2223. PMDL ZeroMdl = NULL;
  2224. ULONG MaxBytesMappedInMdl = 0;
  2225. BOOLEAN Result = TRUE;
  2226. PPFN_NUMBER Page;
  2227. ULONG SavedByteCount;
  2228. LARGE_INTEGER SizeLeft;
  2229. DebugTrace(+1, me, "CcZeroData\n", 0 );
  2230. WriteThrough = (BOOLEAN)(((FileObject->Flags & FO_WRITE_THROUGH) != 0) ||
  2231. (FileObject->PrivateCacheMap == NULL));
  2232. //
  2233. // If the caller specified Wait, but the FileObject is WriteThrough,
  2234. // then we need to just get out.
  2235. //
  2236. if (WriteThrough && !Wait) {
  2237. DebugTrace(-1, me, "CcZeroData->FALSE (WriteThrough && !Wait)\n", 0 );
  2238. return FALSE;
  2239. }
  2240. SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
  2241. SectorMask = IoGetRelatedDeviceObject(FileObject)->SectorSize - 1;
  2242. FOffset = *StartOffset;
  2243. //
  2244. // Calculate how much to zero this time.
  2245. //
  2246. ToGo.QuadPart = EndOffset->QuadPart - FOffset.QuadPart;
  2247. //
  2248. // This magic number is what the fastpaths throttle on, and they will present
  2249. // non-sector aligned zeroing requests. As long as we will always handle them
  2250. // on the cached path, we are OK.
  2251. //
  2252. // If we will not make the cached path, the request must be aligned.
  2253. //
  2254. ASSERT( ToGo.QuadPart <= 0x2000 ||
  2255. ((ToGo.LowPart & SectorMask) == 0 &&
  2256. (FOffset.LowPart & SectorMask) == 0));
  2257. //
  2258. // We will only do zeroing in the cache if the caller is using a
  2259. // cached file object, and did not specify WriteThrough. We are
  2260. // willing to zero some data in the cache if our total is not too
  2261. // much, or there is sufficient available pages.
  2262. //
  2263. if (((ToGo.QuadPart <= 0x2000) ||
  2264. (MmAvailablePages >= ((MAX_ZEROS_IN_CACHE / PAGE_SIZE) * 4))) && !WriteThrough) {
  2265. try {
  2266. while (MaxZerosInCache != 0) {
  2267. ULONG ReceivedLength;
  2268. LARGE_INTEGER BeyondLastByte;
  2269. if ( ToGo.QuadPart > (LONGLONG)MaxZerosInCache ) {
  2270. //
  2271. // If Wait == FALSE, then there is no point in getting started,
  2272. // because we would have to start all over again zeroing with
  2273. // Wait == TRUE, since we would fall out of this loop and
  2274. // start synchronously writing pages to disk.
  2275. //
  2276. if (!Wait) {
  2277. DebugTrace(-1, me, "CcZeroData -> FALSE\n", 0 );
  2278. try_return( Result = FALSE );
  2279. }
  2280. }
  2281. else {
  2282. MaxZerosInCache = ToGo.LowPart;
  2283. }
  2284. //
  2285. // Call local routine to Map or Access the file data, then zero the data,
  2286. // then call another local routine to free the data. If we cannot map
  2287. // the data because of a Wait condition, return FALSE.
  2288. //
  2289. // Note that this call may result in an exception, however, if it
  2290. // does no Bcb is returned and this routine has absolutely no
  2291. // cleanup to perform. Therefore, we do not have a try-finally
  2292. // and we allow the possibility that we will simply be unwound
  2293. // without notice.
  2294. //
  2295. if (!CcPinFileData( FileObject,
  2296. &FOffset,
  2297. MaxZerosInCache,
  2298. FALSE,
  2299. TRUE,
  2300. Wait,
  2301. &Bcb,
  2302. &CacheBuffer,
  2303. &BeyondLastByte )) {
  2304. DebugTrace(-1, me, "CcZeroData -> FALSE\n", 0 );
  2305. try_return( Result = FALSE );
  2306. }
  2307. //
  2308. // Calculate how much data is described by Bcb starting at our desired
  2309. // file offset. If it is more than we need, we will zero the whole thing
  2310. // anyway.
  2311. //
  2312. ReceivedLength = (ULONG)(BeyondLastByte.QuadPart - FOffset.QuadPart );
  2313. //
  2314. // Now attempt to allocate an Mdl to describe the mapped data.
  2315. //
  2316. ZeroMdl = IoAllocateMdl( CacheBuffer,
  2317. ReceivedLength,
  2318. FALSE,
  2319. FALSE,
  2320. NULL );
  2321. if (ZeroMdl == NULL) {
  2322. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2323. }
  2324. //
  2325. // It is necessary to probe and lock the pages, or else
  2326. // the pages may not still be in memory when we do the
  2327. // MmSetAddressRangeModified for the dirty Bcb.
  2328. //
  2329. MmDisablePageFaultClustering(&SavedState);
  2330. MmProbeAndLockPages( ZeroMdl, KernelMode, IoReadAccess );
  2331. MmEnablePageFaultClustering(SavedState);
  2332. SavedState = 0;
  2333. //
  2334. // Assume we did not get all the data we wanted, and set FOffset
  2335. // to the end of the returned data, and advance buffer pointer.
  2336. //
  2337. FOffset = BeyondLastByte;
  2338. //
  2339. // Figure out how many bytes we are allowed to zero in the cache.
  2340. // Note it is possible we have zeroed a little more than our maximum,
  2341. // because we hit an existing Bcb that extended beyond the range.
  2342. //
  2343. if (MaxZerosInCache <= ReceivedLength) {
  2344. MaxZerosInCache = 0;
  2345. }
  2346. else {
  2347. MaxZerosInCache -= ReceivedLength;
  2348. }
  2349. //
  2350. // Now set the Bcb dirty. We have to explicitly set the address
  2351. // range modified here, because that work otherwise gets deferred
  2352. // to the Lazy Writer.
  2353. //
  2354. MmSetAddressRangeModified( CacheBuffer, ReceivedLength );
  2355. CcSetDirtyPinnedData( Bcb, NULL );
  2356. //
  2357. // Unmap the data now
  2358. //
  2359. CcUnpinFileData( Bcb, FALSE, UNPIN );
  2360. Bcb = NULL;
  2361. //
  2362. // Unlock and free the Mdl (we only loop back if we crossed
  2363. // a 256KB boundary.
  2364. //
  2365. MmUnlockPages( ZeroMdl );
  2366. IoFreeMdl( ZeroMdl );
  2367. ZeroMdl = NULL;
  2368. }
  2369. try_exit: NOTHING;
  2370. } finally {
  2371. if (SavedState != 0) {
  2372. MmEnablePageFaultClustering(SavedState);
  2373. }
  2374. //
  2375. // Clean up only necessary in abnormal termination.
  2376. //
  2377. if (Bcb != NULL) {
  2378. CcUnpinFileData( Bcb, FALSE, UNPIN );
  2379. }
  2380. //
  2381. // Since the last thing in the above loop which can
  2382. // fail is the MmProbeAndLockPages, we only need to
  2383. // free the Mdl here.
  2384. //
  2385. if (ZeroMdl != NULL) {
  2386. IoFreeMdl( ZeroMdl );
  2387. }
  2388. }
  2389. //
  2390. // If hit a wait condition above, return it now.
  2391. //
  2392. if (!Result) {
  2393. return FALSE;
  2394. }
  2395. //
  2396. // If we finished, get out nbow.
  2397. //
  2398. if ( FOffset.QuadPart >= EndOffset->QuadPart ) {
  2399. return TRUE;
  2400. }
  2401. }
  2402. //
  2403. // We either get here because we decided above not to zero anything in
  2404. // the cache directly, or else we zeroed up to our maximum and still
  2405. // have some left to zero direct to the file on disk. In either case,
  2406. // we will now zero from FOffset to *EndOffset, and then flush this
  2407. // range in case the file is cached/mapped, and there are modified
  2408. // changes in memory.
  2409. //
  2410. //
  2411. // Round FOffset and EndOffset up to sector boundaries, since
  2412. // we will be doing disk I/O, and calculate size left.
  2413. //
  2414. ASSERT( (FOffset.LowPart & SectorMask) == 0 );
  2415. FOffset.QuadPart += (LONGLONG)SectorMask;
  2416. FOffset.LowPart &= ~SectorMask;
  2417. SizeLeft.QuadPart = EndOffset->QuadPart + (LONGLONG)SectorMask;
  2418. SizeLeft.LowPart &= ~SectorMask;
  2419. SizeLeft.QuadPart -= FOffset.QuadPart;
  2420. ASSERT( (FOffset.LowPart & SectorMask) == 0 );
  2421. ASSERT( (SizeLeft.LowPart & SectorMask) == 0 );
  2422. if (SizeLeft.QuadPart == 0) {
  2423. return TRUE;
  2424. }
  2425. //
  2426. // try-finally to guarantee cleanup.
  2427. //
  2428. try {
  2429. //
  2430. // Allocate a page to hold the zeros we will write, and
  2431. // zero it.
  2432. //
  2433. ZeroBytes = NumberOfColors * PAGE_SIZE;
  2434. if (SizeLeft.HighPart == 0 && SizeLeft.LowPart < ZeroBytes) {
  2435. ZeroBytes = SizeLeft.LowPart;
  2436. }
  2437. Zeros = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolCacheAligned, ZeroBytes, 'eZcC' );
  2438. if (Zeros != NULL) {
  2439. //
  2440. // Allocate and initialize an Mdl to describe the zeros
  2441. // we need to transfer. Allocate to cover the maximum
  2442. // size required, and we will use and reuse it in the
  2443. // loop below, initialized correctly.
  2444. //
  2445. if (SizeLeft.HighPart == 0 && SizeLeft.LowPart < MAX_ZERO_TRANSFER) {
  2446. ZeroTransfer = SizeLeft.LowPart;
  2447. } else {
  2448. //
  2449. // See how aggressive we can afford to be.
  2450. //
  2451. if (InterlockedIncrement( &CcAggressiveZeroCount ) <= CcAggressiveZeroThreshold) {
  2452. AggressiveZero = TRUE;
  2453. ZeroTransfer = MAX_ZERO_TRANSFER;
  2454. } else {
  2455. InterlockedDecrement( &CcAggressiveZeroCount );
  2456. ZeroTransfer = MIN_ZERO_TRANSFER;
  2457. }
  2458. }
  2459. //
  2460. // Since the maximum zero may start at a very aggresive level, fall back
  2461. // until we really have to give up. Since filter drivers, filesystems and
  2462. // even storage drivers may need to map this Mdl, we have to pre-map it
  2463. // into system space so that we know enough PTEs are available. We also
  2464. // need to throttle our consumption of virtual addresses based on the size
  2465. // of the system and the number of parallel instances of this work outstanding.
  2466. // This may be a bit of overkill, but since running out of PTEs is a fatal
  2467. // event for the rest of the system, try to help out while still being fast.
  2468. //
  2469. while (TRUE) {
  2470. //
  2471. // Spin down trying to get an MDL which can describe our operation.
  2472. //
  2473. while (TRUE) {
  2474. ZeroMdl = IoAllocateMdl( Zeros, ZeroTransfer, FALSE, FALSE, NULL );
  2475. //
  2476. // Throttle ourselves to what we've physically allocated. Note that
  2477. // we could have started with an odd multiple of this number. If we
  2478. // tried for exactly that size and failed, we're toast.
  2479. //
  2480. if (ZeroMdl || ZeroTransfer == ZeroBytes) {
  2481. break;
  2482. }
  2483. Fall_Back:
  2484. //
  2485. // Fallback by half and round down to a sector multiple.
  2486. //
  2487. ZeroTransfer /= 2;
  2488. ZeroTransfer &= ~SectorMask;
  2489. if (ZeroTransfer < ZeroBytes) {
  2490. ZeroTransfer = ZeroBytes;
  2491. }
  2492. ASSERT( (ZeroTransfer & SectorMask) == 0 && ZeroTransfer != 0);
  2493. }
  2494. if (ZeroMdl == NULL) {
  2495. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2496. }
  2497. //
  2498. // If we have throttled all the way down, stop and just build a
  2499. // simple MDL describing our previous allocation.
  2500. //
  2501. if (ZeroTransfer == ZeroBytes) {
  2502. MmBuildMdlForNonPagedPool( ZeroMdl );
  2503. break;
  2504. }
  2505. //
  2506. // Now we will temporarily lock the allocated pages
  2507. // only, and then replicate the page frame numbers through
  2508. // the entire Mdl to keep writing the same pages of zeros.
  2509. //
  2510. // It would be nice if Mm exported a way for us to not have
  2511. // to pull the Mdl apart and rebuild it ourselves, but this
  2512. // is so bizzare a purpose as to be tolerable.
  2513. //
  2514. SavedByteCount = ZeroMdl->ByteCount;
  2515. ZeroMdl->ByteCount = ZeroBytes;
  2516. MmBuildMdlForNonPagedPool( ZeroMdl );
  2517. ZeroMdl->MdlFlags &= ~MDL_SOURCE_IS_NONPAGED_POOL;
  2518. ZeroMdl->MdlFlags |= MDL_PAGES_LOCKED;
  2519. ZeroMdl->MappedSystemVa = NULL;
  2520. ZeroMdl->ByteCount = SavedByteCount;
  2521. Page = MmGetMdlPfnArray( ZeroMdl );
  2522. for (i = NumberOfColors;
  2523. i < (ADDRESS_AND_SIZE_TO_SPAN_PAGES( 0, SavedByteCount ));
  2524. i++) {
  2525. *(Page + i) = *(Page + i - NumberOfColors);
  2526. }
  2527. if (MmGetSystemAddressForMdlSafe( ZeroMdl, LowPagePriority ) == NULL) {
  2528. //
  2529. // Blow away this Mdl and trim for the retry. Since it didn't
  2530. // get mapped, there is nothing fancy to do.
  2531. //
  2532. IoFreeMdl( ZeroMdl );
  2533. goto Fall_Back;
  2534. }
  2535. break;
  2536. }
  2537. //
  2538. // We failed to allocate the space we wanted, so we will go to
  2539. // half of a page and limp along.
  2540. //
  2541. } else {
  2542. //
  2543. // Of course, if we have a device which has large sectors, that defines
  2544. // the lower limit of our attempt.
  2545. //
  2546. if (IoGetRelatedDeviceObject(FileObject)->SectorSize < PAGE_SIZE / 2) {
  2547. ZeroBytes = PAGE_SIZE / 2;
  2548. Zeros = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolCacheAligned, ZeroBytes, 'eZcC' );
  2549. }
  2550. //
  2551. // If we cannot get even that much, then let's write a sector at a time.
  2552. //
  2553. if (Zeros == NULL) {
  2554. ZeroBytes = IoGetRelatedDeviceObject(FileObject)->SectorSize;
  2555. Zeros = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolCacheAligned, ZeroBytes, 'eZcC' );
  2556. //
  2557. // If we cannot get even the minimum, we have to give up.
  2558. //
  2559. if (Zeros == NULL) {
  2560. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2561. }
  2562. }
  2563. //
  2564. // Allocate and initialize an Mdl to describe the zeros
  2565. // we need to transfer. Allocate to cover the maximum
  2566. // size required, and we will use and reuse it in the
  2567. // loop below, initialized correctly.
  2568. //
  2569. ZeroTransfer = ZeroBytes;
  2570. ZeroMdl = IoAllocateMdl( Zeros, ZeroBytes, FALSE, FALSE, NULL );
  2571. ASSERT( (ZeroTransfer & SectorMask) == 0 );
  2572. if (ZeroMdl == NULL) {
  2573. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2574. }
  2575. //
  2576. // Now we will lock and map the allocated pages.
  2577. //
  2578. MmBuildMdlForNonPagedPool( ZeroMdl );
  2579. ASSERT( ZeroMdl->MappedSystemVa == Zeros );
  2580. }
  2581. //
  2582. // Zero the buffer now.
  2583. //
  2584. RtlZeroMemory( Zeros, ZeroBytes );
  2585. //
  2586. // We have a mapped and zeroed range back by an MDL to use. Note the
  2587. // size we have for cleanup, since we will possibly wind this down
  2588. // over the operation.
  2589. //
  2590. ASSERT( MmGetSystemAddressForMdl(ZeroMdl) );
  2591. MaxBytesMappedInMdl = ZeroMdl->ByteCount;
  2592. //
  2593. // Now loop to write buffers full of zeros through to the file
  2594. // until we reach the starting Vbn for the transfer.
  2595. //
  2596. ASSERT( ZeroTransfer != 0 &&
  2597. (ZeroTransfer & SectorMask) == 0 &&
  2598. (SizeLeft.LowPart & SectorMask) == 0 );
  2599. while ( SizeLeft.QuadPart != 0 ) {
  2600. IO_STATUS_BLOCK IoStatus;
  2601. NTSTATUS Status;
  2602. KEVENT Event;
  2603. //
  2604. // See if we really need to write that many zeros, and
  2605. // trim the size back if not.
  2606. //
  2607. if ( (LONGLONG)ZeroTransfer > SizeLeft.QuadPart ) {
  2608. ZeroTransfer = SizeLeft.LowPart;
  2609. }
  2610. //
  2611. // (Re)initialize the kernel event to FALSE.
  2612. //
  2613. KeInitializeEvent( &Event, NotificationEvent, FALSE );
  2614. //
  2615. // Initiate and wait for the synchronous transfer.
  2616. //
  2617. ZeroMdl->ByteCount = ZeroTransfer;
  2618. Status = IoSynchronousPageWrite( FileObject,
  2619. ZeroMdl,
  2620. &FOffset,
  2621. &Event,
  2622. &IoStatus );
  2623. //
  2624. // If pending is returned (which is a successful status),
  2625. // we must wait for the request to complete.
  2626. //
  2627. if (Status == STATUS_PENDING) {
  2628. KeWaitForSingleObject( &Event,
  2629. Executive,
  2630. KernelMode,
  2631. FALSE,
  2632. (PLARGE_INTEGER)NULL);
  2633. }
  2634. //
  2635. // If we got an error back in Status, then the Iosb
  2636. // was not written, so we will just copy the status
  2637. // there, then test the final status after that.
  2638. //
  2639. if (!NT_SUCCESS(Status)) {
  2640. ExRaiseStatus( Status );
  2641. }
  2642. if (!NT_SUCCESS(IoStatus.Status)) {
  2643. ExRaiseStatus( IoStatus.Status );
  2644. }
  2645. //
  2646. // If we succeeded, then update where we are at by how much
  2647. // we wrote, and loop back to see if there is more.
  2648. //
  2649. FOffset.QuadPart = FOffset.QuadPart + (LONGLONG)ZeroTransfer;
  2650. SizeLeft.QuadPart = SizeLeft.QuadPart - (LONGLONG)ZeroTransfer;
  2651. }
  2652. }
  2653. finally{
  2654. //
  2655. // Clean up anything from zeroing pages on a noncached
  2656. // write.
  2657. //
  2658. if (ZeroMdl != NULL) {
  2659. if ((MaxBytesMappedInMdl != 0) &&
  2660. !FlagOn(ZeroMdl->MdlFlags, MDL_SOURCE_IS_NONPAGED_POOL)) {
  2661. ZeroMdl->ByteCount = MaxBytesMappedInMdl;
  2662. MmUnmapLockedPages (ZeroMdl->MappedSystemVa, ZeroMdl);
  2663. }
  2664. IoFreeMdl( ZeroMdl );
  2665. }
  2666. if (AggressiveZero) {
  2667. InterlockedDecrement( &CcAggressiveZeroCount );
  2668. }
  2669. if (Zeros != NULL) {
  2670. ExFreePool( Zeros );
  2671. }
  2672. DebugTrace(-1, me, "CcZeroData -> TRUE\n", 0 );
  2673. }
  2674. return TRUE;
  2675. }
  2676. PFILE_OBJECT
  2677. CcGetFileObjectFromSectionPtrs (
  2678. IN PSECTION_OBJECT_POINTERS SectionObjectPointer
  2679. )
  2680. /*++
  2681. This routine may be used to retrieve a pointer to the FileObject that the
  2682. Cache Manager is using for a given file from the Section Object Pointers
  2683. in the nonpaged File System structure Fcb. The use of this function is
  2684. intended for exceptional use unrelated to the processing of user requests,
  2685. when the File System would otherwise not have a FileObject at its disposal.
  2686. An example is for mount verification.
  2687. Note that the File System is responsible for insuring that the File
  2688. Object does not go away while in use. It is impossible for the Cache
  2689. Manager to guarantee this.
  2690. Arguments:
  2691. SectionObjectPointer - A pointer to the Section Object Pointers
  2692. structure in the nonpaged Fcb.
  2693. Return Value:
  2694. Pointer to the File Object, or NULL if the file is not cached or no
  2695. longer cached
  2696. --*/
  2697. {
  2698. KIRQL OldIrql;
  2699. PFILE_OBJECT FileObject = NULL;
  2700. //
  2701. // Serialize with Creation/Deletion of all Shared CacheMaps
  2702. //
  2703. CcAcquireMasterLock( &OldIrql );
  2704. if (SectionObjectPointer->SharedCacheMap != NULL) {
  2705. FileObject = ((PSHARED_CACHE_MAP)SectionObjectPointer->SharedCacheMap)->FileObject;
  2706. }
  2707. CcReleaseMasterLock( OldIrql );
  2708. return FileObject;
  2709. }
  2710. PFILE_OBJECT
  2711. CcGetFileObjectFromBcb (
  2712. IN PVOID Bcb
  2713. )
  2714. /*++
  2715. This routine may be used to retrieve a pointer to the FileObject that the
  2716. Cache Manager is using for a given file from a Bcb of that file.
  2717. Note that the File System is responsible for insuring that the File
  2718. Object does not go away while in use. It is impossible for the Cache
  2719. Manager to guarantee this.
  2720. Arguments:
  2721. Bcb - A pointer to the pinned Bcb.
  2722. Return Value:
  2723. Pointer to the File Object, or NULL if the file is not cached or no
  2724. longer cached
  2725. --*/
  2726. {
  2727. return ((PBCB)Bcb)->SharedCacheMap->FileObject;
  2728. }