Windows NT 4.0 source code leak
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.

1302 lines
29 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. cache.c
  5. Abstract:
  6. This module implements the NtFlushBuffersFile API for NT and provides
  7. support routines for read and write caches.
  8. Author:
  9. Colin Watson (ColinW) 22-Jan-1991
  10. Revision History:
  11. 22-Jan-1991 colinw
  12. Created
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. VOID
  17. FindOldestFcb(
  18. IN PFCB FcbToCheck,
  19. IN PVOID Ctx
  20. );
  21. VOID
  22. PurgeDormantCachedFile(
  23. IN PFCB FcbToCheck,
  24. IN PVOID Context
  25. );
  26. VOID
  27. PurgeAnyDormantCachedFile(
  28. IN PFCB FcbToCheck,
  29. IN PVOID Context
  30. );
  31. #ifdef ALLOC_PRAGMA
  32. #pragma alloc_text(PAGE, RdrFsdFlushBuffersFile)
  33. #pragma alloc_text(PAGE, RdrFspFlushBuffersFile)
  34. #pragma alloc_text(PAGE, RdrFscFlushBuffersFile)
  35. #pragma alloc_text(PAGE, RdrAcquireFcbForLazyWrite)
  36. #pragma alloc_text(PAGE, RdrReleaseFcbFromLazyWrite)
  37. #pragma alloc_text(PAGE, RdrAcquireFcbForReadAhead)
  38. #pragma alloc_text(PAGE, RdrReleaseFcbFromReadAhead)
  39. #pragma alloc_text(PAGE, RdrPurgeCacheFile)
  40. #pragma alloc_text(PAGE, RdrUninitializeCacheMap)
  41. #pragma alloc_text(PAGE, RdrFlushCacheFile)
  42. #pragma alloc_text(PAGE, RdrPurgeDormantCachedFiles)
  43. #pragma alloc_text(PAGE, RdrSetDormantCachedFile)
  44. #pragma alloc_text(PAGE, RdrPurgeDormantFilesOnConnection)
  45. #pragma alloc_text(PAGE, PurgeDormantCachedFile)
  46. #pragma alloc_text(PAGE, PurgeAnyDormantCachedFile)
  47. #pragma alloc_text(PAGE, FindOldestFcb)
  48. #endif
  49. NTSTATUS
  50. RdrFsdFlushBuffersFile (
  51. IN PFS_DEVICE_OBJECT DeviceObject,
  52. IN PIRP Irp
  53. )
  54. /*++
  55. Routine Description:
  56. This routine implements the FSD version of the NtFlushBuffersFile API.
  57. Arguments:
  58. IN PFS_DEVICE_OBJECT DeviceObject, - Supplies the device object for this
  59. request
  60. IN PIRP Irp - Supplies the IRP that describes the request
  61. Return Value:
  62. NTSTATUS - Status of operation
  63. --*/
  64. {
  65. NTSTATUS Status;
  66. PAGED_CODE();
  67. dprintf(DPRT_READWRITE, ("RdrFsdFlushBuffersFile\n"));
  68. FsRtlEnterFileSystem();
  69. //
  70. // Decide if we can block for I/O
  71. //
  72. try {
  73. Status = RdrFscFlushBuffersFile( CanFsdWait( Irp ), DeviceObject, Irp );
  74. } except (RdrExceptionFilter(GetExceptionInformation(), &Status)) {
  75. Status = RdrProcessException(Irp, Status);
  76. }
  77. dprintf(DPRT_READWRITE, ("RdrFsdFlushBuffersFile -> %X\n", Status));
  78. FsRtlExitFileSystem();
  79. return Status;
  80. }
  81. NTSTATUS
  82. RdrFspFlushBuffersFile (
  83. IN PFS_DEVICE_OBJECT DeviceObject,
  84. IN PIRP Irp
  85. )
  86. /*++
  87. Routine Description:
  88. This routine implements the FSP version of the NtFlushBuffersFile API.
  89. API.
  90. Arguments:
  91. IN PFS_DEVICE_OBJECT DeviceObject, - Supplies the device object for this
  92. request
  93. IN PIRP Irp - Supplies the IRP that describes the request
  94. Return Value:
  95. NTSTATUS - Status of operation
  96. --*/
  97. {
  98. PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
  99. PAGED_CODE();
  100. dprintf(DPRT_READWRITE, ("RdrFspFlushBuffersFile\n"));
  101. //
  102. // Call the common routine. The Fsp is always allowed to block
  103. //
  104. return RdrFscFlushBuffersFile( TRUE, DeviceObject, Irp );
  105. }
  106. NTSTATUS
  107. RdrFscFlushBuffersFile (
  108. IN BOOLEAN Wait,
  109. IN PFS_DEVICE_OBJECT DeviceObject,
  110. IN PIRP Irp
  111. )
  112. /*++
  113. Routine Description:
  114. This routine implements the FSD version of the NtFlushBuffersFile API.
  115. API.
  116. Arguments:
  117. IN PFS_DEVICE_OBJECT DeviceObject, - Supplies the device object for this
  118. request
  119. IN PIRP Irp - Supplies the IRP that describes the request
  120. Return Value:
  121. NTSTATUS - Status of operation
  122. --*/
  123. {
  124. NTSTATUS Status;
  125. PIO_STACK_LOCATION IrpSp;
  126. BOOLEAN FcbLocked = FALSE;
  127. PICB Icb;
  128. PSMB_BUFFER SMBBuffer;
  129. PSMB_HEADER Smb;
  130. PREQ_FLUSH FlushFile;
  131. PMDL SendMDL;
  132. ULONG SendLength;
  133. PAGED_CODE();
  134. //
  135. // Get the current stack location
  136. //
  137. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  138. Icb = ICB_OF(IrpSp);
  139. dprintf(DPRT_READWRITE, ("RdrFscFlushBuffersFile. Wait: %lx, Irp:%08lx, FileObject: %08lx\n", Wait, Irp, IrpSp->FileObject));
  140. try {
  141. if (!RdrAcquireFcbLock(Icb->Fcb, ExclusiveLock, Wait)) {
  142. try_return(Status = STATUS_PENDING);
  143. }
  144. FcbLocked = TRUE;
  145. Status = RdrIsOperationValid(Icb, IrpSp->MajorFunction, IrpSp->FileObject);
  146. if (!NT_SUCCESS(Status)) {
  147. try_return(Status);
  148. }
  149. try {
  150. dprintf(DPRT_READWRITE, ("RdrFscFlushBuffersFile. Type:%lx\n", Icb->Type));
  151. switch ( Icb->Type ) {
  152. case NamedPipe:
  153. if (!Wait) {
  154. try_return(Status = STATUS_PENDING);
  155. }
  156. Status = RdrNpFlushBuffers( Wait, Irp, Icb);
  157. try_return(Status);
  158. break;
  159. case DiskFile:
  160. //
  161. // If this file is cached, flush the cache contents
  162. //
  163. if (!Wait) {
  164. try_return(Status = STATUS_PENDING);
  165. }
  166. dprintf(DPRT_READWRITE, ("Flush cache for file %lx\n", IrpSp->FileObject));
  167. Status = RdrFlushWriteBufferForFile(Irp, Icb, (BOOLEAN)!RdrUseAsyncWriteBehind);
  168. if (!NT_SUCCESS(Status)) {
  169. try_return(Status);
  170. }
  171. //RdrLog(( "ccflush1", &Icb->Fcb->FileName, 1, 0xffffffff ));
  172. CcFlushCache(&Icb->NonPagedFcb->SectionObjectPointer, NULL, 0, &Irp->IoStatus);
  173. if (!NT_SUCCESS(Status)) {
  174. try_return(Status);
  175. }
  176. //
  177. // Serialize behind paging I/O to ensure flush is done.
  178. //
  179. ExAcquireResourceExclusive(Icb->Fcb->Header.PagingIoResource, TRUE);
  180. ExReleaseResource(Icb->Fcb->Header.PagingIoResource);
  181. //
  182. // Send a flush SMB to the server.
  183. //
  184. if ((SMBBuffer = RdrAllocateSMBBuffer()) == NULL) {
  185. try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
  186. }
  187. //
  188. // Build the SMB
  189. //
  190. Smb = (PSMB_HEADER)SMBBuffer->Buffer;
  191. Smb->Command = SMB_COM_FLUSH;
  192. FlushFile = (PREQ_FLUSH)(Smb+1);
  193. FlushFile->WordCount = 1;
  194. SmbPutUshort(&FlushFile->Fid, Icb->FileId);
  195. SmbPutUshort( &FlushFile->ByteCount, 0);
  196. SendLength = FlushFile->Buffer - (PUCHAR )(Smb);
  197. SendMDL = SMBBuffer->Mdl;
  198. SendMDL->ByteCount = SendLength;
  199. Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT, // Flags
  200. Irp,
  201. Icb->Fcb->Connection,
  202. SendMDL,
  203. NULL, // Only interested in the error code.
  204. Icb->Se);
  205. RdrFreeSMBBuffer(SMBBuffer);
  206. if (Status == STATUS_INVALID_HANDLE) {
  207. RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
  208. }
  209. try_return(NOTHING);
  210. break;
  211. default:
  212. try_return(Status = STATUS_SUCCESS);
  213. break;
  214. }
  215. } except (EXCEPTION_EXECUTE_HANDLER) {
  216. try_return(Status = GetExceptionCode());
  217. }
  218. try_exit:NOTHING;
  219. } finally {
  220. if (FcbLocked) {
  221. RdrReleaseFcbLock(Icb->Fcb);
  222. }
  223. }
  224. if ( Status == STATUS_PENDING ) {
  225. //
  226. // Need to block and caller requested thread not to block.
  227. //
  228. ASSERT (Wait == FALSE);
  229. RdrFsdPostToFsp(DeviceObject, Irp);
  230. } else {
  231. //
  232. // Complete the I/O request with the specified status.
  233. //
  234. RdrCompleteRequest(Irp, Status);
  235. }
  236. dprintf(DPRT_READWRITE, ("Returning status %X\n", Status));
  237. return Status;
  238. }
  239. BOOLEAN
  240. RdrAcquireFcbForLazyWrite (
  241. IN PVOID Context,
  242. IN BOOLEAN Wait
  243. )
  244. /*++
  245. Routine Description:
  246. This routine acquires an FCB for shared access for a lazy write
  247. operation.
  248. Arguments:
  249. IN PVOID Context - Supplies an FCB to lock.
  250. IN BOOLEAN Wait - True if we can block the callers thread for this request
  251. Return Value:
  252. None.
  253. --*/
  254. {
  255. PFCB Fcb = Context;
  256. BOOLEAN RetValue;
  257. PAGED_CODE();
  258. ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
  259. RetValue = ExAcquireResourceShared(Fcb->Header.PagingIoResource, Wait);
  260. if (RetValue) {
  261. //
  262. // Remember the thread ID of the lazy writer. We use this to
  263. // avoid updating valid data length if the write behind extends
  264. // valid data length.
  265. //
  266. Fcb->LazyWritingThread = PsGetCurrentThread();
  267. }
  268. return RetValue;
  269. }
  270. VOID
  271. RdrReleaseFcbFromLazyWrite (
  272. IN PVOID Context
  273. )
  274. /*++
  275. Routine Description:
  276. This routine releases an FCB that was acquired for a close operation.
  277. Arguments:
  278. IN PVOID Context - Supplies an FCB to lock.
  279. Return Value:
  280. None.
  281. --*/
  282. {
  283. PFCB Fcb = Context;
  284. PAGED_CODE();
  285. ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
  286. //
  287. // We're not doing a lazy write any more, we're done.
  288. //
  289. Fcb->LazyWritingThread = NULL;
  290. ExReleaseResource(Fcb->Header.PagingIoResource);
  291. }
  292. BOOLEAN
  293. RdrAcquireFcbForReadAhead (
  294. IN PVOID Context,
  295. IN BOOLEAN Wait
  296. )
  297. /*++
  298. Routine Description:
  299. This routine acquires an FCB for shared access for a read ahead operation.
  300. Arguments:
  301. IN PVOID Context - Supplies an FCB to lock.
  302. IN BOOLEAN Wait - True if we can tie up the callers thread for this request
  303. Return Value:
  304. None.
  305. --*/
  306. {
  307. PFCB Fcb = Context;
  308. BOOLEAN RetValue;
  309. PAGED_CODE();
  310. ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
  311. RetValue = RdrAcquireFcbLock(Fcb, SharedLock, Wait);
  312. return RetValue;
  313. }
  314. VOID
  315. RdrReleaseFcbFromReadAhead (
  316. IN PVOID Context
  317. )
  318. /*++
  319. Routine Description:
  320. This routine releases an FCB that was acquired for a close operation.
  321. Arguments:
  322. IN PVOID Context - Supplies an FCB to lock.
  323. Return Value:
  324. None.
  325. --*/
  326. {
  327. PFCB Fcb = Context;
  328. PAGED_CODE();
  329. ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
  330. RdrReleaseFcbLock(Fcb);
  331. }
  332. NTSTATUS
  333. RdrPurgeCacheFile (
  334. IN PFCB Fcb
  335. )
  336. /*++
  337. Routine Description:
  338. This routine will purge the specified file from the cache.
  339. Arguments:
  340. IN PFCB Fcb - Supplies an FCB for the file to purge.
  341. Return Value:
  342. NTSTATUS - Status of purge operation. This operation may raise.
  343. Note:
  344. The file must be locked exclusively before calling this routine.
  345. --*/
  346. {
  347. NTSTATUS Status = STATUS_SUCCESS;
  348. PFILE_OBJECT CacheFileObject;
  349. PLIST_ENTRY IcbEntry;
  350. PAGED_CODE();
  351. dprintf(DPRT_CACHE, ("RdrPurgeCacheFile Fcb:%lx (%wZ)\n", Fcb, &Fcb->FileName));
  352. ASSERT ((Fcb->NonPagedFcb->Type == DiskFile) ||
  353. (Fcb->NonPagedFcb->Type == Directory) ||
  354. (Fcb->NonPagedFcb->Type == FileOrDirectory));
  355. ASSERT (Fcb->NonPagedFcb->FileType == FileTypeDisk);
  356. ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  357. for (IcbEntry = Fcb->InstanceChain.Flink;
  358. IcbEntry != &Fcb->InstanceChain ;
  359. IcbEntry = IcbEntry->Flink) {
  360. PICB IcbToFlush = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
  361. if (IcbToFlush->Type == DiskFile &&
  362. IcbToFlush->Flags & ICB_OPENED) {
  363. Status = RdrFlushWriteBufferForFile(NULL, IcbToFlush, TRUE);
  364. }
  365. }
  366. //
  367. // Tell the cache manager to blow away all the references for all the
  368. // file objects associated with this FCB.
  369. //
  370. //RdrLog(( "ccpurge1", &Fcb->FileName, 2, 0xffffffff, 1 << 24 ));
  371. CcPurgeCacheSection(&Fcb->NonPagedFcb->SectionObjectPointer, NULL, 0, TRUE);
  372. //
  373. // Now try to get the cache file object from the cache manager,
  374. // and if there is none, we're done now.
  375. //
  376. CacheFileObject = CcGetFileObjectFromSectionPtrs(&Fcb->NonPagedFcb->SectionObjectPointer);
  377. dprintf(DPRT_CACHE, ("Removing file %lx (Fcb %lx) from the cache (hard)\n", CacheFileObject, Fcb));
  378. if (CacheFileObject != NULL) {
  379. //RdrLog(( "rdunini1", &Fcb->FileName, 0 ));
  380. RdrUninitializeCacheMap(CacheFileObject, &RdrZero);
  381. } else {
  382. //
  383. // Make sure that this thread owns the FCB.
  384. //
  385. ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  386. //
  387. // Release the lock on the FCB that our caller applied.
  388. //
  389. // We do this to make sure that other threads can come in while we
  390. // are waiting for the cache flush to complete.
  391. //
  392. RdrReleaseFcbLock(Fcb);
  393. ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  394. KeWaitForSingleObject(&Fcb->NonPagedFcb->PurgeCacheSynchronizer, Executive, KernelMode, FALSE, NULL);
  395. //
  396. // Re-acquire the FCB lock once we've waited for MM
  397. // finish forcing the section closed.
  398. //
  399. RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
  400. }
  401. //
  402. // Make sure that this thread owns the FCB.
  403. //
  404. ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  405. //
  406. // Release the lock on the FCB that our caller applied.
  407. //
  408. RdrReleaseFcbLock(Fcb);
  409. ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  410. //
  411. // If this is an executable opened over the net, then
  412. // its possible that the executables image section
  413. // might still be kept open.
  414. //
  415. // Ask MM to flush the section closed. This will fail
  416. // if the executable in question is still running.
  417. //
  418. //RdrLog(( "mmflush1", &Fcb->FileName, 1, MmFlushForWrite ));
  419. MmFlushImageSection(&Fcb->NonPagedFcb->SectionObjectPointer, MmFlushForWrite);
  420. //
  421. // There is also a possiblity that there is a user section
  422. // open on this file, in which case we need to force the
  423. // section closed to make sure that they are cleaned up.
  424. //
  425. //RdrLog(( "mmforce1", &Fcb->FileName, 1, TRUE ));
  426. MmForceSectionClosed(&Fcb->NonPagedFcb->SectionObjectPointer, TRUE);
  427. //
  428. // Re-acquire the FCB lock once we've waited for MM
  429. // finish forcing the section closed.
  430. //
  431. RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
  432. ASSERT(Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);;
  433. ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  434. dprintf(DPRT_CACHE, ("RdrPurgeCacheFile returning STATUS_SUCCESS, Fcb: %lx\n", Fcb));
  435. return STATUS_SUCCESS;
  436. }
  437. BOOLEAN
  438. RdrUninitializeCacheMap(
  439. IN PFILE_OBJECT FileObject,
  440. IN PLARGE_INTEGER TruncateSize
  441. )
  442. /*++
  443. Routine Description:
  444. This routine is a redirector wrapper for CcUninitializeCacheMap.
  445. Arguments:
  446. IN PFILE_OBJECT FileObject - Supplies the file object for the file to purge.
  447. IN PLARGE_INTEGER TruncateSize - Specifies the new size for the file.
  448. Return Value:
  449. BOOLEAN - TRUE if file has been immediately purged, FALSE if we had to wait.
  450. Note:
  451. The file must be locked exclusively before calling this routine.
  452. --*/
  453. {
  454. BOOLEAN CacheReturnValue;
  455. CACHE_UNINITIALIZE_EVENT PurgeCompleteEvent;
  456. PFCB Fcb = FileObject->FsContext;
  457. PAGED_CODE();
  458. ASSERT (Fcb->Header.NodeTypeCode == STRUCTURE_SIGNATURE_FCB);
  459. //
  460. // Make sure that this thread owns the FCB.
  461. //
  462. ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  463. //
  464. // In order to guarantee that only one thread is calling
  465. // RdrPurgeCacheFile, we reset this event to the
  466. // not-signalled state before calling CcUninitializeCacheMap,
  467. // and then set it when we exit. If any other threads come in
  468. // while we are waiting on the event, they will find that
  469. // CacheFileObject is NULL, and thus wait until the cache purge
  470. // completes.
  471. //
  472. KeClearEvent(&Fcb->NonPagedFcb->PurgeCacheSynchronizer);
  473. //
  474. // Now uninitialize the cache managers own file object. This is
  475. // done basically simply to allow us to wait until the cache purge
  476. // is complete.
  477. //
  478. KeInitializeEvent(&PurgeCompleteEvent.Event, SynchronizationEvent, FALSE);
  479. //RdrLog(( "ccunini1", &Fcb->FileName, 2,
  480. // (TruncateSize == NULL) ? 0xffffffff : TruncateSize->LowPart,
  481. // (ULONG)&PurgeCompleteEvent ));
  482. CacheReturnValue = CcUninitializeCacheMap(FileObject, TruncateSize, &PurgeCompleteEvent);
  483. //
  484. // Release the lock on the FCB that our caller applied.
  485. //
  486. RdrReleaseFcbLock(Fcb);
  487. //
  488. // Make sure that this thread doesn't own the FCB.
  489. //
  490. ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  491. //
  492. // Now wait for the cache manager to finish purging the file.
  493. //
  494. KeWaitForSingleObject(&PurgeCompleteEvent.Event,
  495. Executive,
  496. KernelMode,
  497. FALSE,
  498. NULL);
  499. //
  500. // Re-acquire the FCB lock once we've waited for the
  501. // cache manager to finish the uninitialize.
  502. //
  503. RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
  504. //
  505. // Now set the purge cache event to the signalled state to allow
  506. // other threads waiting on the cache purge to continue.
  507. //
  508. KeSetEvent(&Fcb->NonPagedFcb->PurgeCacheSynchronizer, 0, FALSE);
  509. return(CacheReturnValue);
  510. }
  511. NTSTATUS
  512. RdrFlushCacheFile (
  513. IN PFCB Fcb
  514. )
  515. /*++
  516. Routine Description:
  517. This routine will purge the specified file from the cache.
  518. Arguments:
  519. IN PFCB Fcb - Supplies an FCB for the file to purge.
  520. Return Value:
  521. NTSTATUS - Status of purge operation. This operation may raise.
  522. Note:
  523. The file must be locked exclusively before calling this routine.
  524. --*/
  525. {
  526. IO_STATUS_BLOCK IoStatus;
  527. PLIST_ENTRY IcbEntry;
  528. NTSTATUS Status;
  529. PAGED_CODE();
  530. dprintf(DPRT_CACHE, ("RdrFlushCacheFile Fcb:%lx (%wZ)\n", Fcb, &Fcb->FileName));
  531. ASSERT ((Fcb->NonPagedFcb->Type == DiskFile) ||
  532. (Fcb->NonPagedFcb->Type == Directory) ||
  533. (Fcb->NonPagedFcb->Type == FileOrDirectory));
  534. ASSERT (Fcb->NonPagedFcb->FileType == FileTypeDisk);
  535. ASSERT (ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  536. for (IcbEntry = Fcb->InstanceChain.Flink ;
  537. IcbEntry != &Fcb->InstanceChain ;
  538. IcbEntry = IcbEntry->Flink) {
  539. PICB Icb = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
  540. ASSERT (Icb->Signature == STRUCTURE_SIGNATURE_ICB);
  541. if (Icb->Type == DiskFile &&
  542. FlagOn(Icb->Flags, ICB_OPENED)) {
  543. //
  544. // Initiate a flush of the write behind data for this file.
  545. //
  546. Status = RdrFlushWriteBufferForFile(NULL, Icb, (BOOLEAN)!RdrUseAsyncWriteBehind);
  547. if (!NT_SUCCESS(Status)) {
  548. return Status;
  549. }
  550. //
  551. // Now wait for the flush operation on the file to complete.
  552. //
  553. RdrWaitForWriteBehindOperation(Icb);
  554. }
  555. }
  556. //
  557. // Flush dirty data for this file object from the cache.
  558. //
  559. //RdrLog(( "ccflush2", &Fcb->FileName, 1, 0xffffffff ));
  560. CcFlushCache(&Fcb->NonPagedFcb->SectionObjectPointer, NULL, 0, &IoStatus);
  561. if (NT_SUCCESS(IoStatus.Status)) {
  562. //
  563. // Serialize behind paging I/O to ensure flush is done.
  564. //
  565. ExAcquireResourceExclusive(Fcb->Header.PagingIoResource, TRUE);
  566. ExReleaseResource(Fcb->Header.PagingIoResource);
  567. }
  568. //
  569. // At this point, dirty data should be flushed for this file
  570. //
  571. return IoStatus.Status;
  572. }
  573. typedef struct _FINDOLDESTFCB {
  574. PFCB OldestFcb;
  575. ULONG NumberOfDormantCachedFiles;
  576. } FINDOLDESTFCB, *PFINDOLDESTFCB;
  577. VOID
  578. RdrSetDormantCachedFile(
  579. IN PFCB Fcb
  580. )
  581. /*++
  582. Routine Description:
  583. This routine will mark a specified FCB as being dormant.
  584. Arguments:
  585. IN PFCB Fcb - Fcb to mark as being dormant.
  586. Return Value:
  587. None.
  588. Note:
  589. This routine assumes that the FCB is currently locked.
  590. --*/
  591. {
  592. PAGED_CODE();
  593. //
  594. // If we are in 'TurboMode' we will cache all of the files we can, without
  595. // limit. This is for benchmarks
  596. //
  597. if( RdrTurboMode == FALSE ) {
  598. FINDOLDESTFCB Context;
  599. BOOLEAN FcbLocked = FALSE;
  600. Context.NumberOfDormantCachedFiles = 0;
  601. Context.OldestFcb = NULL;
  602. //
  603. // This thread cannot own the FCB resource on entry. If it does, we
  604. // might deadlock.
  605. //
  606. ASSERT (!ExIsResourceAcquiredExclusive(Fcb->Header.Resource));
  607. RdrForeachFcbOnConnection (Fcb->Connection, NoLock, FindOldestFcb, &Context);
  608. //
  609. // Now that we've scanned the list to find the oldest cached file,
  610. // we want to pull this file out of the list if we've reached our
  611. // limit on dormant cached files.
  612. //
  613. if (Context.NumberOfDormantCachedFiles >= RdrData.DormantFileLimit) {
  614. //
  615. // Lock this FCB to protect the DormantTimeout field in the FCB.
  616. //
  617. RdrAcquireFcbLock(Context.OldestFcb, ExclusiveLock, TRUE);
  618. FcbLocked = TRUE;
  619. ASSERT (Context.OldestFcb != NULL);
  620. ASSERT (Context.OldestFcb != Fcb);
  621. //
  622. // If the oldest FCB is still dormant, purge it now. Note that
  623. // if this FCB is no longer dormant, we'll end up with an extra
  624. // dormant file, above the limit. So be it.
  625. //
  626. if (Context.OldestFcb->NumberOfOpens == 0) {
  627. //RdrLog(( "rdflush1", &Context.OldestFcb->FileName, 0 ));
  628. RdrFlushCacheFile(Context.OldestFcb);
  629. //RdrLog(( "rdpurge1", &Context.OldestFcb->FileName, 0 ));
  630. RdrPurgeCacheFile(Context.OldestFcb);
  631. }
  632. }
  633. //
  634. // We're done with the oldest FCB, we can release it now.
  635. //
  636. if (Context.OldestFcb != NULL) {
  637. BOOLEAN FcbDeleted;
  638. FcbDeleted = RdrDereferenceFcb(NULL, Context.OldestFcb->NonPagedFcb, FcbLocked, 0, NULL);
  639. // if (!FcbDeleted) {
  640. // ASSERT (Fcb->Header.Resource->Threads[0] != (ULONG) ExGetCurrentResourceThread());
  641. // }
  642. }
  643. }
  644. //
  645. // Lock this FCB to protect the DormantTimeout field in the FCB.
  646. //
  647. RdrAcquireFcbLock(Fcb, ExclusiveLock, TRUE);
  648. //
  649. // Even for benchmarks, we want to eventually push out file changes
  650. //
  651. if( Fcb->UpdatedFile || RdrTurboMode == FALSE ) {
  652. ULONG CacheFileTimeout;
  653. //
  654. // Mark this file as being dormant
  655. //
  656. //
  657. // If the timeout is -1, we don't want to ever purge dormant
  658. // files, otherwise we will set the dormant timeout appropriately.
  659. //
  660. CacheFileTimeout = RdrData.CachedFileTimeout;
  661. if (CacheFileTimeout != -1) {
  662. Fcb->DormantTimeout = RdrCurrentTime + CacheFileTimeout;
  663. }
  664. }
  665. RdrReleaseFcbLock(Fcb);
  666. //
  667. // Now set the hint to indicate that there might be a dormant file on this connection
  668. //
  669. Fcb->Connection->NumberOfDormantFiles = 1;
  670. }
  671. VOID
  672. FindOldestFcb(
  673. IN PFCB FcbToCheck,
  674. IN PVOID Ctx
  675. )
  676. /*++
  677. Routine Description:
  678. This routine is called on each FCB open on a connection to determine which
  679. of them is the oldest FCB.
  680. Arguments:
  681. IN PFCB FcbToCheck - Fcb to check
  682. IN PVOID Ctx - Context block - contains the oldest FCB pointer.
  683. IN OUT PBOOLEAN UnlockFcb - Set/Cleared to indicate if we should unlock the
  684. FCB in question when we dereference it.
  685. Return Value:
  686. None.
  687. --*/
  688. {
  689. PFINDOLDESTFCB Context = Ctx;
  690. PAGED_CODE();
  691. //
  692. // If this FCB is dormant, then it is a candidate for flushing.
  693. //
  694. //
  695. // Please note that files that are currently open have a dormant
  696. // timeout of -1, thus we will always skip over the file we are
  697. // going to mark as dormant.
  698. //
  699. if ((FcbToCheck->NonPagedFcb->Type == DiskFile)
  700. &&
  701. (FcbToCheck->DormantTimeout != 0xffffffff)
  702. &&
  703. (FcbToCheck->NumberOfOpens == 0)) {
  704. Context->NumberOfDormantCachedFiles += 1;
  705. if ((Context->OldestFcb == NULL) ||
  706. (FcbToCheck->DormantTimeout < Context->OldestFcb->DormantTimeout)) {
  707. BOOLEAN FcbDeleted;
  708. if (Context->OldestFcb != NULL) {
  709. FcbDeleted = RdrDereferenceFcb(NULL, Context->OldestFcb->NonPagedFcb, FALSE, 0, NULL);
  710. // if (!FcbDeleted) {
  711. // ASSERT (Fcb->Header.Resource->Threads[0] != (ULONG) ExGetCurrentResourceThread());
  712. // }
  713. }
  714. //
  715. // Reference the new oldest FCB to make sure it doesn't
  716. // go away.
  717. //
  718. Context->OldestFcb = FcbToCheck;
  719. RdrReferenceFcb(Context->OldestFcb->NonPagedFcb);
  720. }
  721. }
  722. }
  723. VOID
  724. RdrPurgeDormantCachedFiles (
  725. VOID
  726. )
  727. /*++
  728. Routine Description:
  729. This routine walks the FCB database and purges all cached files that have
  730. been dormant for longer than the global dormant timeout.
  731. Arguments:
  732. None
  733. Return Value:
  734. None.
  735. --*/
  736. {
  737. PAGED_CODE();
  738. //
  739. // If the discardable code reference count is 0, this means that there are
  740. // no open files that can possibly be dormant (the only files left are
  741. // either tree connections or are handles to the redirector directly),
  742. // so we can simply early out.
  743. //
  744. if (!RdrIsDiscardableCodeReferenced(RdrFileDiscardableSection)) {
  745. return;
  746. }
  747. RdrReferenceDiscardableCode(RdrFileDiscardableSection);
  748. RdrForeachFcb(NoLock, PurgeDormantCachedFile, NULL);
  749. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  750. }
  751. VOID
  752. PurgeDormantCachedFile(
  753. IN PFCB FcbToCheck,
  754. IN PVOID Context
  755. )
  756. /*++
  757. Routine Description:
  758. This routine will purge a file from the cache if it is dormant.
  759. Arguments:
  760. None
  761. Return Value:
  762. None.
  763. --*/
  764. {
  765. PAGED_CODE();
  766. // ASSERT (ExIsResourceAcquiredExclusive(FcbToCheck->Header.Resource));
  767. //
  768. // If this FCB is dormant, and is older than the dormant file timeout,
  769. // then it is to be flushed.
  770. //
  771. if ((FcbToCheck->NonPagedFcb->Type == DiskFile) &&
  772. (FcbToCheck->NumberOfOpens == 0) &&
  773. (RdrCurrentTime > FcbToCheck->DormantTimeout)
  774. ) {
  775. RdrAcquireFcbLock(FcbToCheck, ExclusiveLock, TRUE);
  776. if ((FcbToCheck->NumberOfOpens == 0) &&
  777. (RdrCurrentTime > FcbToCheck->DormantTimeout)
  778. ) {
  779. //
  780. // Flush any write behind data outstanding on the file
  781. //
  782. //RdrLog(( "rdflush2", &FcbToCheck->FileName, 0 ));
  783. RdrFlushCacheFile(FcbToCheck);
  784. //
  785. // Now pull the file from the cache.
  786. //
  787. //RdrLog(( "rdpurge2", &FcbToCheck->FileName, 0 ));
  788. RdrPurgeCacheFile(FcbToCheck);
  789. }
  790. RdrReleaseFcbLock(FcbToCheck);
  791. }
  792. }
  793. VOID
  794. RdrPurgeDormantFilesOnConnection(
  795. IN PCONNECTLISTENTRY Connection
  796. )
  797. /*++
  798. Routine Description:
  799. This routine will close all dormant files on a connection. This fixes a series
  800. of problems such as copy a file and do a dir on the destination directory. If
  801. the connection is not purged then the dir will return 0 bytes as the file length.
  802. Arguments:
  803. IN PCONNECTLISTENTRY Connection - Connection to scan for dormant files.
  804. Return Value:
  805. None.
  806. Note:
  807. This routine assumes that the FCB is currently locked.
  808. --*/
  809. {
  810. PAGED_CODE();
  811. //
  812. // Early out if there aren't any dormant files on the connection.
  813. //
  814. if ( Connection->NumberOfDormantFiles == 0 ) {
  815. return;
  816. }
  817. Connection->NumberOfDormantFiles = 0;
  818. RdrForeachFcbOnConnection(Connection, NoLock, PurgeAnyDormantCachedFile, NULL);
  819. }
  820. VOID
  821. PurgeAnyDormantCachedFile(
  822. IN PFCB FcbToCheck,
  823. IN PVOID Context
  824. )
  825. /*++
  826. Routine Description:
  827. This routine will purge a file from the cache if it is dormant.
  828. Arguments:
  829. None
  830. Return Value:
  831. None.
  832. --*/
  833. {
  834. PAGED_CODE();
  835. //
  836. // If this FCB is dormant, then it is to be flushed.
  837. //
  838. if ((FcbToCheck->NonPagedFcb->Type == DiskFile) &&
  839. (FcbToCheck->UpdatedFile == TRUE) &&
  840. (FcbToCheck->NumberOfOpens == 0)) {
  841. RdrAcquireFcbLock(FcbToCheck, ExclusiveLock, TRUE);
  842. if (FcbToCheck->NumberOfOpens == 0) {
  843. //
  844. // Flush any write behind data outstanding on the file
  845. //
  846. //RdrLog(( "rdflush3", &FcbToCheck->FileName, 0 ));
  847. RdrFlushCacheFile(FcbToCheck);
  848. //
  849. // Now pull the file from the cache.
  850. //
  851. //RdrLog(( "rdpurge3", &FcbToCheck->FileName, 0 ));
  852. RdrPurgeCacheFile(FcbToCheck);
  853. }
  854. RdrReleaseFcbLock(FcbToCheck);
  855. }
  856. }