Leaked source code of windows server 2003
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.

3460 lines
113 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. RwCmpSup.c
  5. Abstract:
  6. This module implements the fast I/O routines for read/write compressed.
  7. Author:
  8. Tom Miller [TomM] 14-Jul-1991
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. VOID
  13. NtfsAddToCompressedMdlChain (
  14. IN OUT PMDL *MdlChain,
  15. IN PVOID MdlBuffer,
  16. IN ULONG MdlLength,
  17. IN PERESOURCE ResourceToRelease OPTIONAL,
  18. IN PBCB Bcb,
  19. IN LOCK_OPERATION Operation,
  20. IN ULONG IsCompressed
  21. );
  22. VOID
  23. NtfsSetMdlBcbOwners (
  24. IN PMDL MdlChain
  25. );
  26. VOID
  27. NtfsCleanupCompressedMdlChain (
  28. IN PMDL MdlChain,
  29. IN ULONG Error
  30. );
  31. #ifdef NTFS_RWC_DEBUG
  32. PRWC_HISTORY_ENTRY
  33. NtfsGetHistoryEntry (
  34. IN PSCB Scb
  35. );
  36. #ifdef ALLOC_PRAGMA
  37. #pragma alloc_text(PAGE, NtfsGetHistoryEntry)
  38. #endif
  39. #define CACHE_NTC_BCB (0x2FD)
  40. #define CACHE_NTC_OBCB (0x2FA)
  41. typedef struct _OBCB {
  42. //
  43. // Type and size of this record
  44. //
  45. CSHORT NodeTypeCode;
  46. CSHORT NodeByteSize;
  47. //
  48. // Byte FileOffset and and length of entire buffer
  49. //
  50. ULONG ByteLength;
  51. LARGE_INTEGER FileOffset;
  52. //
  53. // Vector of Bcb pointers.
  54. //
  55. PPUBLIC_BCB Bcbs[ANYSIZE_ARRAY];
  56. } OBCB;
  57. typedef OBCB *POBCB;
  58. PRWC_HISTORY_ENTRY
  59. NtfsGetHistoryEntry (
  60. IN PSCB Scb
  61. )
  62. {
  63. ULONG NextIndex;
  64. PAGED_CODE();
  65. //
  66. // Store and entry in the history buffer.
  67. //
  68. if (Scb->ScbType.Data.HistoryBuffer == NULL) {
  69. PVOID NewBuffer;
  70. NewBuffer = NtfsAllocatePool( PagedPool,
  71. sizeof( RWC_HISTORY_ENTRY ) * MAX_RWC_HISTORY_INDEX );
  72. RtlZeroMemory( NewBuffer, sizeof( RWC_HISTORY_ENTRY ) * MAX_RWC_HISTORY_INDEX );
  73. NtfsAcquireFsrtlHeader( Scb );
  74. if (Scb->ScbType.Data.HistoryBuffer == NULL) {
  75. Scb->ScbType.Data.HistoryBuffer = NewBuffer;
  76. } else {
  77. NtfsFreePool( NewBuffer );
  78. }
  79. NtfsReleaseFsrtlHeader( Scb );
  80. }
  81. NextIndex = InterlockedIncrement( &Scb->ScbType.Data.RwcIndex );
  82. if (NextIndex >= MAX_RWC_HISTORY_INDEX) {
  83. NextIndex = 0;
  84. InterlockedExchange( &Scb->ScbType.Data.RwcIndex, 0);
  85. }
  86. return Scb->ScbType.Data.HistoryBuffer + NextIndex;
  87. }
  88. #endif
  89. #ifdef ALLOC_PRAGMA
  90. #pragma alloc_text(PAGE, NtfsCopyReadC)
  91. #pragma alloc_text(PAGE, NtfsCompressedCopyRead)
  92. #pragma alloc_text(PAGE, NtfsCopyWriteC)
  93. #pragma alloc_text(PAGE, NtfsCompressedCopyWrite)
  94. #pragma alloc_text(PAGE, NtfsAddToCompressedMdlChain)
  95. #pragma alloc_text(PAGE, NtfsSetMdlBcbOwners)
  96. #pragma alloc_text(PAGE, NtfsSynchronizeCompressedIo)
  97. #pragma alloc_text(PAGE, NtfsSynchronizeUncompressedIo)
  98. #pragma alloc_text(PAGE, NtfsAcquireCompressionSync)
  99. #pragma alloc_text(PAGE, NtfsReleaseCompressionSync)
  100. #endif
  101. #ifdef NTFS_RWCMP_TRACE
  102. ULONG NtfsCompressionTrace = 0;
  103. #endif
  104. BOOLEAN
  105. NtfsCopyReadC (
  106. IN PFILE_OBJECT FileObject,
  107. IN PLARGE_INTEGER FileOffset,
  108. IN ULONG Length,
  109. IN ULONG LockKey,
  110. OUT PVOID Buffer,
  111. OUT PMDL *MdlChain,
  112. OUT PIO_STATUS_BLOCK IoStatus,
  113. OUT PCOMPRESSED_DATA_INFO CompressedDataInfo,
  114. IN ULONG CompressedDataInfoLength,
  115. IN PDEVICE_OBJECT DeviceObject
  116. )
  117. /*++
  118. Routine Description:
  119. This routine does a fast cached read bypassing the usual file system
  120. entry routine (i.e., without the Irp). It is used to do a copy read
  121. of a cached file object.
  122. Arguments:
  123. FileObject - Pointer to the file object being read.
  124. FileOffset - Byte offset in file for desired data.
  125. Length - Length of desired data in bytes.
  126. Buffer - Pointer to output buffer to which data should be copied.
  127. MdlChain - Pointer to an MdlChain pointer to receive an Mdl to describe
  128. the data in the cache.
  129. IoStatus - Pointer to standard I/O status block to receive the status
  130. for the transfer.
  131. CompressedDataInfo - Returns compressed data info with compressed chunk
  132. sizes
  133. CompressedDataInfoLength - Supplies the size of the info buffer in bytes.
  134. DeviceObject - Standard Fast I/O Device object input.
  135. Return Value:
  136. FALSE - if the data was not delivered for any reason
  137. TRUE - if the data is being delivered
  138. --*/
  139. {
  140. PNTFS_ADVANCED_FCB_HEADER Header;
  141. LONGLONG LocalOffset;
  142. PFAST_IO_DISPATCH FastIoDispatch;
  143. FILE_COMPRESSION_INFORMATION CompressionInformation;
  144. ULONG CompressionUnitSize, ChunkSize;
  145. BOOLEAN Status = TRUE;
  146. BOOLEAN DoingIoAtEof = FALSE;
  147. PAGED_CODE();
  148. //
  149. // You cannot have both a buffer to copy into and an MdlChain.
  150. //
  151. ASSERT((Buffer == NULL) || (MdlChain == NULL));
  152. //
  153. // Get out immediately if COW is not supported.
  154. //
  155. if (!NtfsEnableCompressedIO) { return FALSE; }
  156. //
  157. // Assume success.
  158. //
  159. IoStatus->Status = STATUS_SUCCESS;
  160. IoStatus->Information = Length;
  161. CompressedDataInfo->NumberOfChunks = 0;
  162. //
  163. // Special case a read of zero length
  164. //
  165. if (Length != 0) {
  166. //
  167. // Get a real pointer to the common fcb header
  168. //
  169. Header = (PNTFS_ADVANCED_FCB_HEADER)FileObject->FsContext;
  170. #ifdef NTFS_RWCMP_TRACE
  171. if (NtfsCompressionTrace && IsSyscache(Header)) {
  172. DbgPrint("NtfsCopyReadC: FO = %08lx, Len = %08lx\n", FileOffset->LowPart, Length );
  173. }
  174. #endif
  175. //
  176. // Enter the file system
  177. //
  178. FsRtlEnterFileSystem();
  179. //
  180. // Make our best guess on whether we need the file exclusive
  181. // or shared. Note that we do not check FileOffset->HighPart
  182. // until below.
  183. //
  184. Status = ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
  185. //
  186. // Now that the File is acquired shared, we can safely test if it
  187. // is really cached and if we can do fast i/o and if not, then
  188. // release the fcb and return.
  189. //
  190. if ((Header->FileObjectC == NULL) ||
  191. (Header->FileObjectC->PrivateCacheMap == NULL) ||
  192. (Header->IsFastIoPossible == FastIoIsNotPossible)) {
  193. Status = FALSE;
  194. goto Done;
  195. }
  196. //
  197. // Get the address of the driver object's Fast I/O dispatch structure.
  198. //
  199. FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
  200. //
  201. // Get the compression information for this file and return those fields.
  202. //
  203. NtfsFastIoQueryCompressionInfo( FileObject, &CompressionInformation, IoStatus );
  204. CompressedDataInfo->CompressionFormatAndEngine = CompressionInformation.CompressionFormat;
  205. CompressedDataInfo->CompressionUnitShift = CompressionInformation.CompressionUnitShift;
  206. CompressionUnitSize = 1 << CompressionInformation.CompressionUnitShift;
  207. CompressedDataInfo->ChunkShift = CompressionInformation.ChunkShift;
  208. CompressedDataInfo->ClusterShift = CompressionInformation.ClusterShift;
  209. CompressedDataInfo->Reserved = 0;
  210. ChunkSize = 1 << CompressionInformation.ChunkShift;
  211. //
  212. // If we either got an error in the call above, or the file size is less than
  213. // one chunk, then return an error. (Could be an Ntfs resident attribute.)
  214. if (!NT_SUCCESS(IoStatus->Status) || (Header->FileSize.QuadPart < ChunkSize)) {
  215. Status = FALSE;
  216. goto Done;
  217. }
  218. ASSERT((FileOffset->LowPart & (ChunkSize - 1)) == 0);
  219. //
  220. // If there is a normal cache section, flush that first, flushing integral
  221. // compression units so we don't write them twice.
  222. //
  223. if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  224. LocalOffset = FileOffset->QuadPart & ~(LONGLONG)(CompressionUnitSize - 1);
  225. CcFlushCache( FileObject->SectionObjectPointer,
  226. (PLARGE_INTEGER)&LocalOffset,
  227. (Length + (ULONG)(FileOffset->QuadPart - LocalOffset) + ChunkSize - 1) & ~(ChunkSize - 1),
  228. NULL );
  229. }
  230. //
  231. // Now synchronize with the FsRtl Header
  232. //
  233. ExAcquireFastMutex( Header->FastMutex );
  234. //
  235. // Now see if we are reading beyond ValidDataLength. We have to
  236. // do it now so that our reads are not nooped.
  237. //
  238. LocalOffset = FileOffset->QuadPart + (LONGLONG)Length;
  239. if (LocalOffset > Header->ValidDataLength.QuadPart) {
  240. //
  241. // We must serialize with anyone else doing I/O at beyond
  242. // ValidDataLength, and then remember if we need to declare
  243. // when we are done.
  244. //
  245. DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
  246. NtfsWaitForIoAtEof( Header, FileOffset, Length );
  247. //
  248. // Set the Flag if we are in fact beyond ValidDataLength.
  249. //
  250. if (DoingIoAtEof) {
  251. SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
  252. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  253. ((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
  254. } else {
  255. ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
  256. #endif
  257. }
  258. }
  259. ExReleaseFastMutex( Header->FastMutex );
  260. //
  261. // Check if fast I/O is questionable and if so then go ask the
  262. // file system the answer
  263. //
  264. if (Header->IsFastIoPossible == FastIoIsQuestionable) {
  265. //
  266. // All file systems that set "Is Questionable" had better support
  267. // fast I/O
  268. //
  269. ASSERT(FastIoDispatch != NULL);
  270. ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
  271. //
  272. // Call the file system to check for fast I/O. If the answer is
  273. // anything other than GoForIt then we cannot take the fast I/O
  274. // path.
  275. //
  276. if (!FastIoDispatch->FastIoCheckIfPossible( FileObject,
  277. FileOffset,
  278. Length,
  279. TRUE,
  280. LockKey,
  281. TRUE, // read operation
  282. IoStatus,
  283. DeviceObject )) {
  284. //
  285. // Fast I/O is not possible so release the Fcb and return.
  286. //
  287. Status = FALSE;
  288. goto Done;
  289. }
  290. }
  291. //
  292. // Check for read past file size.
  293. //
  294. IoStatus->Information = Length;
  295. if ( LocalOffset > Header->FileSize.QuadPart ) {
  296. if (FileOffset->QuadPart >= Header->FileSize.QuadPart) {
  297. IoStatus->Status = STATUS_END_OF_FILE;
  298. IoStatus->Information = 0;
  299. goto Done;
  300. }
  301. IoStatus->Information =
  302. Length = (ULONG)( Header->FileSize.QuadPart - FileOffset->QuadPart );
  303. }
  304. //
  305. // We can do fast i/o so call the cc routine to do the work and then
  306. // release the fcb when we've done. If for whatever reason the
  307. // copy read fails, then return FALSE to our caller.
  308. //
  309. // Also mark this as the top level "Irp" so that lower file system
  310. // levels will not attempt a pop-up
  311. //
  312. IoSetTopLevelIrp( (PIRP) FSRTL_FAST_IO_TOP_LEVEL_IRP );
  313. if (NT_SUCCESS(IoStatus->Status)) {
  314. //
  315. // Don't do the sychronize flush if we currently own Eof. The recursive
  316. // flush may try to reacquire.
  317. //
  318. if (DoingIoAtEof &&
  319. (((PSCB)Header)->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
  320. IoStatus->Status = STATUS_FILE_LOCK_CONFLICT;
  321. } else {
  322. IoStatus->Status = NtfsCompressedCopyRead( FileObject,
  323. FileOffset,
  324. Length,
  325. Buffer,
  326. MdlChain,
  327. CompressedDataInfo,
  328. CompressedDataInfoLength,
  329. DeviceObject,
  330. Header,
  331. CompressionUnitSize,
  332. ChunkSize );
  333. }
  334. }
  335. Status = (BOOLEAN)NT_SUCCESS(IoStatus->Status);
  336. IoSetTopLevelIrp( NULL );
  337. Done: NOTHING;
  338. if (DoingIoAtEof) {
  339. ExAcquireFastMutex( Header->FastMutex );
  340. NtfsFinishIoAtEof( Header );
  341. ExReleaseFastMutex( Header->FastMutex );
  342. }
  343. //
  344. // For the Mdl case, we must keep the resource unless
  345. // we are past the end of the file or had nothing to write.
  346. //
  347. if ((MdlChain == NULL) || !Status || (*MdlChain == NULL)) {
  348. ExReleaseResourceLite( Header->PagingIoResource );
  349. }
  350. FsRtlExitFileSystem();
  351. }
  352. #ifdef NTFS_RWCMP_TRACE
  353. if (NtfsCompressionTrace && IsSyscache(Header)) {
  354. DbgPrint("Return Status = %08lx\n", Status);
  355. }
  356. #endif
  357. return Status;
  358. }
  359. NTSTATUS
  360. NtfsCompressedCopyRead (
  361. IN PFILE_OBJECT FileObject,
  362. IN PLARGE_INTEGER FileOffset,
  363. IN ULONG Length,
  364. OUT PVOID Buffer,
  365. OUT PMDL *MdlChain,
  366. OUT PCOMPRESSED_DATA_INFO CompressedDataInfo,
  367. IN ULONG CompressedDataInfoLength,
  368. IN PDEVICE_OBJECT DeviceObject,
  369. IN PNTFS_ADVANCED_FCB_HEADER Header,
  370. IN ULONG CompressionUnitSize,
  371. IN ULONG ChunkSize
  372. )
  373. /*++
  374. Routine Description:
  375. This is a common routine for doing compressed copy or Mdl reads in
  376. the compressed stream. It is called both by the FastIo entry for
  377. this function, as well as by read.c if a compressed read Irp is received.
  378. The caller must be correctly synchronized for the stream.
  379. Arguments:
  380. FileObject - Pointer to the file object being read.
  381. FileOffset - Byte offset in file for desired data.
  382. Length - Length of desired data in bytes.
  383. Buffer - Pointer to output buffer to which data should be copied.
  384. MdlChain - Pointer to an MdlChain pointer to receive an Mdl to describe
  385. the data in the cache.
  386. CompressedDataInfo - Returns compressed data info with compressed chunk
  387. sizes
  388. CompressedDataInfoLength - Supplies the size of the info buffer in bytes.
  389. DeviceObject - Standard Fast I/O Device object input.
  390. Header - Pointer to FsRtl header for file (also is our Scb)
  391. CompressionUnitSize - Size of Compression Unit in bytes.
  392. ChunkSize - ChunkSize in bytes.
  393. Return Value:
  394. NTSTATUS for operation. If STATUS_NOT_MAPPED_USER_DATA, then the caller
  395. should map the normal uncompressed data stream and call back.
  396. --*/
  397. {
  398. PFILE_OBJECT LocalFileObject;
  399. PULONG NextReturnChunkSize;
  400. PUCHAR CompressedBuffer, EndOfCompressedBuffer, ChunkBuffer, StartOfCompressionUnit;
  401. LONGLONG LocalOffset;
  402. ULONG CuCompressedSize;
  403. PVOID MdlBuffer;
  404. ULONG MdlLength;
  405. ULONG PinFlags;
  406. BOOLEAN IsCompressed;
  407. BOOLEAN LastCompressionUnit;
  408. NTSTATUS Status = STATUS_SUCCESS;
  409. PCOMPRESSION_SYNC CompressionSync = NULL;
  410. PBCB Bcb = NULL;
  411. PBCB UncompressedBcb = NULL;
  412. ULONG ClusterSize = ((PSCB)Header)->Vcb->BytesPerCluster;
  413. #ifdef NTFS_RWC_DEBUG
  414. PRWC_HISTORY_ENTRY ReadHistoryBuffer;
  415. #endif
  416. UNREFERENCED_PARAMETER( CompressedDataInfoLength );
  417. UNREFERENCED_PARAMETER( DeviceObject );
  418. ASSERT(CompressedDataInfoLength >= (sizeof(COMPRESSED_DATA_INFO) +
  419. (((Length >> CompressedDataInfo->ChunkShift) - 1) *
  420. sizeof(ULONG))));
  421. ASSERT((FileOffset->QuadPart & (ChunkSize - 1)) == 0);
  422. ASSERT((((FileOffset->QuadPart + Length) & (ChunkSize - 1)) == 0) ||
  423. ((FileOffset->QuadPart + Length) == Header->FileSize.QuadPart));
  424. ASSERT((MdlChain == NULL) || (*MdlChain == NULL));
  425. //
  426. // if we start after vdl we will never pin the compressed buffer
  427. //
  428. ASSERT( FileOffset->QuadPart < Header->ValidDataLength.QuadPart );
  429. //
  430. // Return an error if the file is not compressed.
  431. //
  432. if (((PSCB)Header)->CompressionUnit == 0) {
  433. return STATUS_UNSUPPORTED_COMPRESSION;
  434. }
  435. #ifdef NTFS_RWCMP_TRACE
  436. if (NtfsCompressionTrace && IsSyscache(Header)) {
  437. DbgPrint(" CompressedCopyRead: FO = %08lx, Len = %08lx\n", FileOffset->LowPart, Length );
  438. }
  439. #endif
  440. #ifdef NTFS_RWC_DEBUG
  441. if ((FileOffset->QuadPart < NtfsRWCHighThreshold) &&
  442. (FileOffset->QuadPart + Length > NtfsRWCLowThreshold)) {
  443. PRWC_HISTORY_ENTRY NextBuffer;
  444. ReadHistoryBuffer =
  445. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  446. NextBuffer->Operation = StartOfRead;
  447. NextBuffer->Information = Header->ValidDataLength.LowPart;
  448. NextBuffer->FileOffset = (ULONG) FileOffset->QuadPart;
  449. NextBuffer->Length = (ULONG) Length;
  450. }
  451. #endif
  452. try {
  453. //
  454. // Get ready to loop through all of the compression units.
  455. //
  456. LocalOffset = FileOffset->QuadPart & ~(LONGLONG)(CompressionUnitSize - 1);
  457. Length = (Length + (ULONG)(FileOffset->QuadPart - LocalOffset) + ChunkSize - 1) & ~(ChunkSize - 1);
  458. NextReturnChunkSize = &CompressedDataInfo->CompressedChunkSizes[0];
  459. //
  460. // Loop through desired compression units
  461. //
  462. while (TRUE) {
  463. //
  464. // Free any Bcb from previous loop.
  465. //
  466. if (Bcb != NULL) {
  467. ASSERT( (UncompressedBcb == NULL) ||
  468. (UncompressedBcb == Bcb ) );
  469. CcUnpinData( Bcb );
  470. UncompressedBcb = Bcb = NULL;
  471. } else if (UncompressedBcb != NULL) {
  472. CcUnpinData( UncompressedBcb );
  473. UncompressedBcb = NULL;
  474. }
  475. //
  476. // If there is an uncompressed stream, then we have to synchronize with that.
  477. //
  478. if (((PSCB)Header)->NonpagedScb->SegmentObject.DataSectionObject != NULL) {
  479. Status = NtfsSynchronizeCompressedIo( (PSCB)Header,
  480. &LocalOffset,
  481. Length,
  482. FALSE,
  483. &CompressionSync );
  484. if (!NT_SUCCESS(Status)) {
  485. ASSERT( Status == STATUS_USER_MAPPED_FILE );
  486. leave;
  487. }
  488. }
  489. //
  490. // Loop to get the correct data pinned.
  491. //
  492. // The synchronize call above has guaranteed that no data can written through
  493. // the uncompressed section (barring the loop back below), and it has also flushed
  494. // any dirty data that may have already been in the uncompressed section. Here we
  495. // are basically trying to figure out how much data we should pin and then get it
  496. // pinned.
  497. //
  498. // We use the following steps:
  499. //
  500. // 1. Query the current compressed size (derived from the allocation state).
  501. // If the size is neither 0-allocated nor fully allocated, then we will
  502. // simply pin the data in the compressed section - this is the normal case.
  503. // 2. However, the first time we see one of these special sizes, we do not
  504. // know if could be the case that there is dirty data sitting in the compressed
  505. // cache. Therefore, we set up to pin just one page with PIN_IF_BCB. This
  506. // will only pin something if there is aleady a Bcb for it.
  507. // 3. Now we determine if we think the data is compressed or not, counting the
  508. // special cases from the previous point as compressed. This determines
  509. // which section to read from.
  510. // 4. Now, if we think there is/may be data to pin, we call Cc. If he comes
  511. // back with no data (only possible if we set PIN_IF_BCB), then we know we
  512. // can loop back to the top and trust the allocation state on disk now.
  513. // (That is because we flushed the uncompressed stream and found no Bcb in
  514. // the compressed stream.) On the second time through we should correctly
  515. // handle the 0-allocated or fully-allocated cases. (The careful reader
  516. // will note that if there is no uncompressed section, then indeed writers
  517. // to the compressed section could be going on in parallel with this read,
  518. // and we could handle the 0- or fully-allocated case while there is
  519. // new compressed data in the cache. However on the second loop we know
  520. // there really was all 0's in the file at one point which it is correct
  521. // to return, and it is always correct to go to the uncompressed cache
  522. // if we still see fully-allocated. More importantly, we have an
  523. // unsynchronized reader and writer, and the reader's result is therefore
  524. // nondeterministic anyway.
  525. //
  526. PinFlags = PIN_WAIT;
  527. do {
  528. //
  529. // If we are beyond ValidDataLength, then the CompressedSize is 0!
  530. //
  531. if (LocalOffset >= Header->ValidDataLength.QuadPart) {
  532. CuCompressedSize = 0;
  533. ClearFlag( PinFlags, PIN_IF_BCB );
  534. //
  535. // Otherwise query the compressed size.
  536. //
  537. } else {
  538. NtfsFastIoQueryCompressedSize( FileObject,
  539. (PLARGE_INTEGER)&LocalOffset,
  540. &CuCompressedSize );
  541. //
  542. // If it looks uncompressed, we are probably trying to read data
  543. // that has not been written out yet. Also if the space is not yet
  544. // allocated, then we also need to try to hit the data in the compressed
  545. // cache.
  546. //
  547. if (((CuCompressedSize == CompressionUnitSize) || (CuCompressedSize == 0)) &&
  548. !FlagOn(PinFlags, PIN_IF_BCB)) {
  549. CuCompressedSize = 0x1000;
  550. SetFlag( PinFlags, PIN_IF_BCB );
  551. //
  552. // Make sure we really read the data if this is the second time through.
  553. //
  554. } else {
  555. //
  556. // If the range is dirty and there is no Bcb in the compressed stream
  557. // then always go to the uncompressed stream.
  558. //
  559. if (FlagOn( PinFlags, PIN_IF_BCB ) &&
  560. (CuCompressedSize != CompressionUnitSize)) {
  561. LONGLONG ClusterCount = 1 << ((PSCB) Header)->CompressionUnitShift;
  562. if (NtfsCheckForReservedClusters( (PSCB) Header,
  563. LlClustersFromBytesTruncate( ((PSCB) Header)->Vcb, LocalOffset ),
  564. &ClusterCount )) {
  565. CuCompressedSize = CompressionUnitSize;
  566. }
  567. }
  568. ClearFlag( PinFlags, PIN_IF_BCB );
  569. }
  570. }
  571. ASSERT( CuCompressedSize <= CompressionUnitSize );
  572. IsCompressed = (BOOLEAN)((CuCompressedSize != CompressionUnitSize) &&
  573. (CompressedDataInfo->CompressionFormatAndEngine != 0));
  574. //
  575. // Figure out which FileObject to use.
  576. //
  577. LocalFileObject = Header->FileObjectC;
  578. if (!IsCompressed) {
  579. LocalFileObject = ((PSCB)Header)->FileObject;
  580. if (LocalFileObject == NULL) {
  581. Status = STATUS_NOT_MAPPED_DATA;
  582. goto Done;
  583. }
  584. }
  585. //
  586. // If the compression unit is not (yet) allocated, then there is
  587. // no need to synchronize - we will return 0-lengths for chunk sizes.
  588. //
  589. if (CuCompressedSize != 0) {
  590. //
  591. // Map the compression unit in the compressed or uncompressed
  592. // stream.
  593. //
  594. CcPinRead( LocalFileObject,
  595. (PLARGE_INTEGER)&LocalOffset,
  596. CuCompressedSize,
  597. PinFlags,
  598. &Bcb,
  599. &CompressedBuffer );
  600. //
  601. // If there is no Bcb it means we were assuming the data was in
  602. // the compressed buffer and only wanted to wait if it was
  603. // present. Well it isn't there so force ourselved to go
  604. // back and look in the uncompressed section.
  605. //
  606. if (Bcb == NULL) {
  607. ASSERT( FlagOn( PinFlags, PIN_IF_BCB ));
  608. continue;
  609. }
  610. //
  611. // Now that the data is pinned (we are synchronized with the
  612. // CompressionUnit), we have to get the size again since it could
  613. // have changed.
  614. //
  615. if (IsCompressed) {
  616. //
  617. // Now, we know the data where we are about to read is compressed,
  618. // but we cannot really tell for sure how big it is since there may
  619. // be dirty data in the cache.
  620. //
  621. // We will say the size is (CompressionUnitSize - ClusterSize)
  622. // which is the largest possible compressed size, and we will normally
  623. // just hit on the existing dirty Bcb and/or resident pages anyway.
  624. // (If we do not, then we will just fault those pages in one at a
  625. // time anyway. This looks bad having to do this twice, but it
  626. // is only until the dirty data finally gets flushed out.) This also
  627. // means we may walk off the range we pinned in a read-only mode, but
  628. // that should be benign.
  629. //
  630. // Of course in the main line case, we figured out exactly how much
  631. // data to read in and we did so when we pinned it above.
  632. //
  633. CuCompressedSize = CompressionUnitSize - ClusterSize;
  634. //
  635. // Otherwise remember to release this Bcb.
  636. //
  637. } else {
  638. UncompressedBcb = Bcb;
  639. }
  640. }
  641. } while ((Bcb == NULL) && (CuCompressedSize != 0));
  642. //
  643. // Now that we are synchronized with the buffer, see if someone snuck
  644. // in behind us and created the noncached stream since we last checked
  645. // for that stream. If so we have to loop back to synchronize with the
  646. // compressed stream again.
  647. //
  648. if ((CompressionSync == NULL) &&
  649. (((PSCB)Header)->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
  650. continue;
  651. }
  652. EndOfCompressedBuffer = Add2Ptr( CompressedBuffer, CuCompressedSize );
  653. StartOfCompressionUnit = CompressedBuffer;
  654. //
  655. // Remember if we may go past the end of the file.
  656. //
  657. LastCompressionUnit = FALSE;
  658. if (LocalOffset + CuCompressedSize > Header->FileSize.QuadPart) {
  659. LastCompressionUnit = TRUE;
  660. }
  661. //
  662. // Now loop through desired chunks
  663. //
  664. MdlLength = 0;
  665. do {
  666. //
  667. // Assume current chunk does not compress, else get current
  668. // chunk size.
  669. //
  670. if (IsCompressed) {
  671. if (CuCompressedSize != 0) {
  672. PUCHAR PrevCompressedBuffer;
  673. //
  674. // We have to do a careful check to see if the return value is
  675. // greater than or equal to the chunk size AND the data is in fact
  676. // compressed. We don't have anyway to pass this data back to
  677. // the server so he can interpret it correctly.
  678. //
  679. PrevCompressedBuffer = CompressedBuffer;
  680. Status = RtlDescribeChunk( CompressedDataInfo->CompressionFormatAndEngine,
  681. &CompressedBuffer,
  682. EndOfCompressedBuffer,
  683. &ChunkBuffer,
  684. NextReturnChunkSize );
  685. if (!NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) {
  686. ExRaiseStatus(Status);
  687. }
  688. //
  689. // If the size is greater or equal to the chunk size AND the data is compressed
  690. // then force this to the uncompressed path. Note that the Rtl package has
  691. // been changed so that this case shouldn't happen on new disks but it is
  692. // possible that it could exist on exiting disks.
  693. //
  694. if ((*NextReturnChunkSize >= ChunkSize) &&
  695. (PrevCompressedBuffer == ChunkBuffer)) {
  696. //
  697. // Raise an error code that causes the server to reissue in
  698. // the uncompressed path.
  699. //
  700. ExRaiseStatus( STATUS_UNSUPPORTED_COMPRESSION );
  701. }
  702. //
  703. // Another unusual case is where the compressed data extends past the containing
  704. // file size. We don't have anyway to prevent the next page from being zeroed.
  705. // Ask the server to go the uncompressed path.
  706. //
  707. if (LastCompressionUnit) {
  708. LONGLONG EndOfPage;
  709. EndOfPage = LocalOffset + PtrOffset( StartOfCompressionUnit, CompressedBuffer ) + PAGE_SIZE - 1;
  710. ((PLARGE_INTEGER) &EndOfPage)->LowPart &= ~(PAGE_SIZE - 1);
  711. if (EndOfPage > Header->FileSize.QuadPart) {
  712. //
  713. // Raise an error code that causes the server to reissue in
  714. // the uncompressed path.
  715. //
  716. ExRaiseStatus( STATUS_UNSUPPORTED_COMPRESSION );
  717. }
  718. }
  719. ASSERT( *NextReturnChunkSize <= ChunkSize );
  720. //
  721. // If the entire CompressionUnit is empty, do this.
  722. //
  723. } else {
  724. *NextReturnChunkSize = 0;
  725. #ifdef NTFS_RWC_DEBUG
  726. if ((LocalOffset < NtfsRWCHighThreshold) &&
  727. (LocalOffset + CompressionUnitSize > NtfsRWCLowThreshold)) {
  728. PRWC_HISTORY_ENTRY NextBuffer;
  729. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  730. NextBuffer->Operation = ReadZeroes;
  731. NextBuffer->Information = 0;
  732. NextBuffer->FileOffset = (ULONG) LocalOffset;
  733. NextBuffer->Length = 0;
  734. }
  735. #endif
  736. }
  737. //
  738. // If the file is not compressed, we have to fill in
  739. // the appropriate chunk size and buffer, and advance
  740. // CompressedBuffer.
  741. //
  742. } else {
  743. #ifdef NTFS_RWC_DEBUG
  744. if ((LocalOffset < NtfsRWCHighThreshold) &&
  745. (LocalOffset + ChunkSize > NtfsRWCLowThreshold)) {
  746. PRWC_HISTORY_ENTRY NextBuffer;
  747. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  748. NextBuffer->Operation = ReadUncompressed;
  749. NextBuffer->Information = (LocalFileObject == ((PSCB)Header)->FileObject);
  750. NextBuffer->FileOffset = (ULONG) LocalOffset;
  751. NextBuffer->Length = 0;
  752. }
  753. #endif
  754. *NextReturnChunkSize = ChunkSize;
  755. ChunkBuffer = CompressedBuffer;
  756. CompressedBuffer = Add2Ptr( CompressedBuffer, ChunkSize );
  757. }
  758. Status = STATUS_SUCCESS;
  759. //
  760. // We may not have reached the first chunk yet.
  761. //
  762. if (LocalOffset >= FileOffset->QuadPart) {
  763. if (MdlChain != NULL) {
  764. //
  765. // If we have not started remembering an Mdl buffer,
  766. // then do so now.
  767. //
  768. if (MdlLength == 0) {
  769. MdlBuffer = ChunkBuffer;
  770. //
  771. // Otherwise we just have to increase the length
  772. // and check for an uncompressed chunk, because that
  773. // forces us to emit the previous Mdl since we do
  774. // not transmit the chunk header in this case.
  775. //
  776. } else {
  777. //
  778. // In the rare case that we hit an individual chunk
  779. // that did not compress or is all zeros, we have to
  780. // emit what we had (which captures the Bcb pointer),
  781. // and start a new Mdl buffer.
  782. //
  783. if ((*NextReturnChunkSize == ChunkSize) || (*NextReturnChunkSize == 0)) {
  784. NtfsAddToCompressedMdlChain( MdlChain,
  785. MdlBuffer,
  786. MdlLength,
  787. Header->PagingIoResource,
  788. Bcb,
  789. IoReadAccess,
  790. IsCompressed );
  791. Bcb = NULL;
  792. MdlBuffer = ChunkBuffer;
  793. MdlLength = 0;
  794. }
  795. }
  796. MdlLength += *NextReturnChunkSize;
  797. //
  798. // Else copy next chunk (compressed or not).
  799. //
  800. } else {
  801. //
  802. // Copy next chunk (compressed or not).
  803. //
  804. RtlCopyBytes( Buffer,
  805. ChunkBuffer,
  806. (IsCompressed || (Length >= *NextReturnChunkSize)) ?
  807. *NextReturnChunkSize : Length );
  808. //
  809. // Advance output buffer by bytes copied.
  810. //
  811. Buffer = (PCHAR)Buffer + *NextReturnChunkSize;
  812. }
  813. NextReturnChunkSize += 1;
  814. CompressedDataInfo->NumberOfChunks += 1;
  815. }
  816. //
  817. // Reduce length by chunk copied, and check if we are done.
  818. //
  819. if (Length > ChunkSize) {
  820. Length -= ChunkSize;
  821. } else {
  822. goto Done;
  823. }
  824. LocalOffset += ChunkSize;
  825. } while ((LocalOffset & (CompressionUnitSize - 1)) != 0);
  826. //
  827. // If this is an Mdl call, then it is time to add to the MdlChain
  828. // before moving to the next compression unit.
  829. //
  830. if (MdlLength != 0) {
  831. NtfsAddToCompressedMdlChain( MdlChain,
  832. MdlBuffer,
  833. MdlLength,
  834. Header->PagingIoResource,
  835. Bcb,
  836. IoReadAccess,
  837. IsCompressed );
  838. Bcb = NULL;
  839. MdlLength = 0;
  840. }
  841. }
  842. Done:
  843. FileObject->Flags |= FO_FILE_FAST_IO_READ;
  844. if (NT_SUCCESS(Status) && (MdlLength != 0)) {
  845. NtfsAddToCompressedMdlChain( MdlChain,
  846. MdlBuffer,
  847. MdlLength,
  848. Header->PagingIoResource,
  849. Bcb,
  850. IoReadAccess,
  851. IsCompressed );
  852. Bcb = NULL;
  853. }
  854. } except( FsRtlIsNtstatusExpected(Status = GetExceptionCode())
  855. ? EXCEPTION_EXECUTE_HANDLER
  856. : EXCEPTION_CONTINUE_SEARCH ) {
  857. NOTHING;
  858. }
  859. //
  860. // Unpin any Bcbs we still have.
  861. //
  862. if (Bcb != NULL) {
  863. CcUnpinData( Bcb );
  864. } else if (UncompressedBcb != NULL) {
  865. CcUnpinData( UncompressedBcb );
  866. }
  867. if (CompressionSync != NULL) {
  868. NtfsReleaseCompressionSync( CompressionSync );
  869. }
  870. //
  871. // Perform Mdl-specific processing.
  872. //
  873. if (MdlChain != NULL) {
  874. //
  875. // On error, cleanup any MdlChain we built up
  876. //
  877. if (!NT_SUCCESS(Status)) {
  878. NtfsCleanupCompressedMdlChain( *MdlChain, TRUE );
  879. *MdlChain = NULL;
  880. //
  881. // Change owner Id for the Scb and Bcbs we are holding.
  882. //
  883. } else if (*MdlChain != NULL) {
  884. NtfsSetMdlBcbOwners( *MdlChain );
  885. ExSetResourceOwnerPointer( Header->PagingIoResource, (PVOID)((PCHAR)*MdlChain + 3) );
  886. }
  887. }
  888. #ifdef NTFS_RWCMP_TRACE
  889. if (NtfsCompressionTrace && IsSyscache(Header)) {
  890. ULONG ci;
  891. if (NT_SUCCESS(Status)) {
  892. DbgPrint(" Chunks:");
  893. for (ci = 0; ci < CompressedDataInfo->NumberOfChunks; ci++) {
  894. DbgPrint(" %lx", CompressedDataInfo->CompressedChunkSizes[ci]);
  895. }
  896. DbgPrint("\n");
  897. }
  898. DbgPrint(" Return Status = %08lx\n", Status);
  899. }
  900. #endif
  901. #ifdef NTFS_RWC_DEBUG
  902. if ((Status == STATUS_SUCCESS) &&
  903. (FileOffset->QuadPart < NtfsRWCHighThreshold) &&
  904. (FileOffset->QuadPart + Length > NtfsRWCLowThreshold)) {
  905. PRWC_HISTORY_ENTRY NextBuffer;
  906. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  907. NextBuffer->Operation = EndOfRead;
  908. NextBuffer->Information = (ULONG) ReadHistoryBuffer;
  909. NextBuffer->FileOffset = 0;
  910. NextBuffer->Length = 0;
  911. if (ReadHistoryBuffer != NULL) {
  912. SetFlag( ReadHistoryBuffer->Operation, 0x80000000 );
  913. }
  914. }
  915. #endif
  916. return Status;
  917. }
  918. BOOLEAN
  919. NtfsMdlReadCompleteCompressed (
  920. IN struct _FILE_OBJECT *FileObject,
  921. IN PMDL MdlChain,
  922. IN struct _DEVICE_OBJECT *DeviceObject
  923. )
  924. /*++
  925. Routine Description:
  926. This routine frees resources and the Mdl Chain after a compressed read.
  927. Arguments:
  928. FileObject - pointer to the file object for the request.
  929. MdlChain - as returned from compressed copy read.
  930. DeviceObject - As required for a fast I/O routine.
  931. Return Value:
  932. TRUE - if fast path succeeded
  933. FALSE - if an Irp is required
  934. --*/
  935. {
  936. PERESOURCE ResourceToRelease;
  937. if (MdlChain != NULL) {
  938. ResourceToRelease = *(PERESOURCE *)Add2Ptr( MdlChain, MdlChain->Size + sizeof( PBCB ));
  939. }
  940. NtfsCleanupCompressedMdlChain( MdlChain, FALSE );
  941. //
  942. // If the server tried to read past the end of the file in the
  943. // fast path then he calls us with NULL for the MDL. We already
  944. // released the thread in that case.
  945. //
  946. if (MdlChain != NULL) {
  947. ExReleaseResourceForThread( ResourceToRelease, (ERESOURCE_THREAD)((PCHAR)MdlChain + 3) );
  948. }
  949. return TRUE;
  950. UNREFERENCED_PARAMETER( DeviceObject );
  951. UNREFERENCED_PARAMETER( FileObject );
  952. }
  953. BOOLEAN
  954. NtfsCopyWriteC (
  955. IN PFILE_OBJECT FileObject,
  956. IN PLARGE_INTEGER FileOffset,
  957. IN ULONG Length,
  958. IN ULONG LockKey,
  959. IN PVOID Buffer,
  960. OUT PMDL *MdlChain,
  961. OUT PIO_STATUS_BLOCK IoStatus,
  962. IN PCOMPRESSED_DATA_INFO CompressedDataInfo,
  963. IN ULONG CompressedDataInfoLength,
  964. IN PDEVICE_OBJECT DeviceObject
  965. )
  966. /*++
  967. Routine Description:
  968. This routine does a fast cached write bypassing the usual file system
  969. entry routine (i.e., without the Irp). It is used to do a copy write
  970. of a cached file object.
  971. Arguments:
  972. FileObject - Pointer to the file object being write.
  973. FileOffset - Byte offset in file for desired data.
  974. Length - Length of desired data in bytes.
  975. Buffer - Pointer to output buffer to which data should be copied.
  976. MdlChain - Pointer to an MdlChain pointer to receive an Mdl to describe
  977. where the data may be written in the cache.
  978. IoStatus - Pointer to standard I/O status block to receive the status
  979. for the transfer.
  980. CompressedDataInfo - Returns compressed data info with compressed chunk
  981. sizes
  982. CompressedDataInfoLength - Supplies the size of the info buffer in bytes.
  983. Return Value:
  984. FALSE - if there is an error.
  985. TRUE - if the data is being delivered
  986. --*/
  987. {
  988. PNTFS_ADVANCED_FCB_HEADER Header;
  989. FILE_COMPRESSION_INFORMATION CompressionInformation;
  990. ULONG CompressionUnitSize, ChunkSize;
  991. ULONG EngineMatches;
  992. LARGE_INTEGER NewFileSize;
  993. LARGE_INTEGER OldFileSize;
  994. LONGLONG LocalOffset;
  995. PFAST_IO_DISPATCH FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
  996. ULONG DoingIoAtEof = FALSE;
  997. BOOLEAN Status = TRUE;
  998. UNREFERENCED_PARAMETER( CompressedDataInfoLength );
  999. PAGED_CODE();
  1000. //
  1001. // You cannot have both a buffer to copy into and an MdlChain.
  1002. //
  1003. ASSERT((Buffer == NULL) || (MdlChain == NULL));
  1004. //
  1005. // Get out immediately if COW is not supported.
  1006. //
  1007. if (!NtfsEnableCompressedIO) { return FALSE; }
  1008. //
  1009. // Get a real pointer to the common fcb header
  1010. //
  1011. Header = (PNTFS_ADVANCED_FCB_HEADER)FileObject->FsContext;
  1012. #ifdef NTFS_RWCMP_TRACE
  1013. if (NtfsCompressionTrace && IsSyscache(Header)) {
  1014. DbgPrint("NtfsCopyWriteC: FO = %08lx, Len = %08lx\n", FileOffset->LowPart, Length );
  1015. }
  1016. #endif
  1017. //
  1018. // See if it is ok to handle this in the fast path.
  1019. //
  1020. if (CcCanIWrite( FileObject, Length, TRUE, FALSE ) &&
  1021. !FlagOn(FileObject->Flags, FO_WRITE_THROUGH) &&
  1022. CcCopyWriteWontFlush(FileObject, FileOffset, Length)) {
  1023. //
  1024. // Assume our transfer will work
  1025. //
  1026. IoStatus->Status = STATUS_SUCCESS;
  1027. IoStatus->Information = Length;
  1028. //
  1029. // Special case the zero byte length
  1030. //
  1031. if (Length != 0) {
  1032. //
  1033. // Enter the file system
  1034. //
  1035. FsRtlEnterFileSystem();
  1036. //
  1037. // Calculate the compression unit and chunk sizes.
  1038. //
  1039. CompressionUnitSize = 1 << CompressedDataInfo->CompressionUnitShift;
  1040. ChunkSize = 1 << CompressedDataInfo->ChunkShift;
  1041. //
  1042. // If there is a normal cache section, flush that first, flushing integral
  1043. // compression units so we don't write them twice.
  1044. //
  1045. //
  1046. if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  1047. ULONG FlushLength;
  1048. ExAcquireResourceExclusiveLite( Header->PagingIoResource, TRUE );
  1049. CompressionUnitSize = ((PSCB) Header)->CompressionUnit;
  1050. LocalOffset = FileOffset->QuadPart & ~(LONGLONG)(CompressionUnitSize - 1);
  1051. FlushLength = (Length + (ULONG)(FileOffset->QuadPart - LocalOffset) + CompressionUnitSize - 1) &
  1052. ~(CompressionUnitSize - 1);
  1053. CcFlushCache( FileObject->SectionObjectPointer,
  1054. (PLARGE_INTEGER)&LocalOffset,
  1055. FlushLength,
  1056. NULL );
  1057. CcPurgeCacheSection( FileObject->SectionObjectPointer,
  1058. (PLARGE_INTEGER)&LocalOffset,
  1059. FlushLength,
  1060. FALSE );
  1061. ExReleaseResourceLite( Header->PagingIoResource );
  1062. }
  1063. NewFileSize.QuadPart = FileOffset->QuadPart + Length;
  1064. //
  1065. // Prevent truncates by acquiring paging I/O
  1066. //
  1067. ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
  1068. //
  1069. // Get the compression information for this file and return those fields.
  1070. //
  1071. NtfsFastIoQueryCompressionInfo( FileObject, &CompressionInformation, IoStatus );
  1072. CompressionUnitSize = ((PSCB) Header)->CompressionUnit;
  1073. //
  1074. // See if the engine matches, so we can pass that on to the
  1075. // compressed write routine.
  1076. //
  1077. EngineMatches =
  1078. ((CompressedDataInfo->CompressionFormatAndEngine == CompressionInformation.CompressionFormat) &&
  1079. (CompressedDataInfo->ChunkShift == CompressionInformation.ChunkShift));
  1080. //
  1081. // If we either got an error in the call above, or the file size is less than
  1082. // one chunk, then return an error. (Could be an Ntfs resident attribute.)
  1083. //
  1084. if (!NT_SUCCESS(IoStatus->Status) || (Header->FileSize.QuadPart < ChunkSize)) {
  1085. goto ErrOut;
  1086. }
  1087. //
  1088. // Now synchronize with the FsRtl Header
  1089. //
  1090. ExAcquireFastMutex( Header->FastMutex );
  1091. //
  1092. // Now see if we will change FileSize. We have to do it now
  1093. // so that our reads are not nooped. Note we do not allow
  1094. // FileOffset to be WRITE_TO_EOF.
  1095. //
  1096. ASSERT((FileOffset->LowPart & (ChunkSize - 1)) == 0);
  1097. if (NewFileSize.QuadPart > Header->ValidDataLength.QuadPart) {
  1098. //
  1099. // We can change FileSize and ValidDataLength if either, no one
  1100. // else is now, or we are still extending after waiting.
  1101. //
  1102. DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
  1103. NtfsWaitForIoAtEof( Header, FileOffset, Length );
  1104. //
  1105. // Set the Flag if we are changing FileSize or ValidDataLength,
  1106. // and save current values.
  1107. //
  1108. if (DoingIoAtEof) {
  1109. SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
  1110. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  1111. ((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
  1112. #endif
  1113. //
  1114. // Now calculate the new FileSize and see if we wrapped the
  1115. // 32-bit boundary.
  1116. //
  1117. NewFileSize.QuadPart = FileOffset->QuadPart + Length;
  1118. //
  1119. // Update Filesize now so that we do not truncate reads.
  1120. //
  1121. OldFileSize.QuadPart = Header->FileSize.QuadPart;
  1122. if (NewFileSize.QuadPart > Header->FileSize.QuadPart) {
  1123. //
  1124. // If we are beyond AllocationSize, go to ErrOut
  1125. //
  1126. if (NewFileSize.QuadPart > Header->AllocationSize.QuadPart) {
  1127. ExReleaseFastMutex( Header->FastMutex );
  1128. goto ErrOut;
  1129. } else {
  1130. Header->FileSize.QuadPart = NewFileSize.QuadPart;
  1131. }
  1132. }
  1133. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  1134. } else {
  1135. ASSERT( ((PSCB) Header)->IoAtEofThread != (PERESOURCE_THREAD) ExGetCurrentResourceThread() );
  1136. #endif
  1137. }
  1138. }
  1139. ExReleaseFastMutex( Header->FastMutex );
  1140. //
  1141. // Now that the File is acquired shared, we can safely test if it
  1142. // is really cached and if we can do fast i/o and if not, then
  1143. // release the fcb and return.
  1144. //
  1145. // Note, we do not want to call CcZeroData here,
  1146. // but rather defer zeroing to the file system, due to
  1147. // the need for exclusive resource acquisition. Therefore
  1148. // we get out if we are beyond ValidDataLength.
  1149. //
  1150. if ((Header->FileObjectC == NULL) ||
  1151. (Header->FileObjectC->PrivateCacheMap == NULL) ||
  1152. (Header->IsFastIoPossible == FastIoIsNotPossible) ||
  1153. (FileOffset->QuadPart > Header->ValidDataLength.QuadPart)) {
  1154. goto ErrOut;
  1155. }
  1156. //
  1157. // Check if fast I/O is questionable and if so then go ask
  1158. // the file system the answer
  1159. //
  1160. if (Header->IsFastIoPossible == FastIoIsQuestionable) {
  1161. FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
  1162. //
  1163. // All file system then set "Is Questionable" had better
  1164. // support fast I/O
  1165. //
  1166. ASSERT(FastIoDispatch != NULL);
  1167. ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
  1168. //
  1169. // Call the file system to check for fast I/O. If the
  1170. // answer is anything other than GoForIt then we cannot
  1171. // take the fast I/O path.
  1172. //
  1173. if (!FastIoDispatch->FastIoCheckIfPossible( FileObject,
  1174. FileOffset,
  1175. Length,
  1176. TRUE,
  1177. LockKey,
  1178. FALSE, // write operation
  1179. IoStatus,
  1180. DeviceObject )) {
  1181. //
  1182. // Fast I/O is not possible so cleanup and return.
  1183. //
  1184. goto ErrOut;
  1185. }
  1186. }
  1187. //
  1188. // Update both caches with EOF.
  1189. //
  1190. if (DoingIoAtEof) {
  1191. NtfsSetBothCacheSizes( FileObject,
  1192. (PCC_FILE_SIZES)&Header->AllocationSize,
  1193. (PSCB)Header );
  1194. }
  1195. //
  1196. // We can do fast i/o so call the cc routine to do the work
  1197. // and then release the fcb when we've done. If for whatever
  1198. // reason the copy write fails, then return FALSE to our
  1199. // caller.
  1200. //
  1201. // Also mark this as the top level "Irp" so that lower file
  1202. // system levels will not attempt a pop-up
  1203. //
  1204. IoSetTopLevelIrp( (PIRP) FSRTL_FAST_IO_TOP_LEVEL_IRP );
  1205. ASSERT(CompressedDataInfoLength >= (sizeof(COMPRESSED_DATA_INFO) +
  1206. (((Length >> CompressedDataInfo->ChunkShift) - 1) *
  1207. sizeof(ULONG))));
  1208. if (NT_SUCCESS(IoStatus->Status)) {
  1209. if (DoingIoAtEof &&
  1210. (((PSCB)Header)->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
  1211. IoStatus->Status = STATUS_FILE_LOCK_CONFLICT;
  1212. } else {
  1213. IoStatus->Status = NtfsCompressedCopyWrite( FileObject,
  1214. FileOffset,
  1215. Length,
  1216. Buffer,
  1217. MdlChain,
  1218. CompressedDataInfo,
  1219. DeviceObject,
  1220. Header,
  1221. CompressionUnitSize,
  1222. ChunkSize,
  1223. EngineMatches );
  1224. }
  1225. }
  1226. IoSetTopLevelIrp( NULL );
  1227. Status = (BOOLEAN)NT_SUCCESS(IoStatus->Status);
  1228. //
  1229. // If we succeeded, see if we have to update FileSize ValidDataLength.
  1230. //
  1231. if (Status) {
  1232. //
  1233. // Set this handle as having modified the file.
  1234. //
  1235. FileObject->Flags |= FO_FILE_MODIFIED;
  1236. if (DoingIoAtEof) {
  1237. CC_FILE_SIZES CcFileSizes;
  1238. ExAcquireFastMutex( Header->FastMutex );
  1239. FileObject->Flags |= FO_FILE_SIZE_CHANGED;
  1240. Header->ValidDataLength = NewFileSize;
  1241. CcFileSizes = *(PCC_FILE_SIZES)&Header->AllocationSize;
  1242. NtfsVerifySizes( Header );
  1243. NtfsFinishIoAtEof( Header );
  1244. //
  1245. // Update the normal cache with ValidDataLength.
  1246. //
  1247. if (((PSCB)Header)->FileObject != NULL) {
  1248. CcSetFileSizes( ((PSCB)Header)->FileObject, &CcFileSizes );
  1249. }
  1250. ExReleaseFastMutex( Header->FastMutex );
  1251. }
  1252. goto Done1;
  1253. }
  1254. ErrOut: NOTHING;
  1255. Status = FALSE;
  1256. if (DoingIoAtEof) {
  1257. ExAcquireFastMutex( Header->FastMutex );
  1258. if (CcIsFileCached(FileObject)) {
  1259. *CcGetFileSizePointer(FileObject) = OldFileSize;
  1260. }
  1261. if (Header->FileObjectC != NULL) {
  1262. *CcGetFileSizePointer(Header->FileObjectC) = OldFileSize;
  1263. }
  1264. Header->FileSize = OldFileSize;
  1265. NtfsFinishIoAtEof( Header );
  1266. ExReleaseFastMutex( Header->FastMutex );
  1267. }
  1268. Done1: NOTHING;
  1269. //
  1270. // For the Mdl case, we must keep the resource.
  1271. //
  1272. if ((MdlChain == NULL) || !Status || (*MdlChain == NULL)) {
  1273. ExReleaseResourceLite( Header->PagingIoResource );
  1274. }
  1275. FsRtlExitFileSystem();
  1276. }
  1277. } else {
  1278. //
  1279. // We could not do the I/O now.
  1280. //
  1281. Status = FALSE;
  1282. }
  1283. #ifdef NTFS_RWCMP_TRACE
  1284. if (NtfsCompressionTrace && IsSyscache(Header)) {
  1285. DbgPrint("Return Status = %08lx\n", Status);
  1286. }
  1287. #endif
  1288. return Status;
  1289. }
  1290. NTSTATUS
  1291. NtfsCompressedCopyWrite (
  1292. IN PFILE_OBJECT FileObject,
  1293. IN PLARGE_INTEGER FileOffset,
  1294. IN ULONG Length,
  1295. IN PVOID Buffer,
  1296. OUT PMDL *MdlChain,
  1297. IN PCOMPRESSED_DATA_INFO CompressedDataInfo,
  1298. IN PDEVICE_OBJECT DeviceObject,
  1299. IN PNTFS_ADVANCED_FCB_HEADER Header,
  1300. IN ULONG CompressionUnitSize,
  1301. IN ULONG ChunkSize,
  1302. IN ULONG EngineMatches
  1303. )
  1304. /*++
  1305. Routine Description:
  1306. This routine does a fast cached write bypassing the usual file system
  1307. entry routine (i.e., without the Irp). It is used to do a copy write
  1308. of a cached file object.
  1309. Arguments:
  1310. FileObject - Pointer to the file object being write.
  1311. FileOffset - Byte offset in file for desired data.
  1312. Length - Length of desired data in bytes.
  1313. Buffer - Pointer to output buffer to which data should be copied.
  1314. MdlChain - Pointer to an MdlChain pointer to receive an Mdl to describe
  1315. where the data may be written in the cache.
  1316. CompressedDataInfo - Returns compressed data info with compressed chunk
  1317. sizes
  1318. DeviceObject - Standard Fast I/O Device object input.
  1319. Header - Pointer to FsRtl header for file (also is our Scb)
  1320. CompressionUnitSize - Size of Compression Unit in bytes.
  1321. ChunkSize - ChunkSize in bytes.
  1322. EngineMatches - TRUE if the caller has determined that the compressed
  1323. data format matches the compression engine for the file.
  1324. Return Value:
  1325. NTSTATUS for operation. If STATUS_NOT_MAPPED_USER_DATA, then the caller
  1326. should map the normal uncompressed data stream and call back.
  1327. --*/
  1328. {
  1329. NTSTATUS Status = STATUS_SUCCESS;
  1330. PUCHAR StartOfPin;
  1331. ULONG SizeToPin;
  1332. LONGLONG LocalOffset;
  1333. PULONG NextChunkSize, TempChunkSize;
  1334. PUCHAR ChunkBuffer;
  1335. PUCHAR CacheBuffer;
  1336. PUCHAR EndOfCacheBuffer;
  1337. ULONG SavedLength;
  1338. PUCHAR SavedBuffer;
  1339. ULONG ChunkOfZeros;
  1340. ULONG UncompressedChunkHeader;
  1341. ULONG ChunkSizes[17];
  1342. ULONG i, ChunksSeen;
  1343. ULONG TempUlong;
  1344. PVOID MdlBuffer;
  1345. ULONG MdlLength = 0;
  1346. ULONG ClusterSize = ((PSCB)Header)->Vcb->BytesPerCluster;
  1347. PBCB Bcb = NULL;
  1348. PBCB TempBcb = NULL;
  1349. PCOMPRESSION_SYNC CompressionSync = NULL;
  1350. BOOLEAN FullOverwrite = FALSE;
  1351. BOOLEAN IsCompressed;
  1352. ASSERT((FileOffset->QuadPart & (ChunkSize - 1)) == 0);
  1353. ASSERT((((FileOffset->QuadPart + Length) & (ChunkSize - 1)) == 0) ||
  1354. ((FileOffset->QuadPart + Length) == Header->FileSize.QuadPart));
  1355. ASSERT((MdlChain == NULL) || (*MdlChain == NULL));
  1356. //
  1357. // Return an error if the file is not compressed.
  1358. //
  1359. if (!EngineMatches || ((PSCB)Header)->CompressionUnit == 0) {
  1360. return STATUS_UNSUPPORTED_COMPRESSION;
  1361. }
  1362. #ifdef NTFS_RWCMP_TRACE
  1363. if (NtfsCompressionTrace && IsSyscache(Header)) {
  1364. ULONG ci;
  1365. DbgPrint(" CompressedWrite: FO = %08lx, Len = %08lx\n", FileOffset->LowPart, Length );
  1366. DbgPrint(" Chunks:");
  1367. for (ci = 0; ci < CompressedDataInfo->NumberOfChunks; ci++) {
  1368. DbgPrint(" %lx", CompressedDataInfo->CompressedChunkSizes[ci]);
  1369. }
  1370. DbgPrint("\n");
  1371. }
  1372. #endif
  1373. #ifdef NTFS_RWC_DEBUG
  1374. if ((FileOffset->QuadPart < NtfsRWCHighThreshold) &&
  1375. (FileOffset->QuadPart + Length > NtfsRWCLowThreshold)) {
  1376. PRWC_HISTORY_ENTRY NextBuffer;
  1377. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  1378. NextBuffer->Operation = StartOfWrite;
  1379. NextBuffer->Information = CompressedDataInfo->NumberOfChunks;
  1380. NextBuffer->FileOffset = (ULONG) FileOffset->QuadPart;
  1381. NextBuffer->Length = (ULONG) Length;
  1382. }
  1383. #endif
  1384. try {
  1385. //
  1386. // Get ready to loop through all of the compression units.
  1387. //
  1388. LocalOffset = FileOffset->QuadPart & ~(LONGLONG)(CompressionUnitSize - 1);
  1389. Length = (Length + (ULONG)(FileOffset->QuadPart - LocalOffset) + ChunkSize - 1) & ~(ChunkSize - 1);
  1390. NextChunkSize = &CompressedDataInfo->CompressedChunkSizes[0];
  1391. //
  1392. // Get the overhead for zero chunks and uncompressed chunks.
  1393. //
  1394. // **** temporary solution awaits Rtl routine.
  1395. //
  1396. ASSERT(CompressedDataInfo->CompressionFormatAndEngine == COMPRESSION_FORMAT_LZNT1);
  1397. ChunkOfZeros = 6;
  1398. UncompressedChunkHeader = 2;
  1399. // Status = RtlGetSpecialChunkSizes( CompressedDataInfo->CompressionFormatAndEngine,
  1400. // &ChunkOfZeros,
  1401. // &UncompressedChunkHeader );
  1402. //
  1403. // ASSERT(NT_SUCCESS(Status));
  1404. //
  1405. //
  1406. // Loop through desired compression units
  1407. //
  1408. while (TRUE) {
  1409. //
  1410. // Free any Bcb from previous pass
  1411. //
  1412. if (Bcb != NULL) {
  1413. CcUnpinData( Bcb );
  1414. Bcb = NULL;
  1415. }
  1416. //
  1417. // If there is an uncompressed stream, then we have to synchronize with that.
  1418. //
  1419. if (((PSCB)Header)->NonpagedScb->SegmentObject.DataSectionObject != NULL) {
  1420. Status = NtfsSynchronizeCompressedIo( (PSCB)Header,
  1421. &LocalOffset,
  1422. Length,
  1423. TRUE,
  1424. &CompressionSync );
  1425. if (!NT_SUCCESS(Status)) {
  1426. ASSERT( Status == STATUS_USER_MAPPED_FILE );
  1427. leave;
  1428. }
  1429. }
  1430. //
  1431. // Determine whether or not this is a full overwrite of a
  1432. // compression unit.
  1433. //
  1434. FullOverwrite = (LocalOffset >= Header->ValidDataLength.QuadPart)
  1435. ||
  1436. ((LocalOffset >= FileOffset->QuadPart) &&
  1437. (Length >= CompressionUnitSize));
  1438. //
  1439. // Calculate how much of current compression unit is being
  1440. // written, uncompressed.
  1441. //
  1442. SavedLength = Length;
  1443. if (SavedLength > CompressionUnitSize) {
  1444. SavedLength = CompressionUnitSize;
  1445. }
  1446. //
  1447. // If we are not at the start of a compression unit, calculate the
  1448. // index of the chunk we will be working on, and reduce SavedLength
  1449. // accordingly.
  1450. //
  1451. i = 0;
  1452. if (LocalOffset < FileOffset->QuadPart) {
  1453. i = (ULONG)(FileOffset->QuadPart - LocalOffset);
  1454. SavedLength -= i;
  1455. i >>= CompressedDataInfo->ChunkShift;
  1456. }
  1457. //
  1458. // Loop to calculate sum of chunk sizes being written, handling both empty
  1459. // and uncompressed chunk cases. We will remember the nonzero size of each
  1460. // chunk being written so we can merge this info with the sizes of any chunks
  1461. // not being overwritten below.
  1462. // Reserve space for a chunk of zeroes for each chunk ahead of the first one
  1463. // being written.
  1464. //
  1465. SizeToPin = ChunkOfZeros * i;
  1466. TempUlong = SavedLength >> CompressedDataInfo->ChunkShift;
  1467. TempChunkSize = NextChunkSize;
  1468. RtlZeroMemory( ChunkSizes, sizeof( ChunkSizes ));
  1469. while (TempUlong--) {
  1470. ChunkSizes[i] = *TempChunkSize;
  1471. if (*TempChunkSize == 0) {
  1472. ChunkSizes[i] += ChunkOfZeros;
  1473. ASSERT(ChunkOfZeros != 0);
  1474. } else if (*TempChunkSize == ChunkSize) {
  1475. ChunkSizes[i] += UncompressedChunkHeader;
  1476. }
  1477. SizeToPin += ChunkSizes[i];
  1478. TempChunkSize++;
  1479. i += 1;
  1480. }
  1481. //
  1482. // If this is not a full overwrite, get the current compression unit
  1483. // size and make sure we pin at least that much. Don't bother to check
  1484. // the allocation if this range of the file has not been written yet.
  1485. //
  1486. if (!FullOverwrite && (LocalOffset < ((PSCB)Header)->ValidDataToDisk)) {
  1487. NtfsFastIoQueryCompressedSize( FileObject,
  1488. (PLARGE_INTEGER)&LocalOffset,
  1489. &TempUlong );
  1490. ASSERT( TempUlong <= CompressionUnitSize );
  1491. if (TempUlong > SizeToPin) {
  1492. SizeToPin = TempUlong;
  1493. }
  1494. }
  1495. //
  1496. // At this point we are ready to overwrite data in the compression
  1497. // unit. See if the data is really compressed.
  1498. //
  1499. // If it looks like we are beyond ValidDataToDisk, then assume it is compressed
  1500. // for now, and we will see for sure later when we get the data pinned. This
  1501. // is actually an unsafe test that will occassionally send us down the "wrong"
  1502. // path. However, it is always safe to take the uncompressed path, and if we
  1503. // think the data is compressed, we always check again below.
  1504. //
  1505. IsCompressed = (BOOLEAN)(((SizeToPin <= (CompressionUnitSize - ClusterSize)) ||
  1506. (LocalOffset >= ((PSCB)Header)->ValidDataToDisk)) &&
  1507. EngineMatches);
  1508. //
  1509. // Possibly neither the new nor old data for this CompressionUnit is
  1510. // nonzero, so we must pin something so that we can cause any old allocation
  1511. // to get deleted. This code relies on any compression algorithm being
  1512. // able to express an entire compression unit of 0's in one page or less.
  1513. //
  1514. if (SizeToPin == 0) {
  1515. SizeToPin = PAGE_SIZE;
  1516. } else {
  1517. //
  1518. // Add a ulong for the null terminator.
  1519. //
  1520. SizeToPin += sizeof( ULONG );
  1521. }
  1522. Status = STATUS_SUCCESS;
  1523. //
  1524. // Round the pin size to a page boundary. Then we can tell when we need to pin a larger range.
  1525. //
  1526. SizeToPin = (SizeToPin + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
  1527. //
  1528. // Save current length in case we have to restart our work in
  1529. // the uncompressed stream.
  1530. //
  1531. TempChunkSize = NextChunkSize;
  1532. SavedLength = Length;
  1533. SavedBuffer = Buffer;
  1534. if (IsCompressed) {
  1535. //
  1536. // Map the compression unit in the compressed stream.
  1537. //
  1538. if (FullOverwrite) {
  1539. //
  1540. // If we are overwriting the entire compression unit, then
  1541. // call CcPreparePinWrite so that empty pages may be used
  1542. // instead of reading the file. Also force the byte count
  1543. // to integral pages, so no one thinks we need to read the
  1544. // last page.
  1545. //
  1546. CcPreparePinWrite( Header->FileObjectC,
  1547. (PLARGE_INTEGER)&LocalOffset,
  1548. SizeToPin,
  1549. FALSE,
  1550. PIN_WAIT | PIN_EXCLUSIVE,
  1551. &Bcb,
  1552. &CacheBuffer );
  1553. //
  1554. // Now that we are synchronized with the buffer, see if someone snuck
  1555. // in behind us and created the noncached stream since we last checked
  1556. // for that stream. If so we have to go back and get correctly synchronized.
  1557. //
  1558. if ((CompressionSync == NULL) &&
  1559. (((PSCB)Header)->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
  1560. continue;
  1561. }
  1562. //
  1563. // If it is a full overwrite, we need to initialize an empty
  1564. // buffer. **** This is not completely correct, we otherwise
  1565. // need a routine to initialize an empty compressed data buffer.
  1566. //
  1567. *(PULONG)CacheBuffer = 0;
  1568. #ifdef NTFS_RWC_DEBUG
  1569. if ((LocalOffset < NtfsRWCHighThreshold) &&
  1570. (LocalOffset + SizeToPin > NtfsRWCLowThreshold)) {
  1571. PRWC_HISTORY_ENTRY NextBuffer;
  1572. //
  1573. // Check for the case where we don't have a full Bcb.
  1574. //
  1575. if (SafeNodeType( Bcb ) == CACHE_NTC_OBCB) {
  1576. PPUBLIC_BCB NextBcb;
  1577. NextBcb = ((POBCB) Bcb)->Bcbs[0];
  1578. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  1579. NextBuffer->Operation = PartialBcb;
  1580. NextBuffer->Information = 0;
  1581. NextBuffer->FileOffset = (ULONG) NextBcb->MappedFileOffset.QuadPart;
  1582. NextBuffer->Length = NextBcb->MappedLength;
  1583. ASSERT( NextBuffer->Length <= SizeToPin );
  1584. } else {
  1585. PPUBLIC_BCB NextBcb;
  1586. ASSERT( SafeNodeType( Bcb ) == CACHE_NTC_BCB );
  1587. NextBcb = (PPUBLIC_BCB) Bcb;
  1588. ASSERT( LocalOffset + SizeToPin <= NextBcb->MappedFileOffset.QuadPart + NextBcb->MappedLength );
  1589. }
  1590. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  1591. NextBuffer->Operation = FullOverwrite;
  1592. NextBuffer->Information = 0;
  1593. NextBuffer->FileOffset = (ULONG) LocalOffset;
  1594. NextBuffer->Length = (ULONG) SizeToPin;
  1595. }
  1596. #endif
  1597. } else {
  1598. //
  1599. // Read the data from the compressed stream that we will combine
  1600. // with the chunks being written.
  1601. //
  1602. CcPinRead( Header->FileObjectC,
  1603. (PLARGE_INTEGER)&LocalOffset,
  1604. SizeToPin,
  1605. PIN_WAIT | PIN_EXCLUSIVE,
  1606. &Bcb,
  1607. &CacheBuffer );
  1608. //
  1609. // Now that we are synchronized with the buffer, see if someone snuck
  1610. // in behind us and created the noncached stream since we last checked
  1611. // for that stream. If so we have to go back and get correctly synchronized.
  1612. //
  1613. if ((CompressionSync == NULL) &&
  1614. (((PSCB)Header)->NonpagedScb->SegmentObject.DataSectionObject != NULL)) {
  1615. continue;
  1616. }
  1617. //
  1618. // Now that the data is pinned (we are synchronized with the
  1619. // CompressionUnit), we need to recalculate how much should be
  1620. // pinned. We do this by summing up all the sizes of the chunks
  1621. // that are being written with the sizes of the existing chunks
  1622. // that will remain.
  1623. //
  1624. StartOfPin = CacheBuffer;
  1625. EndOfCacheBuffer = Add2Ptr( CacheBuffer, CompressionUnitSize - ClusterSize );
  1626. i = 0;
  1627. //
  1628. // Loop through to find all the existing chunks, and remember their
  1629. // sizes if they are not being overwritten. (Remember if we overwrite
  1630. // with a chunk of all zeros, it takes nonzero bytes to do it!)
  1631. //
  1632. // This loop completes the formation of an array of chunksizes. The
  1633. // start of the array is guaranteed to be nonzero, and it terminates
  1634. // with a chunk size of 0. Note if fewer chunks are filled in than
  1635. // exist in the compression unit, that is ok - we do not need to write
  1636. // trailing chunks of 0's.
  1637. //
  1638. ChunksSeen = FALSE;
  1639. while (i < 16) {
  1640. Status = RtlDescribeChunk( CompressedDataInfo->CompressionFormatAndEngine,
  1641. &StartOfPin,
  1642. EndOfCacheBuffer,
  1643. &ChunkBuffer,
  1644. &TempUlong );
  1645. //
  1646. // If there are no more entries, see if we are done, else treat
  1647. // it as a chunk of 0's.
  1648. //
  1649. if (!NT_SUCCESS(Status)) {
  1650. ASSERT(Status == STATUS_NO_MORE_ENTRIES);
  1651. if (ChunksSeen) {
  1652. break;
  1653. }
  1654. TempUlong = ChunkOfZeros;
  1655. //
  1656. // Make sure we enter the length for a chunk of zeroes.
  1657. //
  1658. } else if (TempUlong == 0) {
  1659. TempUlong = ChunkOfZeros;
  1660. }
  1661. if (ChunkSizes[i] == 0) {
  1662. ChunkSizes[i] = TempUlong;
  1663. } else {
  1664. ChunksSeen = TRUE;
  1665. }
  1666. i += 1;
  1667. }
  1668. //
  1669. // Now sum up the sizes of the chunks we will write.
  1670. //
  1671. i = 0;
  1672. TempUlong = 0;
  1673. while (ChunkSizes[i] != 0) {
  1674. TempUlong += ChunkSizes[i];
  1675. i += 1;
  1676. }
  1677. //
  1678. // If the existing data is larger, pin that range.
  1679. //
  1680. if (TempUlong < PtrOffset(CacheBuffer, StartOfPin)) {
  1681. TempUlong = PtrOffset(CacheBuffer, StartOfPin);
  1682. }
  1683. IsCompressed = (TempUlong <= (CompressionUnitSize - ClusterSize));
  1684. //
  1685. // We now know if we will really end up with compressed data, so
  1686. // get out now stop processing if the data is not compressed.
  1687. //
  1688. if (IsCompressed) {
  1689. TempUlong += sizeof(ULONG);
  1690. //
  1691. // Now we have to repin if we actually need more space.
  1692. //
  1693. if (TempUlong > SizeToPin) {
  1694. SizeToPin = (TempUlong + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
  1695. TempBcb = Bcb;
  1696. Bcb = NULL;
  1697. //
  1698. // Read the data from the compressed stream that we will combine
  1699. // with the chunks being written.
  1700. //
  1701. CcPinRead( Header->FileObjectC,
  1702. (PLARGE_INTEGER)&LocalOffset,
  1703. SizeToPin,
  1704. PIN_WAIT | PIN_EXCLUSIVE,
  1705. &Bcb,
  1706. &CacheBuffer );
  1707. CcUnpinData( TempBcb );
  1708. TempBcb = NULL;
  1709. }
  1710. ASSERT( TempUlong <= CompressionUnitSize );
  1711. //
  1712. // Really make the data dirty by physically modifying a byte
  1713. // in each page.
  1714. //
  1715. TempUlong = 0;
  1716. while (TempUlong < SizeToPin) {
  1717. volatile PULONG NextBuffer;
  1718. NextBuffer = Add2Ptr( CacheBuffer, TempUlong );
  1719. *NextBuffer = *NextBuffer;
  1720. TempUlong += PAGE_SIZE;
  1721. }
  1722. #ifdef NTFS_RWC_DEBUG
  1723. if ((LocalOffset < NtfsRWCHighThreshold) &&
  1724. (LocalOffset + SizeToPin > NtfsRWCLowThreshold)) {
  1725. PRWC_HISTORY_ENTRY NextBuffer;
  1726. NextBuffer = NtfsGetHistoryEntry( (PSCB) Header );
  1727. NextBuffer->Operation = SetDirty;
  1728. NextBuffer->Information = 0;
  1729. NextBuffer->FileOffset = (ULONG) LocalOffset;
  1730. NextBuffer->Length = (ULONG) SizeToPin;
  1731. }
  1732. #endif
  1733. CcSetDirtyPinnedData( Bcb, NULL );
  1734. }
  1735. }
  1736. EndOfCacheBuffer = Add2Ptr( CacheBuffer, CompressionUnitSize - ClusterSize );
  1737. //
  1738. // Now loop through desired chunks (if it is still compressed)
  1739. //
  1740. if (IsCompressed) {
  1741. do {
  1742. //
  1743. // We may not have reached the first chunk yet.
  1744. //
  1745. if (LocalOffset >= FileOffset->QuadPart) {
  1746. //
  1747. // Reserve space for the current chunk.
  1748. //
  1749. Status = RtlReserveChunk( CompressedDataInfo->CompressionFormatAndEngine,
  1750. &CacheBuffer,
  1751. EndOfCacheBuffer,
  1752. &ChunkBuffer,
  1753. *TempChunkSize );
  1754. if (!NT_SUCCESS(Status)) {
  1755. break;
  1756. }
  1757. //
  1758. // If the caller wants an MdlChain, then handle the Mdl
  1759. // processing here.
  1760. //
  1761. if (MdlChain != NULL) {
  1762. //
  1763. // If we have not started remembering an Mdl buffer,
  1764. // then do so now.
  1765. //
  1766. if (MdlLength == 0) {
  1767. MdlBuffer = ChunkBuffer;
  1768. //
  1769. // Otherwise we just have to increase the length
  1770. // and check for an uncompressed chunk, because that
  1771. // forces us to emit the previous Mdl since we do
  1772. // not transmit the chunk header in this case.
  1773. //
  1774. } else {
  1775. //
  1776. // In the rare case that we hit an individual chunk
  1777. // that did not compress or is all 0's, we have to
  1778. // emit what we had (which captures the Bcb pointer),
  1779. // and start a new Mdl buffer.
  1780. //
  1781. if ((*TempChunkSize == ChunkSize) || (*TempChunkSize == 0)) {
  1782. NtfsAddToCompressedMdlChain( MdlChain,
  1783. MdlBuffer,
  1784. MdlLength,
  1785. Header->PagingIoResource,
  1786. Bcb,
  1787. IoWriteAccess,
  1788. TRUE );
  1789. Bcb = NULL;
  1790. MdlBuffer = ChunkBuffer;
  1791. MdlLength = 0;
  1792. }
  1793. }
  1794. MdlLength += *TempChunkSize;
  1795. //
  1796. // Else copy next chunk (compressed or not).
  1797. //
  1798. } else {
  1799. RtlCopyBytes( ChunkBuffer, Buffer, *TempChunkSize );
  1800. //
  1801. // Advance input buffer by bytes copied.
  1802. //
  1803. Buffer = (PCHAR)Buffer + *TempChunkSize;
  1804. }
  1805. TempChunkSize += 1;
  1806. //
  1807. // If we are skipping over a nonexistant chunk, then we have
  1808. // to reserve a chunk of zeros.
  1809. //
  1810. } else {
  1811. //
  1812. // If we have not reached our chunk, then describe the current
  1813. // chunk in order to skip over it.
  1814. //
  1815. Status = RtlDescribeChunk( CompressedDataInfo->CompressionFormatAndEngine,
  1816. &CacheBuffer,
  1817. EndOfCacheBuffer,
  1818. &ChunkBuffer,
  1819. &TempUlong );
  1820. //
  1821. // If there is not current chunk, we must insert a chunk of zeros.
  1822. //
  1823. if (Status == STATUS_NO_MORE_ENTRIES) {
  1824. Status = RtlReserveChunk( CompressedDataInfo->CompressionFormatAndEngine,
  1825. &CacheBuffer,
  1826. EndOfCacheBuffer,
  1827. &ChunkBuffer,
  1828. 0 );
  1829. if (!NT_SUCCESS(Status)) {
  1830. ASSERT(NT_SUCCESS(Status));
  1831. break;
  1832. }
  1833. //
  1834. // Get out if we got some other kind of unexpected error.
  1835. //
  1836. } else if (!NT_SUCCESS(Status)) {
  1837. ASSERT(NT_SUCCESS(Status));
  1838. break;
  1839. }
  1840. }
  1841. //
  1842. // Reduce length by chunk copied, and check if we are done.
  1843. //
  1844. if (Length > ChunkSize) {
  1845. Length -= ChunkSize;
  1846. } else {
  1847. goto Done;
  1848. }
  1849. LocalOffset += ChunkSize;
  1850. } while ((LocalOffset & (CompressionUnitSize - 1)) != 0);
  1851. //
  1852. // If this is an Mdl call, then it is time to add to the MdlChain
  1853. // before moving to the next compression unit.
  1854. //
  1855. if (MdlLength != 0) {
  1856. NtfsAddToCompressedMdlChain( MdlChain,
  1857. MdlBuffer,
  1858. MdlLength,
  1859. Header->PagingIoResource,
  1860. Bcb,
  1861. IoWriteAccess,
  1862. TRUE );
  1863. Bcb = NULL;
  1864. MdlLength = 0;
  1865. }
  1866. }
  1867. }
  1868. //
  1869. // Uncompressed loop.
  1870. //
  1871. if (!IsCompressed || !NT_SUCCESS(Status)) {
  1872. //
  1873. // If we get here for an Mdl request, just tell him to send
  1874. // it uncompressed!
  1875. //
  1876. if (MdlChain != NULL) {
  1877. if (NT_SUCCESS(Status)) {
  1878. Status = STATUS_BUFFER_OVERFLOW;
  1879. }
  1880. goto Done;
  1881. //
  1882. // If we are going to write the uncompressed stream,
  1883. // we have to make sure it is there.
  1884. //
  1885. } else if (((PSCB)Header)->FileObject == NULL) {
  1886. Status = STATUS_NOT_MAPPED_DATA;
  1887. goto Done;
  1888. }
  1889. //
  1890. // Restore sizes and pointers to the beginning of the
  1891. // current compression unit, and we will handle the
  1892. // data uncompressed.
  1893. //
  1894. LocalOffset -= SavedLength - Length;
  1895. Length = SavedLength;
  1896. Buffer = SavedBuffer;
  1897. TempChunkSize = NextChunkSize;
  1898. //
  1899. // We may have a Bcb from the above loop to unpin.
  1900. // Then we must flush and purge the compressed
  1901. // stream before proceding.
  1902. //
  1903. if (Bcb != NULL) {
  1904. CcUnpinData(Bcb);
  1905. Bcb = NULL;
  1906. }
  1907. //
  1908. // We must first flush and purge the compressed stream
  1909. // since we will be writing into the uncompressed stream.
  1910. // The flush is actually only necessary if we are not doing
  1911. // a full overwrite anyway.
  1912. //
  1913. if (!FullOverwrite) {
  1914. CcFlushCache( Header->FileObjectC->SectionObjectPointer,
  1915. (PLARGE_INTEGER)&LocalOffset,
  1916. CompressionUnitSize,
  1917. NULL );
  1918. }
  1919. CcPurgeCacheSection( Header->FileObjectC->SectionObjectPointer,
  1920. (PLARGE_INTEGER)&LocalOffset,
  1921. CompressionUnitSize,
  1922. FALSE );
  1923. //
  1924. // If LocalOffset was rounded down to a compression
  1925. // unit boundary (must have failed in the first
  1926. // compression unit), then start from the actual
  1927. // starting FileOffset.
  1928. //
  1929. if (LocalOffset < FileOffset->QuadPart) {
  1930. Length -= (ULONG)(FileOffset->QuadPart - LocalOffset);
  1931. LocalOffset = FileOffset->QuadPart;
  1932. }
  1933. //
  1934. // Map the compression unit in the uncompressed
  1935. // stream.
  1936. //
  1937. SizeToPin = (((Length < CompressionUnitSize) ? Length : CompressionUnitSize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
  1938. CcPinRead( ((PSCB)Header)->FileObject,
  1939. (PLARGE_INTEGER)&LocalOffset,
  1940. SizeToPin,
  1941. TRUE,
  1942. &Bcb,
  1943. &CacheBuffer );
  1944. CcSetDirtyPinnedData( Bcb, NULL );
  1945. //
  1946. // Now loop through desired chunks
  1947. //
  1948. do {
  1949. //
  1950. // If this chunk is compressed, then decompress it
  1951. // into the cache.
  1952. //
  1953. if (*TempChunkSize != ChunkSize) {
  1954. Status = RtlDecompressBuffer( CompressedDataInfo->CompressionFormatAndEngine,
  1955. CacheBuffer,
  1956. ChunkSize,
  1957. Buffer,
  1958. *TempChunkSize,
  1959. &SavedLength );
  1960. //
  1961. // See if the data is ok.
  1962. //
  1963. if (!NT_SUCCESS(Status)) {
  1964. ASSERT(NT_SUCCESS(Status));
  1965. goto Done;
  1966. }
  1967. //
  1968. // Zero to the end of the chunk if it was not all there.
  1969. //
  1970. if (SavedLength != ChunkSize) {
  1971. RtlZeroMemory( Add2Ptr(CacheBuffer, SavedLength),
  1972. ChunkSize - SavedLength );
  1973. }
  1974. } else {
  1975. //
  1976. // Copy next chunk (it's not compressed).
  1977. //
  1978. RtlCopyBytes( CacheBuffer, Buffer, ChunkSize );
  1979. }
  1980. //
  1981. // Advance input buffer by bytes copied.
  1982. //
  1983. Buffer = (PCHAR)Buffer + *TempChunkSize;
  1984. CacheBuffer = (PCHAR)CacheBuffer + ChunkSize;
  1985. TempChunkSize += 1;
  1986. //
  1987. // Reduce length by chunk copied, and check if we are done.
  1988. //
  1989. if (Length > ChunkSize) {
  1990. Length -= ChunkSize;
  1991. } else {
  1992. goto Done;
  1993. }
  1994. LocalOffset += ChunkSize;
  1995. } while ((LocalOffset & (CompressionUnitSize - 1)) != 0);
  1996. }
  1997. //
  1998. // Now we can finally advance our pointer into the chunk sizes.
  1999. //
  2000. NextChunkSize = TempChunkSize;
  2001. }
  2002. Done: NOTHING;
  2003. if ((MdlLength != 0) && NT_SUCCESS(Status)) {
  2004. NtfsAddToCompressedMdlChain( MdlChain,
  2005. MdlBuffer,
  2006. MdlLength,
  2007. Header->PagingIoResource,
  2008. Bcb,
  2009. IoWriteAccess,
  2010. TRUE );
  2011. Bcb = NULL;
  2012. }
  2013. } except( FsRtlIsNtstatusExpected((Status = GetExceptionCode()))
  2014. ? EXCEPTION_EXECUTE_HANDLER
  2015. : EXCEPTION_CONTINUE_SEARCH ) {
  2016. NOTHING;
  2017. }
  2018. //
  2019. // Unpin the Bcbs we still have.
  2020. //
  2021. if (TempBcb != NULL) {
  2022. CcUnpinData( TempBcb );
  2023. }
  2024. if (Bcb != NULL) {
  2025. CcUnpinData( Bcb );
  2026. }
  2027. if (CompressionSync != NULL) {
  2028. NtfsReleaseCompressionSync( CompressionSync );
  2029. }
  2030. //
  2031. // Perform Mdl-specific processing.
  2032. //
  2033. if (MdlChain != NULL) {
  2034. //
  2035. // On error, cleanup any MdlChain we built up
  2036. //
  2037. if (!NT_SUCCESS(Status)) {
  2038. NtfsCleanupCompressedMdlChain( *MdlChain, TRUE );
  2039. *MdlChain = NULL;
  2040. //
  2041. // Change owner Id for the Scb and Bcbs we are holding.
  2042. //
  2043. } else if (*MdlChain != NULL) {
  2044. NtfsSetMdlBcbOwners( *MdlChain );
  2045. ExSetResourceOwnerPointer( Header->PagingIoResource, (PVOID)((PCHAR)*MdlChain + 3) );
  2046. }
  2047. }
  2048. #ifdef NTFS_RWCMP_TRACE
  2049. if (NtfsCompressionTrace && IsSyscache(Header)) {
  2050. DbgPrint(" Return Status = %08lx\n", Status);
  2051. }
  2052. #endif
  2053. return Status;
  2054. UNREFERENCED_PARAMETER( DeviceObject );
  2055. }
  2056. BOOLEAN
  2057. NtfsMdlWriteCompleteCompressed (
  2058. IN struct _FILE_OBJECT *FileObject,
  2059. IN PLARGE_INTEGER FileOffset,
  2060. IN PMDL MdlChain,
  2061. IN struct _DEVICE_OBJECT *DeviceObject
  2062. )
  2063. /*++
  2064. Routine Description:
  2065. This routine frees resources and the Mdl Chain after a compressed write.
  2066. Arguments:
  2067. FileObject - pointer to the file object for the request.
  2068. MdlChain - as returned from compressed write.
  2069. DeviceObject - As required for a fast I/O routine.
  2070. Return Value:
  2071. TRUE - if fast path succeeded
  2072. FALSE - if an Irp is required
  2073. --*/
  2074. {
  2075. PERESOURCE ResourceToRelease;
  2076. if (MdlChain != NULL) {
  2077. ResourceToRelease = *(PERESOURCE *)Add2Ptr( MdlChain, MdlChain->Size + sizeof( PBCB ));
  2078. NtfsCleanupCompressedMdlChain( MdlChain, FALSE );
  2079. //
  2080. // Release the held resource.
  2081. //
  2082. ExReleaseResourceForThread( ResourceToRelease, (ERESOURCE_THREAD)((PCHAR)MdlChain + 3) );
  2083. }
  2084. return TRUE;
  2085. UNREFERENCED_PARAMETER( FileObject );
  2086. UNREFERENCED_PARAMETER( DeviceObject );
  2087. UNREFERENCED_PARAMETER( FileOffset );
  2088. }
  2089. VOID
  2090. NtfsAddToCompressedMdlChain (
  2091. IN OUT PMDL *MdlChain,
  2092. IN PVOID MdlBuffer,
  2093. IN ULONG MdlLength,
  2094. IN PERESOURCE ResourceToRelease OPTIONAL,
  2095. IN PBCB Bcb,
  2096. IN LOCK_OPERATION Operation,
  2097. IN ULONG IsCompressed
  2098. )
  2099. /*++
  2100. Routine Description:
  2101. This routine creates and Mdl for the described buffer and adds it to
  2102. the chain.
  2103. Arguments:
  2104. MdlChain - MdlChain pointer to append the first/new Mdl to.
  2105. MdlBuffer - Buffer address for this Mdl.
  2106. MdlLength - Length of buffer in bytes.
  2107. ResourceToRelease - Indicates which resource to release, only specified for compressed IO.
  2108. Bcb - Bcb to remember with this Mdl, to be freed when Mdl completed
  2109. Operation - IoReadAccess or IoWriteAccess
  2110. IsCompressed - Supplies TRUE if the Bcb is in the compressed stream
  2111. Return Value:
  2112. None.
  2113. --*/
  2114. {
  2115. PMDL Mdl, MdlTemp;
  2116. ASSERT(sizeof(ULONG) == sizeof(PBCB));
  2117. //
  2118. // Now attempt to allocate an Mdl to describe the mapped data.
  2119. // We "lie" about the length of the buffer by one page, in order
  2120. // to get an extra field to store a pointer to the Bcb in.
  2121. //
  2122. Mdl = IoAllocateMdl( MdlBuffer,
  2123. (MdlLength + (2 * PAGE_SIZE)),
  2124. FALSE,
  2125. FALSE,
  2126. NULL );
  2127. if (Mdl == NULL) {
  2128. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2129. }
  2130. //
  2131. // Now subtract out the space we reserved for our Bcb pointer
  2132. // and then store it.
  2133. //
  2134. Mdl->Size -= 2 * sizeof(ULONG);
  2135. Mdl->ByteCount -= 2 * PAGE_SIZE;
  2136. //
  2137. // Note that this probe should never fail, because we can
  2138. // trust the address returned from CcPinFileData. Therefore,
  2139. // if we succeed in allocating the Mdl above, we should
  2140. // manage to elude any expected exceptions through the end
  2141. // of this loop.
  2142. //
  2143. if (Mdl->ByteCount != 0) {
  2144. MmProbeAndLockPages( Mdl, KernelMode, Operation );
  2145. }
  2146. //
  2147. // Only store the Bcb if this is the compressed stream.
  2148. //
  2149. if (!IsCompressed && (Bcb != NULL)) {
  2150. Bcb = NULL;
  2151. }
  2152. *(PBCB *)Add2Ptr( Mdl, Mdl->Size ) = Bcb;
  2153. *(PERESOURCE *)Add2Ptr( Mdl, Mdl->Size + sizeof( PBCB )) = ResourceToRelease;
  2154. //
  2155. // Now link the Mdl into the caller's chain
  2156. //
  2157. if ( *MdlChain == NULL ) {
  2158. *MdlChain = Mdl;
  2159. } else {
  2160. MdlTemp = CONTAINING_RECORD( *MdlChain, MDL, Next );
  2161. while (MdlTemp->Next != NULL) {
  2162. MdlTemp = MdlTemp->Next;
  2163. }
  2164. MdlTemp->Next = Mdl;
  2165. }
  2166. }
  2167. VOID
  2168. NtfsSetMdlBcbOwners (
  2169. IN PMDL MdlChain
  2170. )
  2171. /*++
  2172. Routine Description:
  2173. This routine may be called to set all of the Bcb resource owners in an Mdl
  2174. to be equal to the address of the first element in the MdlChain, so that they
  2175. can be freed in the context of a different thread.
  2176. Arguments:
  2177. MdlChain - Supplies the MdlChain to process
  2178. Return Value:
  2179. None.
  2180. --*/
  2181. {
  2182. PBCB Bcb;
  2183. while (MdlChain != NULL) {
  2184. //
  2185. // Unpin the Bcb we saved away, and restore the Mdl counts
  2186. // we altered.
  2187. //
  2188. Bcb = *(PBCB *)Add2Ptr(MdlChain, MdlChain->Size);
  2189. if (Bcb != NULL) {
  2190. CcSetBcbOwnerPointer( Bcb, (PVOID)((PCHAR)MdlChain + 3) );
  2191. }
  2192. MdlChain = MdlChain->Next;
  2193. }
  2194. }
  2195. VOID
  2196. NtfsCleanupCompressedMdlChain (
  2197. IN PMDL MdlChain,
  2198. IN ULONG Error
  2199. )
  2200. /*++
  2201. Routine Description:
  2202. This routine is called to free all of the resources associated with a
  2203. compressed Mdl chain. It may be called for an error in the processing
  2204. of a request or when a request completes.
  2205. Arguments:
  2206. MdlChain - Supplies the address of the first element in the chain to clean up.
  2207. Error - Supplies TRUE on error (resources are still owned by current thread) or
  2208. FALSE on a normal completion (resources owned by MdlChain).
  2209. Return Value:
  2210. None.
  2211. --*/
  2212. {
  2213. PMDL MdlTemp;
  2214. PBCB Bcb;
  2215. while (MdlChain != NULL) {
  2216. //
  2217. // Save a pointer to the next guy in the chain.
  2218. //
  2219. MdlTemp = MdlChain->Next;
  2220. //
  2221. // Unlock the pages.
  2222. //
  2223. if (MdlChain->ByteCount != 0) {
  2224. MmUnlockPages( MdlChain );
  2225. }
  2226. //
  2227. // Unpin the Bcb we saved away, and restore the Mdl counts
  2228. // we altered.
  2229. //
  2230. Bcb = *(PBCB *)Add2Ptr(MdlChain, MdlChain->Size);
  2231. if (Bcb != NULL) {
  2232. if (Error) {
  2233. CcUnpinData( Bcb );
  2234. } else {
  2235. CcUnpinDataForThread( Bcb, (ERESOURCE_THREAD)((PCHAR)MdlChain + 3) );
  2236. }
  2237. }
  2238. MdlChain->Size += 2 * sizeof(ULONG);
  2239. MdlChain->ByteCount += 2 * PAGE_SIZE;
  2240. IoFreeMdl( MdlChain );
  2241. MdlChain = MdlTemp;
  2242. }
  2243. }
  2244. NTSTATUS
  2245. NtfsSynchronizeUncompressedIo (
  2246. IN PSCB Scb,
  2247. IN PLONGLONG FileOffset OPTIONAL,
  2248. IN ULONG Length,
  2249. IN ULONG WriteAccess,
  2250. IN OUT PCOMPRESSION_SYNC *CompressionSync
  2251. )
  2252. /*++
  2253. Routine Description:
  2254. This routine attempts to synchronize with the compressed data cache,
  2255. for an I/O in the uncompressed cache. The view in the compressed cache
  2256. is locked shared or exclusive without reading. Then the compressed cache
  2257. is flushed and purged as appropriate.
  2258. We will allocate a COMPRESSION_SYNC structure to serialize each cache
  2259. manager view and use that for the locking granularity.
  2260. Arguments:
  2261. Scb - Supplies the Scb for the stream.
  2262. FileOffset - Byte offset in file for desired data. NULL if we are to
  2263. flush and purge the entire file.
  2264. Length - Length of desired data in bytes.
  2265. WriteAccess - Supplies TRUE if the caller plans to do a write, or FALSE
  2266. for a read.
  2267. CompressionSync - Synchronization object to serialize access to the view.
  2268. The caller's routine is responsible for releasing this.
  2269. Return Value:
  2270. Status of the flush operation, or STATUS_UNSUCCESSFUL for a WriteAccess
  2271. where the purge failed.
  2272. --*/
  2273. {
  2274. ULONG Change = 0;
  2275. IO_STATUS_BLOCK IoStatus;
  2276. PSECTION_OBJECT_POINTERS SectionObjectPointers = &Scb->NonpagedScb->SegmentObjectC;
  2277. LONGLONG LocalFileOffset;
  2278. PLONGLONG LocalOffsetPtr;
  2279. if (ARGUMENT_PRESENT( FileOffset )) {
  2280. LocalFileOffset = *FileOffset & ~(VACB_MAPPING_GRANULARITY - 1);
  2281. LocalOffsetPtr = &LocalFileOffset;
  2282. ASSERT( ((*FileOffset & (VACB_MAPPING_GRANULARITY - 1)) + Length) <= VACB_MAPPING_GRANULARITY );
  2283. } else {
  2284. LocalFileOffset = 0;
  2285. LocalOffsetPtr = NULL;
  2286. Length = 0;
  2287. }
  2288. IoStatus.Status = STATUS_SUCCESS;
  2289. if ((*CompressionSync == NULL) || ((*CompressionSync)->FileOffset != LocalFileOffset)) {
  2290. if (*CompressionSync != NULL) {
  2291. NtfsReleaseCompressionSync( *CompressionSync );
  2292. *CompressionSync = NULL;
  2293. }
  2294. *CompressionSync = NtfsAcquireCompressionSync( LocalFileOffset, Scb, WriteAccess );
  2295. //
  2296. // Always flush the remainder of the Vacb. This is to prevent a problem if MM reads additional
  2297. // pages into section because of the page fault clustering.
  2298. //
  2299. if (ARGUMENT_PRESENT( FileOffset )) {
  2300. LocalFileOffset = *FileOffset & ~((ULONG_PTR)Scb->CompressionUnit - 1);
  2301. Length = VACB_MAPPING_GRANULARITY - (((ULONG) LocalFileOffset) & (VACB_MAPPING_GRANULARITY - 1));
  2302. }
  2303. //
  2304. // We must always flush the other cache.
  2305. //
  2306. CcFlushCache( SectionObjectPointers,
  2307. (PLARGE_INTEGER) LocalOffsetPtr,
  2308. Length,
  2309. &IoStatus );
  2310. #ifdef NTFS_RWCMP_TRACE
  2311. if (NtfsCompressionTrace && IsSyscache(Scb)) {
  2312. DbgPrint(" CcFlushCache: FO = %08lx, Len = %08lx, IoStatus = %08lx, Scb = %08lx\n",
  2313. (ULONG)LocalFileOffset,
  2314. Length,
  2315. IoStatus.Status,
  2316. Scb );
  2317. }
  2318. #endif
  2319. //
  2320. // On writes, we purge the other cache after a successful flush.
  2321. //
  2322. if (WriteAccess && NT_SUCCESS(IoStatus.Status)) {
  2323. if (!CcPurgeCacheSection( SectionObjectPointers,
  2324. (PLARGE_INTEGER) LocalOffsetPtr,
  2325. Length,
  2326. FALSE )) {
  2327. return STATUS_UNSUCCESSFUL;
  2328. }
  2329. }
  2330. }
  2331. return IoStatus.Status;
  2332. }
  2333. NTSTATUS
  2334. NtfsSynchronizeCompressedIo (
  2335. IN PSCB Scb,
  2336. IN PLONGLONG FileOffset,
  2337. IN ULONG Length,
  2338. IN ULONG WriteAccess,
  2339. IN OUT PCOMPRESSION_SYNC *CompressionSync
  2340. )
  2341. /*++
  2342. Routine Description:
  2343. This routine attempts to synchronize with the uncompressed data cache,
  2344. for an I/O in the compressed cache. The range in the compressed cache
  2345. is assumed to already be locked by the caller. Then the uncompressed cache
  2346. is flushed and purged as appropriate.
  2347. We will allocate a COMPRESSION_SYNC structure to serialize each cache
  2348. manager view and use that for the locking granularity.
  2349. Arguments:
  2350. Scb - Supplies the Scb for the stream.
  2351. FileOffset - Byte offset in file for desired data.
  2352. Length - Length of desired data in bytes.
  2353. WriteAccess - Supplies TRUE if the caller plans to do a write, or FALSE
  2354. for a read.
  2355. CompressionSync - Synchronization object to serialize access to the view.
  2356. The caller's routine is responsible for releasing this.
  2357. Return Value:
  2358. Status of the flush operation, or STATUS_USER_MAPPED_FILE for a WriteAccess
  2359. where the purge failed. (This is the only expected case where a purge would
  2360. fail.
  2361. --*/
  2362. {
  2363. IO_STATUS_BLOCK IoStatus;
  2364. PSECTION_OBJECT_POINTERS SectionObjectPointers = &Scb->NonpagedScb->SegmentObject;
  2365. LONGLONG LocalFileOffset = *FileOffset & ~(VACB_MAPPING_GRANULARITY - 1);
  2366. IoStatus.Status = STATUS_SUCCESS;
  2367. if ((*CompressionSync == NULL) || ((*CompressionSync)->FileOffset != LocalFileOffset)) {
  2368. //
  2369. // Release any previous view and Lock the current view.
  2370. //
  2371. if (*CompressionSync != NULL) {
  2372. NtfsReleaseCompressionSync( *CompressionSync );
  2373. *CompressionSync = NULL;
  2374. }
  2375. *CompressionSync = NtfsAcquireCompressionSync( LocalFileOffset, Scb, WriteAccess );
  2376. //
  2377. // Now that we are synchronized on a view, test for a write to a user-mapped file.
  2378. // In case we keep hitting this path, this is better than waiting for a purge to
  2379. // fail.
  2380. //
  2381. if (WriteAccess &&
  2382. (FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE ) ||
  2383. FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE ))) {
  2384. return STATUS_USER_MAPPED_FILE;
  2385. }
  2386. //
  2387. // Always flush the remainder of the Vacb. This is to prevent a problem if MM reads additional
  2388. // pages into section because of the page fault clustering.
  2389. //
  2390. LocalFileOffset = *FileOffset & ~((ULONG_PTR)Scb->CompressionUnit - 1);
  2391. Length = VACB_MAPPING_GRANULARITY - (((ULONG) LocalFileOffset) & (VACB_MAPPING_GRANULARITY - 1));
  2392. //
  2393. // We must always flush the other cache.
  2394. //
  2395. CcFlushCache( SectionObjectPointers,
  2396. (PLARGE_INTEGER)&LocalFileOffset,
  2397. Length,
  2398. &IoStatus );
  2399. //
  2400. // On writes, we purge the other cache after a successful flush.
  2401. //
  2402. if (WriteAccess && NT_SUCCESS(IoStatus.Status)) {
  2403. if (!CcPurgeCacheSection( SectionObjectPointers,
  2404. (PLARGE_INTEGER)&LocalFileOffset,
  2405. Length,
  2406. FALSE )) {
  2407. return STATUS_USER_MAPPED_FILE;
  2408. }
  2409. }
  2410. }
  2411. return IoStatus.Status;
  2412. }
  2413. PCOMPRESSION_SYNC
  2414. NtfsAcquireCompressionSync (
  2415. IN LONGLONG FileOffset,
  2416. IN PSCB Scb,
  2417. IN ULONG WriteAccess
  2418. )
  2419. /*++
  2420. Routine Description:
  2421. This routine is called to lock a range of a stream to serialize the compressed and
  2422. uncompressed IO.
  2423. Arguments:
  2424. FileOffset - File offset to lock. This will be rounded to a cache view boundary.
  2425. Scb - Supplies the Scb for the stream.
  2426. WriteAccess - Indicates if the user wants write access. We will acquire the range
  2427. exclusively in that case.
  2428. Return Value:
  2429. PCOMPRESSION_SYNC - A pointer to the synchronization object for the range. This routine may
  2430. raise, typically if the structure can't be allocated.
  2431. --*/
  2432. {
  2433. PCOMPRESSION_SYNC CompressionSync = NULL;
  2434. PCOMPRESSION_SYNC NewCompressionSync;
  2435. BOOLEAN FoundSync = FALSE;
  2436. PAGED_CODE();
  2437. //
  2438. // Round the file offset down to a view boundary.
  2439. //
  2440. ((PLARGE_INTEGER) &FileOffset)->LowPart &= ~(VACB_MAPPING_GRANULARITY - 1);
  2441. //
  2442. // Acquire the mutex for the stream. Then walk and look for a matching resource.
  2443. //
  2444. NtfsAcquireFsrtlHeader( Scb );
  2445. CompressionSync = (PCOMPRESSION_SYNC) Scb->ScbType.Data.CompressionSyncList.Flink;
  2446. while (CompressionSync != (PCOMPRESSION_SYNC) &Scb->ScbType.Data.CompressionSyncList) {
  2447. //
  2448. // Continue if we haven't found our entry.
  2449. //
  2450. if (CompressionSync->FileOffset < FileOffset) {
  2451. //
  2452. // Go to the next entry.
  2453. //
  2454. CompressionSync = (PCOMPRESSION_SYNC) CompressionSync->CompressionLinks.Flink;
  2455. continue;
  2456. }
  2457. if (CompressionSync->FileOffset == FileOffset) {
  2458. FoundSync = TRUE;
  2459. }
  2460. //
  2461. // Exit in any case.
  2462. //
  2463. break;
  2464. }
  2465. //
  2466. // If we didn't find the entry then attempt to allocate a new one.
  2467. //
  2468. if (!FoundSync) {
  2469. NewCompressionSync = (PCOMPRESSION_SYNC) ExAllocateFromNPagedLookasideList( &NtfsCompressSyncLookasideList );
  2470. //
  2471. // Release the mutex and raise an error if we couldn't allocate.
  2472. //
  2473. if (NewCompressionSync == NULL) {
  2474. NtfsReleaseFsrtlHeader( Scb );
  2475. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  2476. }
  2477. //
  2478. // We have the new entry and know where it belongs in the list. Do the final initialization
  2479. // and add it to the list.
  2480. //
  2481. NewCompressionSync->FileOffset = FileOffset;
  2482. NewCompressionSync->Scb = Scb;
  2483. //
  2484. // Add it just ahead of the entry we stopped at.
  2485. //
  2486. InsertTailList( &CompressionSync->CompressionLinks, &NewCompressionSync->CompressionLinks );
  2487. CompressionSync = NewCompressionSync;
  2488. }
  2489. //
  2490. // We know have the structure. Reference it so it can't go away. Then drop the
  2491. // mutex and wait for it.
  2492. //
  2493. CompressionSync->ReferenceCount += 1;
  2494. NtfsReleaseFsrtlHeader( Scb );
  2495. if (WriteAccess) {
  2496. ExAcquireResourceExclusiveLite( &CompressionSync->Resource, TRUE );
  2497. } else {
  2498. ExAcquireResourceSharedLite( &CompressionSync->Resource, TRUE );
  2499. }
  2500. return CompressionSync;
  2501. }
  2502. VOID
  2503. NtfsReleaseCompressionSync (
  2504. IN PCOMPRESSION_SYNC CompressionSync
  2505. )
  2506. /*++
  2507. Routine Description:
  2508. This routine is called to release a range in a stream which was locked serial compressed and
  2509. uncompressed IO.
  2510. Arguments:
  2511. CompressionSync - Pointer to the synchronization object.
  2512. Return Value:
  2513. None.
  2514. --*/
  2515. {
  2516. PSCB Scb = CompressionSync->Scb;
  2517. PAGED_CODE();
  2518. //
  2519. // Release the resource and then acquire the mutext for the stream. If we are the last
  2520. // reference then free the structure.
  2521. //
  2522. ExReleaseResourceLite( &CompressionSync->Resource );
  2523. NtfsAcquireFsrtlHeader( Scb );
  2524. CompressionSync->ReferenceCount -= 1;
  2525. if (CompressionSync->ReferenceCount == 0) {
  2526. RemoveEntryList( &CompressionSync->CompressionLinks );
  2527. ExFreeToNPagedLookasideList( &NtfsCompressSyncLookasideList, CompressionSync );
  2528. }
  2529. NtfsReleaseFsrtlHeader( Scb );
  2530. return;
  2531. }