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.

1442 lines
42 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. CacheSup.c
  5. Abstract:
  6. This module implements the cache management routines for Ntfs
  7. Author:
  8. Your Name [Email] dd-Mon-Year
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. //
  13. // The Bug check file id for this module
  14. //
  15. #define BugCheckFileId (NTFS_BUG_CHECK_CACHESUP)
  16. #define MAX_ZERO_THRESHOLD (0x00400000)
  17. //
  18. // Local debug trace level
  19. //
  20. #define Dbg (DEBUG_TRACE_CACHESUP)
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text(PAGE, NtfsCompleteMdl)
  23. #pragma alloc_text(PAGE, NtfsCreateInternalStreamCommon)
  24. #pragma alloc_text(PAGE, NtfsDeleteInternalAttributeStream)
  25. #pragma alloc_text(PAGE, NtfsMapStream)
  26. #pragma alloc_text(PAGE, NtfsPinMappedData)
  27. #pragma alloc_text(PAGE, NtfsPinStream)
  28. #pragma alloc_text(PAGE, NtfsPreparePinWriteStream)
  29. #pragma alloc_text(PAGE, NtfsZeroData)
  30. #endif
  31. VOID
  32. NtfsCreateInternalStreamCommon (
  33. IN PIRP_CONTEXT IrpContext,
  34. IN PSCB Scb,
  35. IN BOOLEAN UpdateScb,
  36. IN BOOLEAN CompressedStream,
  37. IN UNICODE_STRING const *StreamName
  38. )
  39. /*++
  40. Routine Description:
  41. This routine is called to prepare a stream file associated with a
  42. particular attribute of a file. On return, the Scb for the attribute
  43. will have an associated stream file object. On return, this
  44. stream file will have been initialized through the cache manager.
  45. TEMPCODE The following assumptions have been made or if open issue,
  46. still unresolved.
  47. - Assume. The call to create Scb will initialize the Mcb for
  48. the non-resident case.
  49. - Assume. When this file is created I increment the open count
  50. but not the unclean count for this Scb. When we are done with
  51. the stream file, we should uninitialize it and dereference it.
  52. We also set the file object pointer to NULL. Close will then
  53. do the correct thing.
  54. - Assume. Since this call is likely to be followed shortly by
  55. either a read or write, the cache map is initialized here.
  56. Arguments:
  57. Scb - Supplies the address to store the Scb for this attribute and
  58. stream file. This will exist on return from this function.
  59. UpdateScb - Indicates if the caller wants to update the Scb from the
  60. attribute.
  61. CompressedStream - Supplies TRUE if caller wishes to create the
  62. compressed stream.
  63. StreamName - Internal stream name or NULL is there isn't one available.
  64. This is a constant value so we don't have to allocate any pool.
  65. Return Value:
  66. None.
  67. --*/
  68. {
  69. PVCB Vcb = Scb->Vcb;
  70. CC_FILE_SIZES CcFileSizes;
  71. PFILE_OBJECT CallersFileObject;
  72. PFILE_OBJECT *FileObjectPtr = &Scb->FileObject;
  73. PFILE_OBJECT UnwindStreamFile = NULL;
  74. BOOLEAN UnwindInitializeCacheMap = FALSE;
  75. BOOLEAN DecrementScbCleanup = FALSE;
  76. BOOLEAN AcquiredMutex = FALSE;
  77. ASSERT_IRP_CONTEXT( IrpContext );
  78. PAGED_CODE();
  79. DebugTrace( +1, Dbg, ("NtfsCreateInternalAttributeStream\n") );
  80. DebugTrace( 0, Dbg, ("Scb -> %08lx\n", Scb) );
  81. //
  82. // Change FileObjectPtr if he wants the compressed stream
  83. //
  84. #ifdef COMPRESS_ON_WIRE
  85. if (CompressedStream) {
  86. FileObjectPtr = &Scb->Header.FileObjectC;
  87. }
  88. #endif
  89. //
  90. // If there is no file object, we create one and initialize
  91. // it.
  92. //
  93. if (*FileObjectPtr == NULL) {
  94. //
  95. // Only acquire the mutex if we don't have the file exclusive.
  96. //
  97. if (!NtfsIsExclusiveScb( Scb )) {
  98. KeWaitForSingleObject( &StreamFileCreationMutex, Executive, KernelMode, FALSE, NULL );
  99. AcquiredMutex = TRUE;
  100. }
  101. try {
  102. //
  103. // Someone could have gotten there first.
  104. //
  105. if (*FileObjectPtr == NULL) {
  106. UnwindStreamFile = IoCreateStreamFileObjectLite( NULL, Scb->Vcb->Vpb->RealDevice);
  107. if (ARGUMENT_PRESENT( StreamName )) {
  108. UnwindStreamFile->FileName.MaximumLength = StreamName->MaximumLength;
  109. UnwindStreamFile->FileName.Length = StreamName->Length;
  110. UnwindStreamFile->FileName.Buffer = StreamName->Buffer;
  111. }
  112. //
  113. // Propagate any flags from the caller's FileObject to our
  114. // stream file that the Cache Manager may look at, so we do not
  115. // miss hints like sequential only or temporary.
  116. //
  117. if (!FlagOn(Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE) &&
  118. (IrpContext->OriginatingIrp != NULL) &&
  119. (CallersFileObject = IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->FileObject)) {
  120. SetFlag( UnwindStreamFile->Flags,
  121. CallersFileObject->Flags & NTFS_FO_PROPAGATE_TO_STREAM );
  122. }
  123. UnwindStreamFile->SectionObjectPointer = &Scb->NonpagedScb->SegmentObject;
  124. //
  125. // For a compressed stream, we have to use separate section
  126. // object pointers.
  127. //
  128. #ifdef COMPRESS_ON_WIRE
  129. if (CompressedStream) {
  130. UnwindStreamFile->SectionObjectPointer = &Scb->NonpagedScb->SegmentObjectC;
  131. }
  132. #endif
  133. //
  134. // If we have created the stream file, we set it to type
  135. // 'StreamFileOpen'
  136. //
  137. NtfsSetFileObject( UnwindStreamFile,
  138. StreamFileOpen,
  139. Scb,
  140. NULL );
  141. if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
  142. SetFlag( UnwindStreamFile->Flags, FO_TEMPORARY_FILE );
  143. }
  144. //
  145. // Initialize the fields of the file object.
  146. //
  147. UnwindStreamFile->ReadAccess = TRUE;
  148. UnwindStreamFile->WriteAccess = TRUE;
  149. UnwindStreamFile->DeleteAccess = TRUE;
  150. //
  151. // Increment the open count and set the section
  152. // object pointers. We don't set the unclean count as the
  153. // cleanup call has already occurred.
  154. //
  155. NtfsIncrementCloseCounts( Scb, TRUE, FALSE );
  156. //
  157. // Increment the cleanup count in this Scb to prevent the
  158. // Scb from going away if the cache call fails.
  159. //
  160. InterlockedIncrement( &Scb->CleanupCount );
  161. DecrementScbCleanup = TRUE;
  162. //
  163. // If the Scb header has not been initialized, we will do so now.
  164. //
  165. if (UpdateScb &&
  166. !FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  167. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  168. }
  169. //
  170. // If this is a compressed stream and the file is not already
  171. // marked as MODIFIED_NO_WRITE then do it now. Use the
  172. // Extended flag field in the Fsrtl header for this. Since this
  173. // is the only place we make this call with FsContext2 == NULL,
  174. // it does not matter how we leave the FsRtl header flag.!
  175. //
  176. NtfsAcquireFsrtlHeader( Scb );
  177. ClearFlag(Scb->Header.Flags2, FSRTL_FLAG2_DO_MODIFIED_WRITE);
  178. if (!FlagOn( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE ) &&
  179. !FlagOn( Scb->Header.Flags2, FSRTL_FLAG2_DO_MODIFIED_WRITE ) &&
  180. !CompressedStream) {
  181. SetFlag(Scb->Header.Flags2, FSRTL_FLAG2_DO_MODIFIED_WRITE);
  182. }
  183. NtfsReleaseFsrtlHeader( Scb );
  184. //
  185. // Check if we need to initialize the cache map for the stream file.
  186. // The size of the section to map will be the current allocation
  187. // for the stream file.
  188. //
  189. if (UnwindStreamFile->PrivateCacheMap == NULL) {
  190. BOOLEAN PinAccess;
  191. CcFileSizes = *(PCC_FILE_SIZES)&Scb->Header.AllocationSize;
  192. //
  193. // If this is a stream with Usa protection, we want to tell
  194. // the Cache Manager we do not need to get any valid data
  195. // callbacks. We do this by having xxMax sitting in
  196. // ValidDataLength for the call, but we have to restore the
  197. // correct value afterwards.
  198. //
  199. // We also do this for all of the stream files created during
  200. // restart. This has the effect of telling Mm to always
  201. // fault the page in from disk. Don't generate a zero page if
  202. // push up the file size during restart.
  203. //
  204. if (FlagOn( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE )) {
  205. CcFileSizes.ValidDataLength.QuadPart = MAXLONGLONG;
  206. }
  207. PinAccess =
  208. (BOOLEAN) (Scb->AttributeTypeCode != $DATA ||
  209. FlagOn(Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE | FCB_STATE_SYSTEM_FILE) ||
  210. FlagOn( Scb->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) ||
  211. CompressedStream);
  212. //
  213. // Bias this for the Usn journal.
  214. //
  215. if (FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) {
  216. CcFileSizes.AllocationSize.QuadPart -= Vcb->UsnCacheBias;
  217. CcFileSizes.FileSize.QuadPart -= Vcb->UsnCacheBias;
  218. }
  219. CcInitializeCacheMap( UnwindStreamFile,
  220. &CcFileSizes,
  221. PinAccess,
  222. &NtfsData.CacheManagerCallbacks,
  223. (PCHAR)Scb + CompressedStream );
  224. UnwindInitializeCacheMap = TRUE;
  225. }
  226. //
  227. // Now call Cc to set the log handle for the file.
  228. //
  229. if (FlagOn( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE ) &&
  230. (Scb != Vcb->LogFileScb)) {
  231. CcSetLogHandleForFile( UnwindStreamFile,
  232. Vcb->LogHandle,
  233. &LfsFlushToLsn );
  234. }
  235. //
  236. // It is now safe to store the stream file in the Scb. We wait
  237. // until now because we don't want an unsafe tester to use the
  238. // file object until the cache is initialized.
  239. //
  240. *FileObjectPtr = UnwindStreamFile;
  241. }
  242. } finally {
  243. DebugUnwind( NtfsCreateInternalAttributeStream );
  244. //
  245. // Undo our work if an error occurred.
  246. //
  247. if (AbnormalTermination()) {
  248. //
  249. // Uninitialize the cache file if we initialized it.
  250. //
  251. if (UnwindInitializeCacheMap) {
  252. CcUninitializeCacheMap( UnwindStreamFile, NULL, NULL );
  253. }
  254. //
  255. // Dereference the stream file if we created it.
  256. //
  257. if (UnwindStreamFile != NULL) {
  258. //
  259. // Clear the internal file name constant
  260. //
  261. NtfsClearInternalFilename( UnwindStreamFile );
  262. ObDereferenceObject( UnwindStreamFile );
  263. }
  264. }
  265. //
  266. // Restore the Scb cleanup count.
  267. //
  268. if (DecrementScbCleanup) {
  269. InterlockedDecrement( &Scb->CleanupCount );
  270. }
  271. if (AcquiredMutex) {
  272. KeReleaseMutant( &StreamFileCreationMutex, IO_NO_INCREMENT, FALSE, FALSE );
  273. }
  274. DebugTrace( -1, Dbg, ("NtfsCreateInternalAttributeStream -> VOID\n") );
  275. }
  276. }
  277. return;
  278. }
  279. BOOLEAN
  280. NtfsDeleteInternalAttributeStream (
  281. IN PSCB Scb,
  282. IN ULONG ForceClose,
  283. IN ULONG CompressedStreamOnly
  284. )
  285. /*++
  286. Routine Description:
  287. This routine is the inverse of NtfsCreateInternalAttributeStream. It
  288. uninitializes the cache map and dereferences the stream file object.
  289. It is coded defensively, in case the stream file object does not exist
  290. or the cache map has not been initialized.
  291. Arguments:
  292. Scb - Supplies the Scb for which the stream file is to be deleted.
  293. ForceClose - Indicates if we to immediately close everything down or
  294. if we are willing to let Mm slowly migrate things out.
  295. CompressedStreamOnly - Indicates if we only want to delete the compressed
  296. stream.
  297. Return Value:
  298. BOOLEAN - TRUE if we dereference a file object, FALSE otherwise.
  299. --*/
  300. {
  301. PFILE_OBJECT FileObject;
  302. #ifdef COMPRESS_ON_WIRE
  303. PFILE_OBJECT FileObjectC;
  304. #endif
  305. BOOLEAN Dereferenced = FALSE;
  306. PAGED_CODE();
  307. //
  308. // We normally already have the paging Io resource. If we do
  309. // not, then it is typically some cleanup path of create or
  310. // whatever. This code assumes that if we cannot get the paging
  311. // Io resource, then there is other activity still going on,
  312. // and it is ok to not delete the stream! For example, it could
  313. // be the lazy writer, who definitely needs the stream.
  314. //
  315. if (
  316. #ifdef COMPRESS_ON_WIRE
  317. ((Scb->FileObject != NULL) || (Scb->Header.FileObjectC != NULL)) &&
  318. #else
  319. (Scb->FileObject != NULL) &&
  320. #endif
  321. ((Scb->Header.PagingIoResource == NULL) ||
  322. ExAcquireResourceExclusiveLite( Scb->Header.PagingIoResource, FALSE ))) {
  323. KeWaitForSingleObject( &StreamFileCreationMutex, Executive, KernelMode, FALSE, NULL );
  324. //
  325. // Capture both file objects and clear the fields so no one else
  326. // can access them.
  327. //
  328. if (CompressedStreamOnly) {
  329. FileObject = NULL;
  330. } else {
  331. FileObject = Scb->FileObject;
  332. Scb->FileObject = NULL;
  333. //
  334. // Clear the internal file name constant
  335. //
  336. NtfsClearInternalFilename( FileObject );
  337. }
  338. #ifdef COMPRESS_ON_WIRE
  339. FileObjectC = Scb->Header.FileObjectC;
  340. Scb->Header.FileObjectC = NULL;
  341. #endif
  342. KeReleaseMutant( &StreamFileCreationMutex, IO_NO_INCREMENT, FALSE, FALSE );
  343. if (Scb->Header.PagingIoResource != NULL) {
  344. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  345. }
  346. //
  347. // Now dereference each file object.
  348. //
  349. if (FileObject != NULL) {
  350. //
  351. // We shouldn't be deleting the internal stream objects of the MFT & co, unless
  352. // we are in the dismounting, restarting or mounting path.
  353. //
  354. ASSERT( (((PSCB) FileObject->FsContext)->Header.NodeTypeCode != NTFS_NTC_SCB_MFT) ||
  355. FlagOn( Scb->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) ||
  356. FlagOn( Scb->Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT ) ||
  357. !FlagOn( Scb->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) ||
  358. Scb->Vcb->RootIndexScb == NULL );
  359. if (FileObject->PrivateCacheMap != NULL) {
  360. CcUninitializeCacheMap( FileObject,
  361. (ForceClose ? &Li0 : NULL),
  362. NULL );
  363. }
  364. ObDereferenceObject( FileObject );
  365. Dereferenced = TRUE;
  366. }
  367. #ifdef COMPRESS_ON_WIRE
  368. if (FileObjectC != NULL) {
  369. if (FileObjectC->PrivateCacheMap != NULL) {
  370. CcUninitializeCacheMap( FileObjectC,
  371. (ForceClose ? &Li0 : NULL),
  372. NULL );
  373. }
  374. //
  375. // For the compressed stream, deallocate the additional
  376. // section object pointers.
  377. //
  378. ObDereferenceObject( FileObjectC );
  379. Dereferenced = TRUE;
  380. }
  381. #endif
  382. }
  383. return Dereferenced;
  384. }
  385. VOID
  386. NtfsMapStream (
  387. IN PIRP_CONTEXT IrpContext,
  388. IN PSCB Scb,
  389. IN LONGLONG FileOffset,
  390. IN ULONG Length,
  391. OUT PVOID *Bcb,
  392. OUT PVOID *Buffer
  393. )
  394. /*++
  395. Routine Description:
  396. This routine is called to map a range of bytes within the stream file
  397. for an Scb. The allowed range to map is bounded by the allocation
  398. size for the Scb. This operation is only valid on a non-resident
  399. Scb.
  400. TEMPCODE - The following need to be resolved for this routine.
  401. - Can the caller specify either an empty range or an invalid range.
  402. In that case we need to able to return the actual length of the
  403. mapped range.
  404. Arguments:
  405. Scb - This is the Scb for the operation.
  406. FileOffset - This is the offset within the Scb where the data is to
  407. be pinned.
  408. Length - This is the number of bytes to pin.
  409. Bcb - Returns a pointer to the Bcb for this range of bytes.
  410. Buffer - Returns a pointer to the range of bytes. We can fault them in
  411. by touching them, but they aren't guaranteed to stay unless
  412. we pin them via the Bcb.
  413. Return Value:
  414. None.
  415. --*/
  416. {
  417. ASSERT_IRP_CONTEXT( IrpContext );
  418. ASSERT_SCB( Scb );
  419. ASSERT( Length != 0 );
  420. PAGED_CODE();
  421. DebugTrace( +1, Dbg, ("NtfsMapStream\n") );
  422. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  423. DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) );
  424. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  425. //
  426. // The file object should already exist in the Scb.
  427. //
  428. ASSERT( Scb->FileObject != NULL );
  429. //
  430. // If we are trying to go beyond the end of the allocation, assume
  431. // we have some corruption.
  432. //
  433. if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) {
  434. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  435. }
  436. //
  437. // Call the cache manager to map the data. This call may raise, but
  438. // will never return an error (including CANT_WAIT).
  439. //
  440. if (!CcMapData( Scb->FileObject,
  441. (PLARGE_INTEGER)&FileOffset,
  442. Length,
  443. TRUE,
  444. Bcb,
  445. Buffer )) {
  446. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  447. }
  448. #ifdef MAPCOUNT_DBG
  449. IrpContext->MapCount++;
  450. #endif
  451. DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) );
  452. DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") );
  453. return;
  454. }
  455. VOID
  456. NtfsPinMappedData (
  457. IN PIRP_CONTEXT IrpContext,
  458. IN PSCB Scb,
  459. IN LONGLONG FileOffset,
  460. IN ULONG Length,
  461. IN OUT PVOID *Bcb
  462. )
  463. /*++
  464. Routine Description:
  465. This routine is called to pin a previously mapped range of bytes
  466. within the stream file for an Scb, for the purpose of subsequently
  467. modifying this byte range. The allowed range to map is
  468. bounded by the allocation size for the Scb. This operation is only
  469. valid on a non-resident Scb.
  470. The data is guaranteed to stay at the same virtual address as previously
  471. returned from NtfsMapStream.
  472. TEMPCODE - The following need to be resolved for this routine.
  473. - Can the caller specify either an empty range or an invalid range.
  474. In that case we need to able to return the actual length of the
  475. mapped range.
  476. Arguments:
  477. Scb - This is the Scb for the operation.
  478. FileOffset - This is the offset within the Scb where the data is to
  479. be pinned.
  480. Length - This is the number of bytes to pin.
  481. Bcb - Returns a pointer to the Bcb for this range of bytes.
  482. Return Value:
  483. None.
  484. --*/
  485. {
  486. ASSERT_IRP_CONTEXT( IrpContext );
  487. ASSERT_SCB( Scb );
  488. ASSERT( Length != 0 );
  489. PAGED_CODE();
  490. DebugTrace( +1, Dbg, ("NtfsPinMappedData\n") );
  491. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  492. DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) );
  493. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  494. //
  495. // The file object should already exist in the Scb.
  496. //
  497. ASSERT( Scb->FileObject != NULL );
  498. //
  499. // If we are trying to go beyond the end of the allocation, assume
  500. // we have some corruption.
  501. //
  502. if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) {
  503. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  504. }
  505. //
  506. // Call the cache manager to map the data. This call may raise, but
  507. // will never return an error (including CANT_WAIT).
  508. //
  509. if (!CcPinMappedData( Scb->FileObject,
  510. (PLARGE_INTEGER)&FileOffset,
  511. Length,
  512. FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  513. Bcb )) {
  514. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  515. }
  516. DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") );
  517. return;
  518. }
  519. VOID
  520. NtfsPinStream (
  521. IN PIRP_CONTEXT IrpContext,
  522. IN PSCB Scb,
  523. IN LONGLONG FileOffset,
  524. IN ULONG Length,
  525. OUT PVOID *Bcb,
  526. OUT PVOID *Buffer
  527. )
  528. /*++
  529. Routine Description:
  530. This routine is called to pin a range of bytes within the stream file
  531. for an Scb. The allowed range to pin is bounded by the allocation
  532. size for the Scb. This operation is only valid on a non-resident
  533. Scb.
  534. TEMPCODE - The following need to be resolved for this routine.
  535. - Can the caller specify either an empty range or an invalid range.
  536. In that case we need to able to return the actual length of the
  537. pinned range.
  538. Arguments:
  539. Scb - This is the Scb for the operation.
  540. FileOffset - This is the offset within the Scb where the data is to
  541. be pinned.
  542. Length - This is the number of bytes to pin.
  543. Bcb - Returns a pointer to the Bcb for this range of bytes.
  544. Buffer - Returns a pointer to the range of bytes pinned in memory.
  545. Return Value:
  546. None.
  547. --*/
  548. {
  549. NTSTATUS OldStatus = IrpContext->ExceptionStatus;
  550. ASSERT_IRP_CONTEXT( IrpContext );
  551. ASSERT_SCB( Scb );
  552. ASSERT( Length != 0 );
  553. PAGED_CODE();
  554. DebugTrace( +1, Dbg, ("NtfsPinStream\n") );
  555. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  556. DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) );
  557. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  558. //
  559. // The file object should already exist in the Scb.
  560. //
  561. ASSERT( Scb->FileObject != NULL );
  562. //
  563. // If we are trying to go beyond the end of the allocation, assume
  564. // we have some corruption.
  565. //
  566. if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) {
  567. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  568. }
  569. //
  570. // Call the cache manager to map the data. This call may raise, or
  571. // will return FALSE if waiting is required.
  572. //
  573. if (FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) {
  574. FileOffset -= Scb->Vcb->UsnCacheBias;
  575. }
  576. if (!CcPinRead( Scb->FileObject,
  577. (PLARGE_INTEGER)&FileOffset,
  578. Length,
  579. FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  580. Bcb,
  581. Buffer )) {
  582. ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ));
  583. //
  584. // Could not pin the data without waiting (cache miss).
  585. //
  586. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  587. }
  588. //
  589. // We don't want to propagate wether or not we hit eof. Its assumed the code pinning is
  590. // already filesize synchronized
  591. //
  592. if (IrpContext->ExceptionStatus == STATUS_END_OF_FILE) {
  593. IrpContext->ExceptionStatus = OldStatus;
  594. }
  595. #ifdef MAPCOUNT_DBG
  596. IrpContext->MapCount++;
  597. #endif
  598. DebugTrace( 0, Dbg, ("Bcb -> %08lx\n", *Bcb) );
  599. DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) );
  600. DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") );
  601. return;
  602. }
  603. VOID
  604. NtfsPreparePinWriteStream (
  605. IN PIRP_CONTEXT IrpContext,
  606. IN PSCB Scb,
  607. IN LONGLONG FileOffset,
  608. IN ULONG Length,
  609. IN BOOLEAN Zero,
  610. OUT PVOID *Bcb,
  611. OUT PVOID *Buffer
  612. )
  613. /*++
  614. Routine Description:
  615. Arguments:
  616. Return Value:
  617. --*/
  618. {
  619. ASSERT_IRP_CONTEXT( IrpContext );
  620. ASSERT_SCB( Scb );
  621. PAGED_CODE();
  622. DebugTrace( +1, Dbg, ("NtfsPreparePinWriteStream\n") );
  623. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  624. DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) );
  625. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  626. //
  627. // The file object should already exist in the Scb.
  628. //
  629. ASSERT( Scb->FileObject != NULL );
  630. //
  631. // If we are trying to go beyond the end of the allocation, assume
  632. // we have some corruption.
  633. //
  634. if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) {
  635. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  636. }
  637. //
  638. // Call the cache manager to do it. This call may raise, or
  639. // will return FALSE if waiting is required.
  640. //
  641. if (!CcPreparePinWrite( Scb->FileObject,
  642. (PLARGE_INTEGER)&FileOffset,
  643. Length,
  644. Zero,
  645. FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  646. Bcb,
  647. Buffer )) {
  648. ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ));
  649. //
  650. // Could not pin the data without waiting (cache miss).
  651. //
  652. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  653. }
  654. #ifdef MAPCOUNT_DBG
  655. IrpContext->MapCount++;
  656. #endif
  657. DebugTrace( 0, Dbg, ("Bcb -> %08lx\n", *Bcb) );
  658. DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) );
  659. DebugTrace( -1, Dbg, ("NtfsPreparePinWriteStream -> VOID\n") );
  660. return;
  661. }
  662. NTSTATUS
  663. NtfsCompleteMdl (
  664. IN PIRP_CONTEXT IrpContext,
  665. IN PIRP Irp
  666. )
  667. /*++
  668. Routine Description:
  669. This routine performs the function of completing Mdl read and write
  670. requests. It should be called only from NtfsFsdRead and NtfsFsdWrite.
  671. Arguments:
  672. Irp - Supplies the originating Irp.
  673. Return Value:
  674. NTSTATUS - Will always be STATUS_PENDING or STATUS_SUCCESS.
  675. --*/
  676. {
  677. PFILE_OBJECT FileObject;
  678. PIO_STACK_LOCATION IrpSp;
  679. PNTFS_ADVANCED_FCB_HEADER Header;
  680. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  681. PAGED_CODE();
  682. DebugTrace( +1, Dbg, ("NtfsCompleteMdl\n") );
  683. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  684. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  685. //
  686. // Do completion processing.
  687. //
  688. FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject;
  689. switch( IrpContext->MajorFunction ) {
  690. case IRP_MJ_READ:
  691. CcMdlReadComplete( FileObject, Irp->MdlAddress );
  692. break;
  693. case IRP_MJ_WRITE:
  694. try {
  695. PSCB Scb;
  696. VBO StartingVbo;
  697. LONGLONG ByteCount;
  698. LONGLONG ByteRange;
  699. BOOLEAN DoingIoAtEof = FALSE;
  700. ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ));
  701. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  702. Scb = (PSCB)(IrpSp->FileObject->FsContext);
  703. Header = &(Scb->Header);
  704. //
  705. // Now synchronize with the FsRtl Header and Scb.
  706. //
  707. if (Header->PagingIoResource != NULL) {
  708. StartingVbo = IrpSp->Parameters.Write.ByteOffset.QuadPart;
  709. ByteCount = (LONGLONG) IrpSp->Parameters.Write.Length;
  710. ByteRange = StartingVbo + ByteCount + PAGE_SIZE - 1;
  711. ClearFlag( ((ULONG) ByteRange), PAGE_SIZE - 1 );
  712. ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
  713. NtfsAcquireFsrtlHeader( Scb );
  714. //
  715. // Now see if this is at EOF.
  716. // Recursive flush will generate IO which ends on page boundary
  717. // which is why we rounded the range
  718. //
  719. if (ByteRange > Header->ValidDataLength.QuadPart) {
  720. //
  721. // Mark that we are writing to EOF. If someone else is currently
  722. // writing to EOF, wait for them.
  723. //
  724. ASSERT( ByteRange - StartingVbo < MAXULONG );
  725. DoingIoAtEof = !FlagOn( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE ) ||
  726. NtfsWaitForIoAtEof( Header, (PLARGE_INTEGER)&StartingVbo, (ULONG)(ByteRange - StartingVbo) );
  727. if (DoingIoAtEof) {
  728. SetFlag( Header->Flags, FSRTL_FLAG_EOF_ADVANCE_ACTIVE );
  729. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  730. ((PSCB) Header)->IoAtEofThread = (PERESOURCE_THREAD) ExGetCurrentResourceThread();
  731. #endif
  732. //
  733. // Store this in the IrpContext until commit or post.
  734. //
  735. IrpContext->CleanupStructure = Scb;
  736. }
  737. }
  738. NtfsReleaseFsrtlHeader( Scb );
  739. }
  740. CcMdlWriteComplete( FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress );
  741. } finally {
  742. if (Header->PagingIoResource != NULL) {
  743. ExReleaseResourceLite( Header->PagingIoResource );
  744. }
  745. }
  746. break;
  747. default:
  748. DebugTrace( DEBUG_TRACE_ERROR, 0, ("Illegal Mdl Complete.\n") );
  749. ASSERTMSG("Illegal Mdl Complete, About to bugcheck ", FALSE);
  750. NtfsBugCheck( IrpContext->MajorFunction, 0, 0 );
  751. }
  752. //
  753. // Mdl is now deallocated.
  754. //
  755. Irp->MdlAddress = NULL;
  756. //
  757. // Ignore errors. CC has already cleaned up his structures.
  758. //
  759. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  760. NtfsMinimumExceptionProcessing( IrpContext );
  761. //
  762. // Complete the request and exit right away.
  763. //
  764. NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
  765. DebugTrace( -1, Dbg, ("NtfsCompleteMdl -> STATUS_SUCCESS\n") );
  766. return STATUS_SUCCESS;
  767. }
  768. BOOLEAN
  769. NtfsZeroData (
  770. IN PIRP_CONTEXT IrpContext,
  771. IN PSCB Scb,
  772. IN PFILE_OBJECT FileObject,
  773. IN LONGLONG StartingZero,
  774. IN LONGLONG ByteCount,
  775. IN OUT PLONGLONG CommittedFileSize OPTIONAL
  776. )
  777. /*++
  778. Routine Description:
  779. This routine is called to zero a range of a file in order to
  780. advance valid data length.
  781. Arguments:
  782. Scb - Scb for the stream to zero.
  783. FileObject - FileObject for the stream.
  784. StartingZero - Offset to begin the zero operation.
  785. ByteCount - Length of range to zero.
  786. CommittedFileSize - If we write the file sizes and commit the
  787. transaction then we want to let our caller know what
  788. point to roll back file size on a subsequent failure. On entry
  789. it has the size our caller wants to roll back the file size to.
  790. On exit it has the new size to roll back to which takes into
  791. account any updates to the file size which have been logged.
  792. Return Value:
  793. BOOLEAN - TRUE if the entire range was zeroed, FALSE if the request
  794. is broken up or the cache manager would block.
  795. --*/
  796. {
  797. LONGLONG Temp;
  798. #ifdef COMPRESS_ON_WIRE
  799. IO_STATUS_BLOCK IoStatus;
  800. #endif
  801. ULONG SectorSize;
  802. BOOLEAN Finished;
  803. BOOLEAN CompleteZero = TRUE;
  804. BOOLEAN ScbAcquired = FALSE;
  805. PVCB Vcb = Scb->Vcb;
  806. LONGLONG ZeroStart;
  807. LONGLONG BeyondZeroEnd;
  808. ULONG CompressionUnit = Scb->CompressionUnit;
  809. BOOLEAN Wait;
  810. PAGED_CODE();
  811. Wait = (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  812. //
  813. // We don't expect to ever be explicitly zeroing system files
  814. //
  815. ASSERT( !FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE ) );
  816. SectorSize = Vcb->BytesPerSector;
  817. //
  818. // We may be able to simplify the zero operation (sparse file or when writing
  819. // compressed) by deallocating large ranges of the file. Otherwise we have to
  820. // generate zeroes for the entire range. If that is the case we want to split
  821. // this operation up.
  822. //
  823. if ((ByteCount > MAX_ZERO_THRESHOLD) &&
  824. !FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED ) &&
  825. !FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  826. ByteCount = MAX_ZERO_THRESHOLD;
  827. CompleteZero = FALSE;
  828. }
  829. ZeroStart = BlockAlign( StartingZero, (LONG)SectorSize );
  830. BeyondZeroEnd = BlockAlign( StartingZero + ByteCount, (LONG)SectorSize );
  831. ASSERT( BeyondZeroEnd >= (StartingZero + ByteCount) );
  832. //
  833. // Directly zero from startingzero to zerostart on disk for vanilla nonresident
  834. // files. Compressed files always write out compression units worth of data
  835. // which would cover this range. Resident files are always changed in
  836. // NtfsChangeAttributeValue which also also zeroes any gaps
  837. //
  838. if ((CompressionUnit == 0) &&
  839. (ZeroStart != StartingZero) &&
  840. (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT ))) {
  841. //
  842. // Writing directly to disk is always safe - we can go through the
  843. // cachemap if the file is cached and its not mapped. If its mapped
  844. // there may be data between vdl and fs we don't know about yet
  845. // We also must go non cached if we're not the top level request to avoid
  846. // a recursive flush if mm is initiating the initial write via the deref seg thread or
  847. // if an initial cache coherency flush caused this
  848. //
  849. BOOLEAN CachedWrite = NtfsIsTopLevelRequest( IrpContext ) && (FileObject->PrivateCacheMap != NULL) && !FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE );
  850. NtfsZeroEndOfSector( IrpContext, IrpContext->OriginatingIrp, Scb, StartingZero, CachedWrite );
  851. #ifdef SYSCACHE_DEBUG
  852. if (ScbIsBeingLogged( Scb )) {
  853. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_HEAD_SECTOR, CachedWrite, StartingZero, ZeroStart, Scb->Header.ValidDataLength.QuadPart );
  854. }
  855. #endif
  856. }
  857. //
  858. // We must flush the first compression unit in case it is partially populated
  859. // in the compressed stream.
  860. //
  861. #ifdef COMPRESS_ON_WIRE
  862. if ((Scb->NonpagedScb->SegmentObjectC.DataSectionObject != NULL) &&
  863. ((StartingZero & (CompressionUnit - 1)) != 0)) {
  864. StartingZero = BlockAlignTruncate( StartingZero, (LONG)CompressionUnit );
  865. CcFlushCache( &Scb->NonpagedScb->SegmentObjectC,
  866. (PLARGE_INTEGER)&StartingZero,
  867. CompressionUnit,
  868. &IoStatus );
  869. if (!NT_SUCCESS(IoStatus.Status)) {
  870. NtfsNormalizeAndRaiseStatus( IrpContext, IoStatus.Status, STATUS_UNEXPECTED_IO_ERROR );
  871. }
  872. }
  873. #endif
  874. //
  875. // If this is a sparse or compressed file and we are zeroing a lot, then let's
  876. // just delete the space instead of writing tons of zeros and deleting
  877. // the space in the noncached path! If we are currently decompressing
  878. // a compressed file we can't take this path.
  879. //
  880. if ((FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED ) ||
  881. FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) &&
  882. (ByteCount > (Scb->CompressionUnit * 2))) {
  883. //
  884. // Find the end of the first compression unit being zeroed.
  885. //
  886. Temp = BlockAlign( ZeroStart, (LONG)CompressionUnit );
  887. //
  888. // Zero the first compression unit.
  889. //
  890. if ((ULONG)Temp != (ULONG)ZeroStart) {
  891. Finished = CcZeroData( FileObject, (PLARGE_INTEGER)&ZeroStart, (PLARGE_INTEGER)&Temp, Wait );
  892. #ifdef SYSCACHE_DEBUG
  893. if (ScbIsBeingLogged( Scb )) {
  894. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_HEAD_COMPRESSED, 0, ZeroStart, Temp, Finished );
  895. }
  896. #endif
  897. if (!Finished) {return FALSE;}
  898. ZeroStart = Temp;
  899. }
  900. //
  901. // Now delete all of the compression units in between.
  902. //
  903. //
  904. // Calculate the start of the last compression unit in bytes.
  905. //
  906. Temp = BeyondZeroEnd;
  907. (ULONG)Temp &= ~(CompressionUnit - 1);
  908. //
  909. // If the caller has not already started a transaction (like write.c),
  910. // then let's just do the delete as an atomic action.
  911. //
  912. if (!NtfsIsExclusiveScb( Scb )) {
  913. NtfsAcquireExclusiveScb( IrpContext, Scb );
  914. ScbAcquired = TRUE;
  915. if (ARGUMENT_PRESENT( CommittedFileSize )) {
  916. NtfsMungeScbSnapshot( IrpContext, Scb, *CommittedFileSize );
  917. }
  918. }
  919. try {
  920. //
  921. // Delete the space.
  922. //
  923. NtfsDeleteAllocation( IrpContext,
  924. FileObject,
  925. Scb,
  926. LlClustersFromBytes( Vcb, ZeroStart ),
  927. LlClustersFromBytesTruncate( Vcb, Temp ) - 1,
  928. TRUE,
  929. TRUE );
  930. //
  931. // If we didn't raise then update the Scb values for compressed files.
  932. //
  933. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  934. Scb->ValidDataToDisk = Temp;
  935. }
  936. //
  937. // If we succeed, commit the atomic action. Release all of the exclusive
  938. // resources if our user explicitly acquired the Fcb here.
  939. //
  940. if (ScbAcquired) {
  941. NtfsCheckpointCurrentTransaction( IrpContext );
  942. if (ARGUMENT_PRESENT( CommittedFileSize )) {
  943. ASSERT( Scb->ScbSnapshot != NULL );
  944. *CommittedFileSize = Scb->ScbSnapshot->FileSize;
  945. }
  946. while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
  947. NtfsReleaseFcb( IrpContext,
  948. (PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
  949. FCB,
  950. ExclusiveFcbLinks ));
  951. }
  952. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
  953. IRP_CONTEXT_FLAG_RELEASE_MFT );
  954. ScbAcquired = FALSE;
  955. }
  956. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  957. Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  958. SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  959. }
  960. } finally {
  961. if (ScbAcquired) {
  962. NtfsReleaseScb( IrpContext, Scb );
  963. }
  964. }
  965. //
  966. // Zero the beginning of the last compression unit.
  967. //
  968. if ((ULONG)Temp != (ULONG)BeyondZeroEnd) {
  969. Finished = CcZeroData( FileObject, (PLARGE_INTEGER)&Temp, (PLARGE_INTEGER)&BeyondZeroEnd, Wait );
  970. #ifdef SYSCACHE_DEBUG
  971. if (ScbIsBeingLogged( Scb )) {
  972. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_TAIL_COMPRESSED, 0, Temp, BeyondZeroEnd, Finished );
  973. }
  974. #endif
  975. if (!Finished) {return FALSE;}
  976. BeyondZeroEnd = Temp;
  977. }
  978. return TRUE;
  979. }
  980. //
  981. // If we were called to just zero part of a sector we are in trouble.
  982. //
  983. if (ZeroStart == BeyondZeroEnd) {
  984. return TRUE;
  985. }
  986. Finished = CcZeroData( FileObject,
  987. (PLARGE_INTEGER)&ZeroStart,
  988. (PLARGE_INTEGER)&BeyondZeroEnd,
  989. Wait );
  990. //
  991. // If we are breaking this request up then commit the current
  992. // transaction (including updating the valid data length in
  993. // in the Scb) and return FALSE.
  994. //
  995. if (Finished && !CompleteZero) {
  996. //
  997. // Synchronize the valid data length change using the mutex.
  998. //
  999. ExAcquireFastMutex( Scb->Header.FastMutex );
  1000. Scb->Header.ValidDataLength.QuadPart = BeyondZeroEnd;
  1001. //
  1002. // Move the rollback point up to include the range of zeroed
  1003. // data.
  1004. //
  1005. if (ARGUMENT_PRESENT( CommittedFileSize )) {
  1006. if (BeyondZeroEnd > *CommittedFileSize) {
  1007. *CommittedFileSize = BeyondZeroEnd;
  1008. }
  1009. }
  1010. ASSERT( Scb->Header.ValidDataLength.QuadPart <= Scb->Header.FileSize.QuadPart );
  1011. ExReleaseFastMutex( Scb->Header.FastMutex );
  1012. NtfsCheckpointCurrentTransaction( IrpContext );
  1013. return FALSE;
  1014. }
  1015. return Finished;
  1016. }