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

1858 lines
52 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. VAttrSup.c
  5. Abstract:
  6. This module implements the attribute routines for NtOfs
  7. Author:
  8. Tom Miller [TomM] 10-Apr-1996
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. //
  13. // Define a tag for general pool allocations from this module
  14. //
  15. #undef MODULE_POOL_TAG
  16. #define MODULE_POOL_TAG ('vFtN')
  17. #undef NtOfsMapAttribute
  18. NTFSAPI
  19. VOID
  20. NtOfsMapAttribute (
  21. IN PIRP_CONTEXT IrpContext,
  22. IN PSCB Scb,
  23. IN LONGLONG Offset,
  24. IN ULONG Length,
  25. OUT PVOID *Buffer,
  26. OUT PMAP_HANDLE MapHandle
  27. );
  28. #undef NtOfsPreparePinWrite
  29. NTFSAPI
  30. VOID
  31. NtOfsPreparePinWrite (
  32. IN PIRP_CONTEXT IrpContext,
  33. IN PSCB Scb,
  34. IN LONGLONG Offset,
  35. IN ULONG Length,
  36. OUT PVOID *Buffer,
  37. OUT PMAP_HANDLE MapHandle
  38. );
  39. #undef NtOfsPinRead
  40. NTFSAPI
  41. VOID
  42. NtOfsPinRead(
  43. IN PIRP_CONTEXT IrpContext,
  44. IN PSCB Scb,
  45. IN LONGLONG Offset,
  46. IN ULONG Length,
  47. OUT PMAP_HANDLE MapHandle
  48. );
  49. #undef NtOfsReleaseMap
  50. NTFSAPI
  51. VOID
  52. NtOfsReleaseMap (
  53. IN PIRP_CONTEXT IrpContext,
  54. IN PMAP_HANDLE MapHandle
  55. );
  56. #ifdef ALLOC_PRAGMA
  57. #pragma alloc_text(PAGE, NtOfsCreateAttribute)
  58. #pragma alloc_text(PAGE, NtOfsCreateAttributeEx)
  59. #pragma alloc_text(PAGE, NtOfsCloseAttribute)
  60. #pragma alloc_text(PAGE, NtOfsDeleteAttribute)
  61. #pragma alloc_text(PAGE, NtOfsQueryLength)
  62. #pragma alloc_text(PAGE, NtOfsSetLength)
  63. #pragma alloc_text(PAGE, NtfsHoldIrpForNewLength)
  64. #pragma alloc_text(PAGE, NtOfsPostNewLength)
  65. #pragma alloc_text(PAGE, NtOfsFlushAttribute)
  66. #pragma alloc_text(PAGE, NtOfsPutData)
  67. #pragma alloc_text(PAGE, NtOfsMapAttribute)
  68. #pragma alloc_text(PAGE, NtOfsReleaseMap)
  69. #endif
  70. NTFSAPI
  71. NTSTATUS
  72. NtOfsCreateAttribute (
  73. IN PIRP_CONTEXT IrpContext,
  74. IN PFCB Fcb,
  75. IN UNICODE_STRING Name,
  76. IN CREATE_OPTIONS CreateOptions,
  77. IN ULONG LogNonresidentToo,
  78. OUT PSCB *ReturnScb
  79. )
  80. /*++
  81. Routine Description:
  82. This routine may be called to create / open a named data attribute
  83. within a given file, which may or may not be recoverable.
  84. Arguments:
  85. Fcb - File in which the attribute is to be created. It is acquired exclusive
  86. Name - Name of the attribute for all related Scbs and attributes on disk.
  87. CreateOptions - Standard create flags.
  88. LogNonresidentToo - Supplies nonzero if updates to the attribute should
  89. be logged.
  90. ReturnScb - Returns an Scb as handle for the attribute.
  91. Return Value:
  92. STATUS_OBJECT_NAME_COLLISION -- if CreateNew and attribute already exists
  93. STATUS_OBJECT_NAME_NOT_FOUND -- if OpenExisting and attribute does not exist
  94. --*/
  95. {
  96. return NtOfsCreateAttributeEx( IrpContext,
  97. Fcb,
  98. Name,
  99. $DATA,
  100. CreateOptions,
  101. LogNonresidentToo,
  102. ReturnScb );
  103. }
  104. NTFSAPI
  105. NTSTATUS
  106. NtOfsCreateAttributeEx (
  107. IN PIRP_CONTEXT IrpContext,
  108. IN PFCB Fcb,
  109. IN UNICODE_STRING Name,
  110. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  111. IN CREATE_OPTIONS CreateOptions,
  112. IN ULONG LogNonresidentToo,
  113. OUT PSCB *ReturnScb
  114. )
  115. /*++
  116. Routine Description:
  117. This routine may be called to create / open a named data attribute
  118. within a given file, which may or may not be recoverable.
  119. Arguments:
  120. Fcb - File in which the attribute is to be created. It is acquired exclusive
  121. Name - Name of the attribute for all related Scbs and attributes on disk.
  122. CreateOptions - Standard create flags.
  123. LogNonresidentToo - Supplies nonzero if updates to the attribute should
  124. be logged.
  125. ReturnScb - Returns an Scb as handle for the attribute.
  126. Return Value:
  127. STATUS_OBJECT_NAME_COLLISION -- if CreateNew and attribute already exists
  128. STATUS_OBJECT_NAME_NOT_FOUND -- if OpenExisting and attribute does not exist
  129. --*/
  130. {
  131. ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
  132. BOOLEAN FoundAttribute;
  133. NTSTATUS Status = STATUS_SUCCESS;
  134. PSCB Scb = NULL;
  135. ASSERT_IRP_CONTEXT( IrpContext );
  136. ASSERT( NtfsIsExclusiveFcb( Fcb ));
  137. PAGED_CODE();
  138. if (AttributeTypeCode != $DATA &&
  139. AttributeTypeCode != $LOGGED_UTILITY_STREAM) {
  140. ASSERTMSG( "Invalid attribute type code in NtOfsCreateAttributeEx", FALSE );
  141. *ReturnScb = NULL;
  142. return STATUS_INVALID_PARAMETER;
  143. }
  144. //
  145. // Now, just create the Data Attribute.
  146. //
  147. NtfsInitializeAttributeContext( &LocalContext );
  148. try {
  149. //
  150. // First see if the attribute already exists, by searching for the root
  151. // attribute.
  152. //
  153. FoundAttribute = NtfsLookupAttributeByName( IrpContext,
  154. Fcb,
  155. &Fcb->FileReference,
  156. AttributeTypeCode,
  157. &Name,
  158. NULL,
  159. TRUE,
  160. &LocalContext );
  161. //
  162. // If it is not there, and the CreateOptions allow, then let's create
  163. // the attribute root now. (First cleaning up the attribute context from
  164. // the lookup).
  165. //
  166. if (!FoundAttribute && (CreateOptions <= CREATE_OR_OPEN)) {
  167. //
  168. // Make sure we acquire the quota resource before creating the stream. Just
  169. // in case we need the Mft during the create.
  170. //
  171. if (NtfsIsTypeCodeSubjectToQuota( AttributeTypeCode ) &&
  172. NtfsPerformQuotaOperation( Fcb )) {
  173. //
  174. // The quota index must be acquired before the mft scb is acquired.
  175. //
  176. ASSERT( !NtfsIsExclusiveScb( Fcb->Vcb->MftScb ) ||
  177. ExIsResourceAcquiredSharedLite( Fcb->Vcb->QuotaTableScb->Fcb->Resource ));
  178. NtfsAcquireQuotaControl( IrpContext, Fcb->QuotaControl );
  179. }
  180. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  181. NtfsCreateAttributeWithValue( IrpContext,
  182. Fcb,
  183. AttributeTypeCode,
  184. &Name,
  185. NULL,
  186. 0,
  187. 0,
  188. NULL,
  189. TRUE,
  190. &LocalContext );
  191. //
  192. // If the attribute is already there, and we were asked to create it, then
  193. // return an error.
  194. //
  195. } else if (FoundAttribute && (CreateOptions == CREATE_NEW)) {
  196. Status = STATUS_OBJECT_NAME_COLLISION;
  197. leave;
  198. //
  199. // If the attribute is not there, and we were supposed to open existing, then
  200. // return an error.
  201. //
  202. } else if (!FoundAttribute) {
  203. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  204. leave;
  205. }
  206. //
  207. // Otherwise create/find the Scb and reference it.
  208. //
  209. Scb = NtfsCreateScb( IrpContext, Fcb, AttributeTypeCode, &Name, FALSE, &FoundAttribute );
  210. //
  211. // Make sure things are correctly reference counted
  212. //
  213. NtfsIncrementCloseCounts( Scb, TRUE, FALSE );
  214. //
  215. // If we created the Scb, then get the no modified write set correctly.
  216. //
  217. ASSERT( !FoundAttribute ||
  218. (LogNonresidentToo == BooleanFlagOn(Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE)) );
  219. if (!FoundAttribute && LogNonresidentToo) {
  220. SetFlag( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE );
  221. Scb->Header.ValidDataLength.QuadPart = MAXLONGLONG;
  222. }
  223. //
  224. // Make sure the stream can be mapped internally. Defer this for the Usn journal
  225. // until we set up the journal bias.
  226. //
  227. if ((Scb->FileObject == NULL) && !FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) {
  228. NtfsCreateInternalAttributeStream( IrpContext, Scb, TRUE, NULL );
  229. }
  230. NtfsUpdateScbFromAttribute( IrpContext, Scb, NtfsFoundAttribute(&LocalContext) );
  231. } finally {
  232. if (AbnormalTermination( )) {
  233. if (Scb != NULL) {
  234. NtOfsCloseAttribute( IrpContext, Scb );
  235. }
  236. }
  237. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  238. }
  239. *ReturnScb = Scb;
  240. return Status;
  241. }
  242. NTFSAPI
  243. VOID
  244. NtOfsCloseAttribute (
  245. IN PIRP_CONTEXT IrpContext,
  246. IN PSCB Scb
  247. )
  248. /*++
  249. Routine Description:
  250. This routine may be called to close a previously returned handle on an attribute.
  251. Arguments:
  252. Scb - Supplies an Scb as the previously returned handle for this attribute.
  253. Return Value:
  254. None.
  255. --*/
  256. {
  257. ASSERT( NtfsIsExclusiveFcb( Scb->Fcb ));
  258. PAGED_CODE();
  259. //
  260. // We either need the caller to empty this list before closing (as assumed here),
  261. // or possibly empty it here. At this point it seems better to assume that the
  262. // caller must take action to insure any waiting threads will shutdown and not
  263. // touch the stream anymore, then call NtOfsPostNewLength to flush the queue.
  264. // If the queue is nonempty here, maybe the caller didn't think this through!
  265. //
  266. ASSERT( IsListEmpty( &Scb->ScbType.Data.WaitForNewLength ) ||
  267. (Scb->CloseCount > 1) );
  268. NtfsDecrementCloseCounts( IrpContext, Scb, NULL, TRUE, FALSE, TRUE );
  269. }
  270. NTFSAPI
  271. VOID
  272. NtOfsDeleteAttribute (
  273. IN PIRP_CONTEXT IrpContext,
  274. IN PFCB Fcb,
  275. IN PSCB Scb
  276. )
  277. /*++
  278. Routine Description:
  279. This routine may be called to delete an attribute with type code
  280. $LOGGED_UTILITY_STREAM.
  281. Arguments:
  282. Fcb - Supplies an Fcb as the previously returned object handle for the file
  283. Scb - Supplies an Scb as the previously returned handle for this attribute.
  284. Return Value:
  285. None (Deleting a nonexistant index is benign).
  286. --*/
  287. {
  288. ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
  289. BOOLEAN FoundAttribute;
  290. ASSERT_IRP_CONTEXT( IrpContext );
  291. PAGED_CODE();
  292. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  293. try {
  294. //
  295. // Make sure we aren't deleting a data stream. We do this after
  296. // initializing the attribute context to make the finally clause simpler.
  297. // This test can be removed if some trusted component using the NtOfs
  298. // API has a legitimate need to delete other types of attributes.
  299. //
  300. NtfsInitializeAttributeContext( &LocalContext );
  301. if (Scb->AttributeTypeCode != $LOGGED_UTILITY_STREAM) {
  302. leave;
  303. }
  304. //
  305. // First see if there is some attribute allocation, and if so truncate it
  306. // away allowing this operation to be broken up.
  307. //
  308. if (NtfsLookupAttributeByName( IrpContext,
  309. Fcb,
  310. &Fcb->FileReference,
  311. Scb->AttributeTypeCode,
  312. &Scb->AttributeName,
  313. NULL,
  314. FALSE,
  315. &LocalContext )
  316. &&
  317. !NtfsIsAttributeResident( NtfsFoundAttribute( &LocalContext ))) {
  318. ASSERT( Scb->FileObject != NULL );
  319. NtfsDeleteAllocation( IrpContext, NULL, Scb, 0, MAXLONGLONG, TRUE, TRUE );
  320. }
  321. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  322. //
  323. // Initialize the attribute context on each trip through the loop.
  324. //
  325. NtfsInitializeAttributeContext( &LocalContext );
  326. //
  327. // Now there should be a single attribute record, so look it up and delete it.
  328. //
  329. FoundAttribute = NtfsLookupAttributeByName( IrpContext,
  330. Fcb,
  331. &Fcb->FileReference,
  332. Scb->AttributeTypeCode,
  333. &Scb->AttributeName,
  334. NULL,
  335. TRUE,
  336. &LocalContext );
  337. //
  338. // If this stream is subject to quota, make sure the quota has been enlarged.
  339. //
  340. NtfsDeleteAttributeRecord( IrpContext,
  341. Fcb,
  342. (DELETE_LOG_OPERATION |
  343. DELETE_RELEASE_FILE_RECORD |
  344. DELETE_RELEASE_ALLOCATION),
  345. &LocalContext );
  346. SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  347. } finally {
  348. NtfsReleaseFcb( IrpContext, Fcb );
  349. NtfsCleanupAttributeContext( IrpContext, &LocalContext );
  350. }
  351. return;
  352. }
  353. NTFSAPI
  354. LONGLONG
  355. NtOfsQueryLength (
  356. IN PSCB Scb
  357. )
  358. /*++
  359. Routine Description:
  360. This routine may be called to query the Length (FileSize) of an attribute.
  361. Arguments:
  362. Scb - Supplies an Scb as the previously returned handle for this attribute.
  363. Length - Returns the current Length of the attribute.
  364. Return Value:
  365. None (Deleting a nonexistant index is benign).
  366. --*/
  367. {
  368. LONGLONG Length;
  369. PAGED_CODE();
  370. ExAcquireFastMutex( Scb->Header.FastMutex );
  371. Length = Scb->Header.FileSize.QuadPart;
  372. ExReleaseFastMutex( Scb->Header.FastMutex );
  373. return Length;
  374. }
  375. NTFSAPI
  376. VOID
  377. NtOfsSetLength (
  378. IN PIRP_CONTEXT IrpContext,
  379. IN PSCB Scb,
  380. IN LONGLONG Length
  381. )
  382. /*++
  383. Routine Description:
  384. This routine may be called to set the Length (FileSize) of an attribute.
  385. Arguments:
  386. Scb - Supplies an Scb as the previously returned handle for this attribute.
  387. Length - Supplies the new Length for the attribute.
  388. Return Value:
  389. None (Deleting a nonexistant index is benign).
  390. --*/
  391. {
  392. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  393. PFILE_OBJECT FileObject = Scb->FileObject;
  394. PFCB Fcb = Scb->Fcb;
  395. PVCB Vcb = Scb->Vcb;
  396. BOOLEAN DoingIoAtEof = FALSE;
  397. BOOLEAN Truncating = FALSE;
  398. BOOLEAN CleanupAttrContext = FALSE;
  399. ASSERT_IRP_CONTEXT( IrpContext );
  400. ASSERT_SCB( Scb );
  401. ASSERT( NtfsIsExclusiveScb( Scb ));
  402. ASSERT(FileObject != NULL);
  403. PAGED_CODE();
  404. try {
  405. //
  406. // If this is a resident attribute we will try to keep it resident.
  407. //
  408. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  409. //
  410. // If the new file size is larger than a file record then convert
  411. // to non-resident and use the non-resident code below. Otherwise
  412. // call ChangeAttributeValue which may also convert to nonresident.
  413. //
  414. NtfsInitializeAttributeContext( &AttrContext );
  415. CleanupAttrContext = TRUE;
  416. NtfsLookupAttributeForScb( IrpContext,
  417. Scb,
  418. NULL,
  419. &AttrContext );
  420. //
  421. // Either convert or change the attribute value.
  422. //
  423. if (Length >= Scb->Vcb->BytesPerFileRecordSegment) {
  424. NtfsConvertToNonresident( IrpContext,
  425. Fcb,
  426. NtfsFoundAttribute( &AttrContext ),
  427. FALSE,
  428. &AttrContext );
  429. } else {
  430. ULONG AttributeOffset;
  431. //
  432. // We are sometimes called by MM during a create section, so
  433. // for right now the best way we have of detecting a create
  434. // section is whether or not the requestor mode is kernel.
  435. //
  436. if ((ULONG)Length > Scb->Header.FileSize.LowPart) {
  437. AttributeOffset = Scb->Header.FileSize.LowPart;
  438. } else {
  439. AttributeOffset = (ULONG) Length;
  440. }
  441. //
  442. // ****TEMP Ideally we would do this simple case by hand.
  443. //
  444. NtfsChangeAttributeValue( IrpContext,
  445. Fcb,
  446. AttributeOffset,
  447. NULL,
  448. (ULONG)Length - AttributeOffset,
  449. TRUE,
  450. FALSE,
  451. FALSE,
  452. FALSE,
  453. &AttrContext );
  454. ExAcquireFastMutex( Scb->Header.FastMutex );
  455. Scb->Header.FileSize.QuadPart = Length;
  456. //
  457. // If the file went non-resident, then the allocation size in
  458. // the Scb is correct. Otherwise we quad-align the new file size.
  459. //
  460. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  461. Scb->Header.AllocationSize.LowPart = QuadAlign( Scb->Header.FileSize.LowPart );
  462. if (Scb->Header.ValidDataLength.QuadPart != MAXLONGLONG) {
  463. Scb->Header.ValidDataLength.QuadPart = Length;
  464. }
  465. Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
  466. } else {
  467. SetFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  468. }
  469. ExReleaseFastMutex( Scb->Header.FastMutex );
  470. //
  471. // Now update Cc.
  472. //
  473. CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
  474. //
  475. // ****TEMP**** This hack is awaiting our actually doing this change
  476. // in CcSetFileSizes.
  477. //
  478. *((PLONGLONG)(Scb->NonpagedScb->SegmentObject.SharedCacheMap) + 5) = Length;
  479. leave;
  480. }
  481. }
  482. //
  483. // Nonresident path
  484. //
  485. // Now determine where the new file size lines up with the
  486. // current file layout. The two cases we need to consider are
  487. // where the new file size is less than the current file size and
  488. // valid data length, in which case we need to shrink them.
  489. // Or we new file size is greater than the current allocation,
  490. // in which case we need to extend the allocation to match the
  491. // new file size.
  492. //
  493. if (Length > Scb->Header.AllocationSize.QuadPart) {
  494. LONGLONG NewAllocationSize = Length;
  495. BOOLEAN AskForMore = TRUE;
  496. //
  497. // See if this is the Usn Journal to enforce allocation granularity.
  498. //
  499. // **** Temporary - this support should be generalized with an Scb field
  500. // settable by all callers.
  501. //
  502. if (Scb == Vcb->UsnJournal) {
  503. LONGLONG MaxAllocation;
  504. //
  505. // Limit ourselves to 128 runs. We don't want to commit in the
  506. // middle of the allocation.
  507. //
  508. NewAllocationSize = MAXIMUM_RUNS_AT_ONCE * Vcb->BytesPerCluster;
  509. //
  510. // Don't use more than 1/4 of the free space on the volume.
  511. //
  512. MaxAllocation = Int64ShllMod32( Vcb->FreeClusters, Vcb->ClusterShift - 2 );
  513. if (NewAllocationSize > MaxAllocation) {
  514. //
  515. // Round down to the Max. Don't worry if there is nothing, our code
  516. // below will catch this case and the allocation package always rounds
  517. // to a compression unit boundary.
  518. //
  519. NewAllocationSize = MaxAllocation;
  520. }
  521. //
  522. // Don't grow by more than the Usn delta.
  523. //
  524. if (NewAllocationSize > (LONGLONG) Vcb->UsnJournalInstance.AllocationDelta) {
  525. NewAllocationSize = (LONGLONG) Vcb->UsnJournalInstance.AllocationDelta;
  526. }
  527. NewAllocationSize += (LONGLONG) Scb->Header.AllocationSize.QuadPart;
  528. //
  529. // Handle possible weird case.
  530. //
  531. if (NewAllocationSize < Length) {
  532. NewAllocationSize = Length;
  533. }
  534. //
  535. // Always pad the allocation to a compression unit boundary.
  536. //
  537. ASSERT( Scb->CompressionUnit != 0 );
  538. NewAllocationSize += Scb->CompressionUnit - 1;
  539. NewAllocationSize &= ~((LONGLONG) (Scb->CompressionUnit - 1));
  540. AskForMore = FALSE;
  541. } else if (Scb->Header.PagingIoResource == NULL) {
  542. //
  543. // If the file is sparse then make sure we allocate a full compression unit.
  544. // Otherwise we can end up with a partially allocated chunk in the Usn
  545. // Journal.
  546. //
  547. if (Scb->CompressionUnit != 0) {
  548. NewAllocationSize += Scb->CompressionUnit - 1;
  549. ((PLARGE_INTEGER) &NewAllocationSize)->LowPart &= ~(Scb->CompressionUnit - 1);
  550. }
  551. AskForMore = FALSE;
  552. }
  553. //
  554. // Add the allocation. Never ask for extra for logged streams.
  555. //
  556. NtfsAddAllocation( IrpContext,
  557. FileObject,
  558. Scb,
  559. LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart ),
  560. LlClustersFromBytes(Scb->Vcb, (NewAllocationSize - Scb->Header.AllocationSize.QuadPart)),
  561. AskForMore,
  562. NULL );
  563. ExAcquireFastMutex( Scb->Header.FastMutex );
  564. //
  565. // Otherwise see if we have to knock these numbers down...
  566. //
  567. } else {
  568. ExAcquireFastMutex( Scb->Header.FastMutex );
  569. if ((Length < Scb->Header.ValidDataLength.QuadPart) &&
  570. (Scb->Header.ValidDataLength.QuadPart != MAXLONGLONG)) {
  571. Scb->Header.ValidDataLength.QuadPart = Length;
  572. }
  573. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
  574. (Length < Scb->ValidDataToDisk)) {
  575. Scb->ValidDataToDisk = Length;
  576. }
  577. }
  578. //
  579. // Now put the new size in the Scb.
  580. //
  581. Scb->Header.FileSize.QuadPart = Length;
  582. ExReleaseFastMutex( Scb->Header.FastMutex );
  583. //
  584. // Call our common routine to modify the file sizes. We are now
  585. // done with Length and NewValidDataLength, and we have
  586. // PagingIo + main exclusive (so no one can be working on this Scb).
  587. // NtfsWriteFileSizes uses the sizes in the Scb, and this is the
  588. // one place where in Ntfs where we wish to use a different value
  589. // for ValidDataLength. Therefore, we save the current ValidData
  590. // and plug it with our desired value and restore on return.
  591. //
  592. NtfsWriteFileSizes( IrpContext,
  593. Scb,
  594. &Scb->Header.ValidDataLength.QuadPart,
  595. FALSE,
  596. TRUE,
  597. TRUE );
  598. //
  599. // Now update Cc.
  600. //
  601. NtfsSetCcFileSizes( FileObject, Scb, (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
  602. } finally {
  603. if (CleanupAttrContext) {
  604. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  605. }
  606. }
  607. }
  608. NTFSAPI
  609. NTSTATUS
  610. NtfsHoldIrpForNewLength (
  611. IN PIRP_CONTEXT IrpContext,
  612. IN PSCB Scb,
  613. IN PIRP Irp,
  614. IN LONGLONG Length,
  615. IN PDRIVER_CANCEL CancelRoutine,
  616. IN PVOID CapturedData OPTIONAL,
  617. OUT PVOID *CopyCapturedData OPTIONAL,
  618. IN ULONG CapturedDataLength
  619. )
  620. /*++
  621. RoutineDescription:
  622. This routine may be called to wait until the designated stream exceeds the specified
  623. length.
  624. Arguments:
  625. Scb - Supplies the stream to wait on.
  626. Irp - Supplies the address of the Irp to hold
  627. Length - Supplies the length to be exceeded. To wait for any file extend, supply the last seen
  628. FileSize. To wait for N new bytes wait for last seen FileSize + N.
  629. CancelRoutine - Routine to register as the cancel routine.
  630. CapturedData - Specified if caller wishes to have auxillary data captured to pool.
  631. CopyCapturedData - Address to store copy of the captured data.
  632. CapturedDataLength - Length of the auxillary data to capture. Must be 0 if CapturedData not
  633. specified.
  634. Return value:
  635. NTSTATUS - Status of posting this request. STATUS_CANCELLED if the irp has been cancelled
  636. before we could register a callback, STATUS_PENDING if the request was posted without
  637. problem. Any other error indicates the irp wasn't posted and our caller needs to
  638. clean it up.
  639. --*/
  640. {
  641. PWAIT_FOR_NEW_LENGTH WaitForNewLength;
  642. NTSTATUS Status = STATUS_PENDING;
  643. PAGED_CODE();
  644. //
  645. // Allocate and initialize a wait block.
  646. //
  647. WaitForNewLength = NtfsAllocatePool( NonPagedPool, QuadAlign(sizeof(WAIT_FOR_NEW_LENGTH)) + CapturedDataLength );
  648. RtlZeroMemory( WaitForNewLength, sizeof(WAIT_FOR_NEW_LENGTH) );
  649. if (ARGUMENT_PRESENT(CapturedData)) {
  650. RtlCopyMemory( Add2Ptr(WaitForNewLength, QuadAlign(sizeof(WAIT_FOR_NEW_LENGTH))),
  651. CapturedData,
  652. CapturedDataLength );
  653. CapturedData = Add2Ptr(WaitForNewLength, QuadAlign(sizeof(WAIT_FOR_NEW_LENGTH)));
  654. *CopyCapturedData = CapturedData;
  655. }
  656. WaitForNewLength->Irp = Irp;
  657. WaitForNewLength->Length = Length;
  658. WaitForNewLength->Stream = Scb;
  659. WaitForNewLength->Status = STATUS_SUCCESS;
  660. WaitForNewLength->Flags = NTFS_WAIT_FLAG_ASYNC;
  661. //
  662. // Prepare the Irp for hanging around. Only make this call once per Irp. We occasionally
  663. // wake up a waiting Irp and then find we don't have enough data to return. In that
  664. // case we don't want to clean up the 'borrowed' IrpContext and the Irp has already
  665. // been prepared.
  666. //
  667. if (IrpContext->OriginatingIrp == Irp) {
  668. NtfsPrePostIrp( IrpContext, Irp );
  669. }
  670. //
  671. // Synchronize to queue and initialize the wait block.
  672. //
  673. ExAcquireFastMutex( Scb->Header.FastMutex );
  674. if (NtfsSetCancelRoutine( Irp, CancelRoutine, (ULONG_PTR) WaitForNewLength, TRUE )) {
  675. InsertTailList( &Scb->ScbType.Data.WaitForNewLength, &WaitForNewLength->WaitList );
  676. IoMarkIrpPending( Irp );
  677. //
  678. // The irp has already been marked for cancel.
  679. //
  680. } else {
  681. Status = STATUS_CANCELLED;
  682. NtfsFreePool( WaitForNewLength );
  683. }
  684. ExReleaseFastMutex( Scb->Header.FastMutex );
  685. //
  686. // Mark the Irp pending and get out.
  687. //
  688. return Status;
  689. }
  690. NTFSAPI
  691. NTSTATUS
  692. NtOfsWaitForNewLength (
  693. IN PSCB Scb,
  694. IN LONGLONG Length,
  695. IN ULONG Async,
  696. IN PIRP Irp,
  697. IN PDRIVER_CANCEL CancelRoutine,
  698. IN PLARGE_INTEGER Timeout OPTIONAL
  699. )
  700. /*++
  701. RoutineDescription:
  702. This routine may be called to wait until the designated stream exceeds the specified
  703. length.
  704. Arguments:
  705. Scb - Supplies the stream to wait on.
  706. Length - Supplies the length to be exceeded. To wait for any file extend, supply the last seen
  707. FileSize. To wait for N new bytes wait for last seen FileSize + N.
  708. Async - Indicates if we want to complete this request in another thread in
  709. the case of cancel.
  710. Irp - Supplies Irp of current request, so that wait can be skipped if Irp has been cancelled.
  711. CancelRoutine - This is the cancel routine to store in the Irp.
  712. TimeOut - Supplies an standard optional timeout spec, in case the caller wants to set
  713. a max time to wait.
  714. Return value:
  715. NTSTATUS - Status to proceed with the request. It may be STATUS_SUCCESS, STATUS_TIMEOUT or
  716. STATUS_CANCELLED. It may also be some other error specific to this type of request.
  717. In general the caller may wish to ignore the status code since they own the Irp now
  718. and are responsible for completing it.
  719. --*/
  720. {
  721. PWAIT_FOR_NEW_LENGTH WaitForNewLength;
  722. LONGLONG OriginalLength = Scb->Header.FileSize.QuadPart;
  723. NTSTATUS Status = STATUS_SUCCESS;
  724. PAGED_CODE();
  725. //
  726. // Allocate and initialize a wait block.
  727. //
  728. WaitForNewLength = NtfsAllocatePool( NonPagedPool, sizeof( WAIT_FOR_NEW_LENGTH ));
  729. WaitForNewLength->Irp = Irp;
  730. WaitForNewLength->Length = Length;
  731. WaitForNewLength->Stream = Scb;
  732. WaitForNewLength->Status = STATUS_SUCCESS;
  733. //
  734. // Take different action if this is async or sync.
  735. //
  736. if (Async) {
  737. WaitForNewLength->Flags = NTFS_WAIT_FLAG_ASYNC;
  738. } else {
  739. WaitForNewLength->Flags = 0;
  740. KeInitializeEvent( &WaitForNewLength->Event, NotificationEvent, FALSE );
  741. }
  742. //
  743. // Test if we need to wait at all.
  744. //
  745. ExAcquireFastMutex( Scb->Header.FastMutex );
  746. //
  747. // Has the length already changed? If not we must wait.
  748. //
  749. if (Scb->Header.FileSize.QuadPart <= Length) {
  750. //
  751. // Now set up the cancel routine. Return cancel if the user has
  752. // already cancelled this. Otherwise set up to wait.
  753. //
  754. if (NtfsSetCancelRoutine( Irp, CancelRoutine, (ULONG_PTR) WaitForNewLength, Async )) {
  755. InsertTailList( &Scb->ScbType.Data.WaitForNewLength, &WaitForNewLength->WaitList );
  756. ExReleaseFastMutex( Scb->Header.FastMutex );
  757. //
  758. // Now wait for someone to signal the length change.
  759. //
  760. if (!Async) {
  761. do {
  762. Status = KeWaitForSingleObject( &WaitForNewLength->Event,
  763. Executive,
  764. (KPROCESSOR_MODE)(ARGUMENT_PRESENT(Irp) ?
  765. Irp->RequestorMode :
  766. KernelMode),
  767. TRUE,
  768. Timeout );
  769. //
  770. // If the system timed out but there was no change in the file length then
  771. // we want to wait for the first change of the file. Wait again but without
  772. // a timeout and a length of the current size + 1. This satisfies the timeout
  773. // semantics which are don't wait for the full user length request to be satisfied
  774. // if it doesn't occur within the timeout period. Return either what has changed
  775. // in that time or the first change which occurs if nothing changed within the
  776. // timeout period.
  777. //
  778. if ((Status == STATUS_TIMEOUT) &&
  779. ARGUMENT_PRESENT( Timeout ) &&
  780. (Scb->Header.FileSize.QuadPart == OriginalLength)) {
  781. Timeout = NULL;
  782. WaitForNewLength->Length = OriginalLength + 1;
  783. //
  784. // Set the status to STATUS_KERNEL_APC so we will retry.
  785. //
  786. Status = STATUS_KERNEL_APC;
  787. continue;
  788. }
  789. } while (Status == STATUS_KERNEL_APC);
  790. //
  791. // Make sure to clear the cancel routine. We don't care if
  792. // a cancel is underway here.
  793. //
  794. ExAcquireFastMutex( Scb->Header.FastMutex );
  795. //
  796. // Make a timeout look like STATUS_SUCCESS. Otherwise return the error.
  797. //
  798. if (Status == STATUS_TIMEOUT) {
  799. Status = STATUS_SUCCESS;
  800. //
  801. // Clear the cancel routine.
  802. //
  803. NtfsClearCancelRoutine( WaitForNewLength->Irp );
  804. } else {
  805. //
  806. // If the wait completed with success then check for the error
  807. // in the wait block.
  808. //
  809. if (Status == STATUS_SUCCESS) {
  810. Status = WaitForNewLength->Status;
  811. //
  812. // Clear the cancel routine.
  813. //
  814. } else {
  815. NtfsClearCancelRoutine( WaitForNewLength->Irp );
  816. }
  817. }
  818. RemoveEntryList( &WaitForNewLength->WaitList );
  819. ExReleaseFastMutex( Scb->Header.FastMutex );
  820. NtfsFreePool( WaitForNewLength );
  821. //
  822. // The current thread is finished with the Irp.
  823. //
  824. } else {
  825. Status = STATUS_PENDING;
  826. }
  827. //
  828. // The irp has already been marked for cancel.
  829. //
  830. } else {
  831. ExReleaseFastMutex( Scb->Header.FastMutex );
  832. NtfsFreePool( WaitForNewLength );
  833. Status = STATUS_CANCELLED;
  834. }
  835. } else {
  836. ExReleaseFastMutex( Scb->Header.FastMutex );
  837. NtfsFreePool( WaitForNewLength );
  838. }
  839. return Status;
  840. }
  841. VOID
  842. NtOfsPostNewLength (
  843. IN PIRP_CONTEXT IrpContext OPTIONAL,
  844. IN PSCB Scb,
  845. IN BOOLEAN WakeAll
  846. )
  847. /*++
  848. RoutineDescription:
  849. This routine may be called to wake one or more waiters based on the desired FileSize change,
  850. or to unconditionally wake all waiters (such as for a shutdown condition).
  851. NOTE: The caller must have the FsRtl header mutex acquired when calling this routine.
  852. Arguments:
  853. Scb - Supplies the stream to act on.
  854. WakeAll - Supplies TRUE if all waiters should be unconditionally woken.
  855. Return value:
  856. None.
  857. --*/
  858. {
  859. PWAIT_FOR_NEW_LENGTH WaitForNewLength, WaiterToWake;
  860. ASSERT(FIELD_OFFSET(WAIT_FOR_NEW_LENGTH, WaitList) == 0);
  861. PAGED_CODE();
  862. ExAcquireFastMutex( Scb->Header.FastMutex );
  863. WaitForNewLength = (PWAIT_FOR_NEW_LENGTH)Scb->ScbType.Data.WaitForNewLength.Flink;
  864. while (WaitForNewLength != (PWAIT_FOR_NEW_LENGTH)&Scb->ScbType.Data.WaitForNewLength) {
  865. //
  866. // If we are supposed to wake this guy, then move our pointer to the next guy
  867. // first, then wake him, setting his event after removing him from the list,
  868. // since setting the event will cause him to eventually reuse the stack space
  869. // containing the wait block.
  870. //
  871. if ((Scb->Header.FileSize.QuadPart > WaitForNewLength->Length) || WakeAll) {
  872. WaiterToWake = WaitForNewLength;
  873. WaitForNewLength = (PWAIT_FOR_NEW_LENGTH)WaitForNewLength->WaitList.Flink;
  874. //
  875. // If this is for an asynchronous Irp, then remove him from the list and
  876. // drop the mutex to do further processing. We only do further processing
  877. // if there is not currently a cancel thread active for this Irp.
  878. //
  879. // NOTE: This code currently relies on the fact that there is just one
  880. // caller to the routine to hold an Irp. If more such caller's
  881. // surface, then the routine address would have to be stored in
  882. // the wait context.
  883. //
  884. // If cancel is active then we will skip over this Irp.
  885. //
  886. if (NtfsClearCancelRoutine( WaiterToWake->Irp )) {
  887. if (FlagOn( WaiterToWake->Flags, NTFS_WAIT_FLAG_ASYNC )) {
  888. //
  889. // Make sure we decrement the reference count in the Scb.
  890. //
  891. InterlockedDecrement( &Scb->CloseCount );
  892. RemoveEntryList( &WaiterToWake->WaitList );
  893. ExReleaseFastMutex( Scb->Header.FastMutex );
  894. //
  895. // Nothing really should go wrong, unless we get an I/O error,
  896. // none the less, we want to stop any exceptions and complete
  897. // the request ourselves rather than impact our caller.
  898. //
  899. if (ARGUMENT_PRESENT( IrpContext )) {
  900. try {
  901. NtfsReadUsnJournal( IrpContext,
  902. WaiterToWake->Irp,
  903. FALSE );
  904. } except(NtfsExceptionFilter( NULL, GetExceptionInformation())) {
  905. NtfsCompleteRequest( NULL, WaiterToWake->Irp, GetExceptionCode() );
  906. }
  907. //
  908. // Assume the only caller with no IrpContext is cancelling the request.
  909. //
  910. } else {
  911. NtfsCompleteRequest( NULL, WaiterToWake->Irp, STATUS_CANCELLED );
  912. }
  913. //
  914. // Free the wait block and go back to the beginning of the list.
  915. // Is it possible that we can into a continuous loop here? We may
  916. // need a strategy to recognize which entries we have visited
  917. // in this loop.
  918. //
  919. NtfsFreePool( WaiterToWake );
  920. ExAcquireFastMutex( Scb->Header.FastMutex );
  921. WaitForNewLength = (PWAIT_FOR_NEW_LENGTH)Scb->ScbType.Data.WaitForNewLength.Flink;
  922. } else {
  923. KeSetEvent( &WaiterToWake->Event, 0, FALSE );
  924. }
  925. }
  926. } else {
  927. WaitForNewLength = (PWAIT_FOR_NEW_LENGTH)WaitForNewLength->WaitList.Flink;
  928. }
  929. }
  930. ExReleaseFastMutex( Scb->Header.FastMutex );
  931. }
  932. NTFSAPI
  933. VOID
  934. NtOfsFlushAttribute (
  935. IN PIRP_CONTEXT IrpContext,
  936. IN PSCB Scb,
  937. IN ULONG Purge
  938. )
  939. /*++
  940. Routine Description:
  941. This routine flushes the specified attribute, and optionally purges it from the cache.
  942. Arguments:
  943. Scb - Supplies an Scb as the previously returned handle for this attribute.
  944. Purge - Supplies TRUE if the attribute is to be purged.
  945. Return Value:
  946. None (Deleting a nonexistant index is benign).
  947. --*/
  948. {
  949. PAGED_CODE();
  950. if (Purge) {
  951. NtfsFlushAndPurgeScb( IrpContext, Scb, NULL );
  952. } else {
  953. NtfsFlushUserStream( IrpContext, Scb, NULL, 0 );
  954. }
  955. }
  956. NTFSAPI
  957. VOID
  958. NtOfsPutData (
  959. IN PIRP_CONTEXT IrpContext,
  960. IN PSCB Scb,
  961. IN LONGLONG Offset,
  962. IN ULONG Length,
  963. IN PVOID Data OPTIONAL
  964. )
  965. /*++
  966. Routine Description:
  967. This routine is called to update a range of a recoverable stream. Note this
  968. update cannot extend the filesize unless its a write to eof put (Offset = -1)
  969. Arguments:
  970. Scb - Scb for the stream to zero.
  971. Offset - Offset in stream to update.
  972. Length - Length of stream to update in bytes.
  973. Data - Data to update stream with if specified, else range should be zeroed.
  974. Return Value:
  975. None.
  976. --*/
  977. {
  978. ULONG OriginalLength = Length;
  979. BOOLEAN WriteToEof = FALSE;
  980. BOOLEAN MovingBackwards = TRUE;
  981. PAGED_CODE();
  982. ASSERT( FlagOn( Scb->ScbState, SCB_STATE_MODIFIED_NO_WRITE ) );
  983. //
  984. // Handle Put to end of file.
  985. //
  986. if (Offset < 0) {
  987. WriteToEof = TRUE;
  988. Offset = Scb->Header.FileSize.QuadPart;
  989. NtOfsSetLength( IrpContext, Scb, Offset + Length );
  990. }
  991. ASSERT((Offset + Length) <= Scb->Header.FileSize.QuadPart);
  992. //
  993. // First handle the resident case.
  994. //
  995. if (FlagOn(Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT)) {
  996. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  997. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  998. PATTRIBUTE_RECORD_HEADER Attribute;
  999. ULONG RecordOffset, AttributeOffset;
  1000. PVCB Vcb = Scb->Vcb;
  1001. NtfsInitializeAttributeContext( &Context );
  1002. try {
  1003. //
  1004. // Lookup and pin the attribute.
  1005. //
  1006. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  1007. NtfsPinMappedAttribute( IrpContext, Vcb, &Context );
  1008. //
  1009. // Extract the relevant pointers and calculate offsets.
  1010. //
  1011. FileRecord = NtfsContainingFileRecord(&Context);
  1012. Attribute = NtfsFoundAttribute(&Context);
  1013. RecordOffset = PtrOffset(FileRecord, Attribute);
  1014. AttributeOffset = Attribute->Form.Resident.ValueOffset + (ULONG)Offset;
  1015. //
  1016. // Log the change while we still have the old data.
  1017. //
  1018. FileRecord->Lsn =
  1019. NtfsWriteLog( IrpContext,
  1020. Vcb->MftScb,
  1021. NtfsFoundBcb(&Context),
  1022. UpdateResidentValue,
  1023. Data,
  1024. Length,
  1025. UpdateResidentValue,
  1026. Add2Ptr(Attribute, Attribute->Form.Resident.ValueOffset + (ULONG)Offset),
  1027. Length,
  1028. NtfsMftOffset(&Context),
  1029. RecordOffset,
  1030. AttributeOffset,
  1031. Vcb->BytesPerFileRecordSegment );
  1032. //
  1033. // Now update this data by calling the same routine as restart.
  1034. //
  1035. NtfsRestartChangeValue( IrpContext,
  1036. FileRecord,
  1037. RecordOffset,
  1038. AttributeOffset,
  1039. Data,
  1040. Length,
  1041. FALSE );
  1042. //
  1043. // If there is a stream for this attribute, then we must update it in the
  1044. // cache, copying from the attribute itself in order to handle the zeroing
  1045. // (Data == NULL) case.
  1046. //
  1047. if (Scb->FileObject != NULL) {
  1048. CcCopyWrite( Scb->FileObject,
  1049. (PLARGE_INTEGER)&Offset,
  1050. Length,
  1051. TRUE,
  1052. Add2Ptr(Attribute, AttributeOffset) );
  1053. }
  1054. //
  1055. // Optionally update ValidDataLength
  1056. //
  1057. Offset += Length;
  1058. if (Offset > Scb->Header.ValidDataLength.QuadPart) {
  1059. Scb->Header.ValidDataLength.QuadPart = Offset;
  1060. }
  1061. } finally {
  1062. NtfsCleanupAttributeContext( IrpContext, &Context );
  1063. }
  1064. //
  1065. // Now handle the nonresident case.
  1066. //
  1067. } else {
  1068. PVOID Buffer;
  1069. PVOID SubData = NULL;
  1070. LONGLONG NewValidDataLength = Offset + Length;
  1071. PBCB Bcb = NULL;
  1072. ULONG PageOffset = (ULONG)Offset & (PAGE_SIZE - 1);
  1073. ULONGLONG SubOffset;
  1074. ULONG SubLength;
  1075. ASSERT(Scb->FileObject != NULL);
  1076. //
  1077. // If we are starting beyond ValidDataLength, then recurse to
  1078. // zero what we need.
  1079. //
  1080. if (Offset > Scb->Header.FileSize.QuadPart) {
  1081. ASSERT((Offset - Scb->Header.FileSize.QuadPart) <= MAXULONG);
  1082. NtOfsPutData( IrpContext,
  1083. Scb,
  1084. Scb->Header.FileSize.QuadPart,
  1085. (ULONG)(Offset - Scb->Header.FileSize.QuadPart),
  1086. NULL );
  1087. }
  1088. try {
  1089. //
  1090. // Now loop until there are no more pages with new data
  1091. // to log. We'll start assuming a backwards copy
  1092. //
  1093. while (Length != 0) {
  1094. if (MovingBackwards) {
  1095. //
  1096. // Calculate the last page of the transfer - if its the 1st page start at offset
  1097. //
  1098. SubOffset = max( Offset, BlockAlignTruncate( Offset + Length - 1, PAGE_SIZE ) );
  1099. SubLength = (ULONG)(Offset + Length - SubOffset);
  1100. //
  1101. // This guarantees we can truncate to a 32 bit value
  1102. //
  1103. ASSERT( Offset + Length - SubOffset <= PAGE_SIZE );
  1104. } else {
  1105. SubOffset = Offset + OriginalLength - Length;
  1106. SubLength = min( PAGE_SIZE - ((ULONG)SubOffset & (PAGE_SIZE - 1)), Length );
  1107. }
  1108. if (Data != NULL) {
  1109. SubData = Add2Ptr( Data, SubOffset - Offset );
  1110. }
  1111. #ifdef BENL_DBG
  1112. if(BlockAlignTruncate( Offset + Length - 1, PAGE_SIZE ) != BlockAlignTruncate( Offset, PAGE_SIZE )) {
  1113. // KdPrint(( "NTFS: pin %I64x %x from %I64x %x\n", SubOffset, SubLength, Offset, Length ));
  1114. }
  1115. #endif
  1116. //
  1117. // Pin the page
  1118. //
  1119. NtfsPinStream( IrpContext,
  1120. Scb,
  1121. SubOffset,
  1122. SubLength,
  1123. &Bcb,
  1124. &Buffer );
  1125. //
  1126. // Doublecheck the direction of copy based on the relative position of the
  1127. // source (data) and destination (buffer). We don't care if the source is null
  1128. // We'll only switch once from backwards to forwards
  1129. //
  1130. if (MovingBackwards &&
  1131. ((PCHAR)Buffer < (PCHAR)SubData) &&
  1132. (Data != NULL)) {
  1133. //
  1134. // Start over with the opposite direction
  1135. //
  1136. MovingBackwards = FALSE;
  1137. NtfsUnpinBcb( IrpContext, &Bcb );
  1138. continue;
  1139. }
  1140. //
  1141. // Now log the changes to this page.
  1142. //
  1143. (VOID)
  1144. NtfsWriteLog( IrpContext,
  1145. Scb,
  1146. Bcb,
  1147. UpdateNonresidentValue,
  1148. SubData,
  1149. SubLength,
  1150. WriteToEof ? Noop : UpdateNonresidentValue,
  1151. WriteToEof ? NULL : Buffer,
  1152. WriteToEof ? 0 : SubLength,
  1153. BlockAlignTruncate( SubOffset, PAGE_SIZE ),
  1154. (ULONG)(SubOffset & (PAGE_SIZE - 1)),
  1155. 0,
  1156. (ULONG)(SubOffset & (PAGE_SIZE - 1)) + SubLength );
  1157. //
  1158. // Move the data into place.
  1159. //
  1160. if (Data != NULL) {
  1161. RtlMoveMemory( Buffer, SubData, SubLength );
  1162. } else {
  1163. RtlZeroMemory( Buffer, SubLength );
  1164. }
  1165. //
  1166. // Unpin the page and decrement the length
  1167. //
  1168. NtfsUnpinBcb( IrpContext, &Bcb );
  1169. Length -= SubLength;
  1170. }
  1171. //
  1172. // Optionally update ValidDataLength
  1173. //
  1174. if (NewValidDataLength > Scb->Header.ValidDataLength.QuadPart) {
  1175. Scb->Header.ValidDataLength.QuadPart = NewValidDataLength;
  1176. NtfsWriteFileSizes( IrpContext, Scb, &NewValidDataLength, TRUE, TRUE, TRUE );
  1177. //
  1178. // See if we have to wake anyone.
  1179. //
  1180. if (!IsListEmpty(&Scb->ScbType.Data.WaitForNewLength)) {
  1181. NtfsPostToNewLengthQueue( IrpContext, Scb );
  1182. }
  1183. }
  1184. } finally {
  1185. NtfsUnpinBcb( IrpContext, &Bcb );
  1186. }
  1187. }
  1188. }
  1189. //
  1190. // The following prototypes are here only for someone external to Ntfs (such as EFS)
  1191. // trying to link to Ntfs using ntfsexp.h.
  1192. //
  1193. NTFSAPI
  1194. VOID
  1195. NtOfsMapAttribute (
  1196. IN PIRP_CONTEXT IrpContext,
  1197. IN PSCB Scb,
  1198. IN LONGLONG Offset,
  1199. IN ULONG Length,
  1200. OUT PVOID *Buffer,
  1201. OUT PMAP_HANDLE MapHandle
  1202. )
  1203. /*++
  1204. Routine Description:
  1205. NtOfsMapAttribute maps the given region of an Scb. Its a thin wrapper
  1206. around CcMapData.
  1207. Arguments:
  1208. IrpContext - Supplies the irpcontext associated with the current operation
  1209. Scb - Scb to map data from
  1210. Offset - offset into data
  1211. Length - length of region to be pinned
  1212. Buffer - returned buffer with pinned data virtual address
  1213. MapHandle - returned map handle used to manage the pinned region.
  1214. Return Value:
  1215. None
  1216. --*/
  1217. {
  1218. PAGED_CODE( );
  1219. UNREFERENCED_PARAMETER( IrpContext );
  1220. CcMapData( Scb->FileObject, (PLARGE_INTEGER)&Offset, Length, TRUE, &MapHandle->Bcb, Buffer );
  1221. #ifdef MAPCOUNT_DBG
  1222. IrpContext->MapCount++;
  1223. #endif
  1224. MapHandle->FileOffset = Offset;
  1225. MapHandle->Length = Length;
  1226. MapHandle->Buffer = *(PVOID *)Buffer;
  1227. }
  1228. NTFSAPI
  1229. VOID
  1230. NtOfsPreparePinWrite (
  1231. IN PIRP_CONTEXT IrpContext,
  1232. IN PSCB Scb,
  1233. IN LONGLONG Offset,
  1234. IN ULONG Length,
  1235. OUT PVOID *Buffer,
  1236. OUT PMAP_HANDLE MapHandle
  1237. )
  1238. /*++
  1239. Routine Description:
  1240. NtOfsPreparePinWrite maps and pins a portion of the specified attribute and
  1241. returns a pointer to the memory. This is equivalent to doing a NtOfsMapAttribute
  1242. followed by NtOfsPinRead and NtOfsDirty but is more efficient.
  1243. Arguments:
  1244. IrpContext - Supplies the irpcontext associated with the current operation
  1245. Scb - Scb to pin in preparation for a write
  1246. Offset - offset into data
  1247. Length - length of region to be pinned
  1248. Buffer - returned buffer with pinned data virtual address
  1249. MapHandle - returned map handle used to manage the pinned region.
  1250. Return Value:
  1251. None
  1252. --*/
  1253. {
  1254. UNREFERENCED_PARAMETER( IrpContext );
  1255. if ((Offset + Length) > Scb->Header.AllocationSize.QuadPart) {
  1256. ExRaiseStatus(STATUS_END_OF_FILE);
  1257. }
  1258. CcPreparePinWrite( Scb->FileObject, (PLARGE_INTEGER)&Offset, Length, FALSE, TRUE, &MapHandle->Bcb, Buffer );
  1259. #ifdef MAPCOUNT_DBG
  1260. IrpContext->MapCount++;
  1261. #endif
  1262. MapHandle->FileOffset = Offset;
  1263. MapHandle->Length = Length;
  1264. MapHandle->Buffer = Buffer;
  1265. }
  1266. NTFSAPI
  1267. VOID
  1268. NtOfsPinRead(
  1269. IN PIRP_CONTEXT IrpContext,
  1270. IN PSCB Scb,
  1271. IN LONGLONG Offset,
  1272. IN ULONG Length,
  1273. OUT PMAP_HANDLE MapHandle
  1274. )
  1275. /*++
  1276. Routine Description:
  1277. NtOfsPinRead pins a section of a map and read in all pages from the mapped
  1278. attribute. Offset and Length must describe a byte range which is equal to
  1279. or included by the original mapped range.
  1280. Arguments:
  1281. IrpContext - Supplies the irpcontext associated with the current operation
  1282. Scb - Scb to pin data for reads in
  1283. Offset - offset into data
  1284. Length - length of region to be pinned
  1285. MapHandle - returned map handle used to manage the pinned region.
  1286. Return Value:
  1287. None
  1288. --*/
  1289. {
  1290. UNREFERENCED_PARAMETER( IrpContext );
  1291. ASSERT( MapHandle->Bcb != NULL );
  1292. ASSERT( (Offset >= MapHandle->FileOffset) && ((Offset + Length) <= (MapHandle->FileOffset + MapHandle->Length)) );
  1293. CcPinMappedData( Scb->FileObject, (PLARGE_INTEGER)&Offset, Length, TRUE, &MapHandle->Bcb );
  1294. MapHandle->FileOffset = Offset;
  1295. MapHandle->Length = Length;
  1296. }
  1297. NTFSAPI
  1298. VOID
  1299. NtOfsReleaseMap (
  1300. IN PIRP_CONTEXT IrpContext,
  1301. IN PMAP_HANDLE MapHandle
  1302. )
  1303. /*++
  1304. Routine Description:
  1305. This routine unmaps/unpins a mapped portion of an attribute.
  1306. Arguments:
  1307. IrpContext - Supplies the irpcontext associated with the current operation
  1308. MapHandle - Supplies map handle containing the bcb to be released.
  1309. Return Value:
  1310. None
  1311. --*/
  1312. {
  1313. UNREFERENCED_PARAMETER( IrpContext );
  1314. if (MapHandle->Bcb != NULL) {
  1315. CcUnpinData( MapHandle->Bcb );
  1316. #ifdef MAPCOUNT_DBG
  1317. IrpContext->MapCount--;
  1318. #endif
  1319. MapHandle->Bcb = NULL;
  1320. }
  1321. }