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.

18163 lines
575 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. AttrSup.c
  5. Abstract:
  6. This module implements the attribute management routines for Ntfs
  7. Author:
  8. David Goebel [DavidGoe] 25-June-1991
  9. Tom Miller [TomM] 9-November-1991
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (NTFS_BUG_CHECK_ATTRSUP)
  17. //
  18. // Local debug trace level
  19. //
  20. #define Dbg (DEBUG_TRACE_ATTRSUP)
  21. //
  22. // Define a tag for general pool allocations from this module
  23. //
  24. #undef MODULE_POOL_TAG
  25. #define MODULE_POOL_TAG ('AFtN')
  26. #define NTFS_MAX_ZERO_RANGE (0x40000000)
  27. #define NTFS_CHECK_INSTANCE_ROLLOVER (0xf000)
  28. //
  29. //
  30. // Internal support routines
  31. //
  32. BOOLEAN
  33. NtfsFindInFileRecord (
  34. IN PIRP_CONTEXT IrpContext,
  35. IN PATTRIBUTE_RECORD_HEADER Attribute,
  36. OUT PATTRIBUTE_RECORD_HEADER *ReturnAttribute,
  37. IN ATTRIBUTE_TYPE_CODE QueriedTypeCode,
  38. IN PCUNICODE_STRING QueriedName OPTIONAL,
  39. IN BOOLEAN IgnoreCase,
  40. IN PVOID QueriedValue OPTIONAL,
  41. IN ULONG QueriedValueLength
  42. );
  43. //
  44. // Internal support routines for managing file record space
  45. //
  46. VOID
  47. NtfsCreateNonresidentWithValue (
  48. IN PIRP_CONTEXT IrpContext,
  49. IN PFCB Fcb,
  50. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  51. IN PUNICODE_STRING AttributeName OPTIONAL,
  52. IN PVOID Value OPTIONAL,
  53. IN ULONG ValueLength,
  54. IN USHORT AttributeFlags,
  55. IN BOOLEAN WriteClusters,
  56. IN PSCB ThisScb OPTIONAL,
  57. IN BOOLEAN LogIt,
  58. IN PATTRIBUTE_ENUMERATION_CONTEXT Context
  59. );
  60. BOOLEAN
  61. NtfsGetSpaceForAttribute (
  62. IN PIRP_CONTEXT IrpContext,
  63. IN PFCB Fcb,
  64. IN ULONG Length,
  65. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  66. );
  67. VOID
  68. MakeRoomForAttribute (
  69. IN PIRP_CONTEXT IrpContext,
  70. IN PFCB Fcb,
  71. IN ULONG SizeNeeded,
  72. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  73. );
  74. VOID
  75. FindLargestAttributes (
  76. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  77. IN ULONG Number,
  78. OUT PATTRIBUTE_RECORD_HEADER *AttributeArray
  79. );
  80. LONGLONG
  81. MoveAttributeToOwnRecord (
  82. IN PIRP_CONTEXT IrpContext,
  83. IN PFCB Fcb,
  84. IN PATTRIBUTE_RECORD_HEADER Attribute,
  85. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context,
  86. OUT PBCB *NewBcb OPTIONAL,
  87. OUT PFILE_RECORD_SEGMENT_HEADER *NewFileRecord OPTIONAL
  88. );
  89. VOID
  90. SplitFileRecord (
  91. IN PIRP_CONTEXT IrpContext,
  92. IN PFCB Fcb,
  93. IN ULONG SizeNeeded,
  94. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  95. );
  96. PFILE_RECORD_SEGMENT_HEADER
  97. NtfsCloneFileRecord (
  98. IN PIRP_CONTEXT IrpContext,
  99. IN PFCB Fcb,
  100. IN BOOLEAN MftData,
  101. OUT PBCB *Bcb,
  102. OUT PMFT_SEGMENT_REFERENCE FileReference
  103. );
  104. ULONG
  105. GetSizeForAttributeList (
  106. IN PFILE_RECORD_SEGMENT_HEADER FileRecord
  107. );
  108. VOID
  109. CreateAttributeList (
  110. IN PIRP_CONTEXT IrpContext,
  111. IN PFCB Fcb,
  112. IN PFILE_RECORD_SEGMENT_HEADER FileRecord1,
  113. IN PFILE_RECORD_SEGMENT_HEADER FileRecord2 OPTIONAL,
  114. IN MFT_SEGMENT_REFERENCE SegmentReference2,
  115. IN PATTRIBUTE_RECORD_HEADER OldPosition OPTIONAL,
  116. IN ULONG SizeOfList,
  117. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT ListContext
  118. );
  119. VOID
  120. UpdateAttributeListEntry (
  121. IN PIRP_CONTEXT IrpContext,
  122. IN PFCB Fcb,
  123. IN PMFT_SEGMENT_REFERENCE OldFileReference,
  124. IN USHORT OldInstance,
  125. IN PMFT_SEGMENT_REFERENCE NewFileReference,
  126. IN USHORT NewInstance,
  127. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT ListContext
  128. );
  129. VOID
  130. NtfsAddNameToParent (
  131. IN PIRP_CONTEXT IrpContext,
  132. IN PSCB ParentScb,
  133. IN PFCB ThisFcb,
  134. IN BOOLEAN IgnoreCase,
  135. IN PBOOLEAN LogIt,
  136. IN PFILE_NAME FileNameAttr,
  137. OUT PUCHAR FileNameFlags,
  138. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  139. IN PNAME_PAIR NamePair OPTIONAL,
  140. IN PINDEX_CONTEXT IndexContext OPTIONAL
  141. );
  142. VOID
  143. NtfsAddDosOnlyName (
  144. IN PIRP_CONTEXT IrpContext,
  145. IN PSCB ParentScb,
  146. IN PFCB ThisFcb,
  147. IN UNICODE_STRING FileName,
  148. IN BOOLEAN LogIt,
  149. IN PUNICODE_STRING SuggestedDosName OPTIONAL
  150. );
  151. BOOLEAN
  152. NtfsAddTunneledNtfsOnlyName (
  153. IN PIRP_CONTEXT IrpContext,
  154. IN PSCB ParentScb,
  155. IN PFCB ThisFcb,
  156. IN PUNICODE_STRING FileName,
  157. IN PBOOLEAN LogIt
  158. );
  159. USHORT
  160. NtfsScanForFreeInstance (
  161. IN PIRP_CONTEXT IrpContext,
  162. IN PVCB Vcb,
  163. IN PFILE_RECORD_SEGMENT_HEADER FileRecord
  164. );
  165. VOID
  166. NtfsMergeFileRecords (
  167. IN PIRP_CONTEXT IrpContext,
  168. IN PSCB Scb,
  169. IN BOOLEAN RestoreContext,
  170. IN PATTRIBUTE_ENUMERATION_CONTEXT Context
  171. );
  172. NTSTATUS
  173. NtfsCheckLocksInZeroRange (
  174. IN PIRP_CONTEXT IrpContext,
  175. IN PIRP Irp,
  176. IN PSCB Scb,
  177. IN PFILE_OBJECT FileObject,
  178. IN PLONGLONG StartingOffset,
  179. IN ULONG ByteCount
  180. );
  181. #ifdef ALLOC_PRAGMA
  182. #pragma alloc_text(PAGE, CreateAttributeList)
  183. #pragma alloc_text(PAGE, FindLargestAttributes)
  184. #pragma alloc_text(PAGE, GetSizeForAttributeList)
  185. #pragma alloc_text(PAGE, MakeRoomForAttribute)
  186. #pragma alloc_text(PAGE, MoveAttributeToOwnRecord)
  187. #pragma alloc_text(PAGE, NtfsAddAttributeAllocation)
  188. #pragma alloc_text(PAGE, NtfsAddDosOnlyName)
  189. #pragma alloc_text(PAGE, NtfsAddLink)
  190. #pragma alloc_text(PAGE, NtfsAddNameToParent)
  191. #pragma alloc_text(PAGE, NtfsAddToAttributeList)
  192. #pragma alloc_text(PAGE, NtfsAddTunneledNtfsOnlyName)
  193. #pragma alloc_text(PAGE, NtfsChangeAttributeSize)
  194. #pragma alloc_text(PAGE, NtfsChangeAttributeValue)
  195. #pragma alloc_text(PAGE, NtfsCheckLocksInZeroRange)
  196. #pragma alloc_text(PAGE, NtfsCleanupAttributeContext)
  197. #pragma alloc_text(PAGE, NtfsCloneFileRecord)
  198. #pragma alloc_text(PAGE, NtfsConvertToNonresident)
  199. #pragma alloc_text(PAGE, NtfsCreateAttributeWithAllocation)
  200. #pragma alloc_text(PAGE, NtfsCreateAttributeWithValue)
  201. #pragma alloc_text(PAGE, NtfsCreateNonresidentWithValue)
  202. #pragma alloc_text(PAGE, NtfsDeleteAllocationFromRecord)
  203. #pragma alloc_text(PAGE, NtfsDeleteAttributeAllocation)
  204. #pragma alloc_text(PAGE, NtfsDeleteAttributeRecord)
  205. #pragma alloc_text(PAGE, NtfsDeleteFile)
  206. #pragma alloc_text(PAGE, NtfsDeleteFromAttributeList)
  207. #pragma alloc_text(PAGE, NtfsFindInFileRecord)
  208. #pragma alloc_text(PAGE, NtfsGetAttributeTypeCode)
  209. #pragma alloc_text(PAGE, NtfsGetSpaceForAttribute)
  210. #pragma alloc_text(PAGE, NtfsGrowStandardInformation)
  211. #pragma alloc_text(PAGE, NtfsInitializeFileInExtendDirectory)
  212. #pragma alloc_text(PAGE, NtfsIsFileDeleteable)
  213. #pragma alloc_text(PAGE, NtfsLookupEntry)
  214. #pragma alloc_text(PAGE, NtfsLookupExternalAttribute)
  215. #pragma alloc_text(PAGE, NtfsLookupInFileRecord)
  216. #pragma alloc_text(PAGE, NtfsMapAttributeValue)
  217. #pragma alloc_text(PAGE, NtfsMergeFileRecords)
  218. #pragma alloc_text(PAGE, NtfsModifyAttributeFlags)
  219. #pragma alloc_text(PAGE, NtfsPrepareForUpdateDuplicate)
  220. #pragma alloc_text(PAGE, NtfsRemoveLink)
  221. #pragma alloc_text(PAGE, NtfsRemoveLinkViaFlags)
  222. #pragma alloc_text(PAGE, NtfsRestartChangeAttributeSize)
  223. #pragma alloc_text(PAGE, NtfsRestartChangeMapping)
  224. #pragma alloc_text(PAGE, NtfsRestartChangeValue)
  225. #pragma alloc_text(PAGE, NtfsRestartInsertAttribute)
  226. #pragma alloc_text(PAGE, NtfsRestartRemoveAttribute)
  227. #pragma alloc_text(PAGE, NtfsRestartWriteEndOfFileRecord)
  228. #pragma alloc_text(PAGE, NtfsRewriteMftMapping)
  229. #pragma alloc_text(PAGE, NtfsScanForFreeInstance)
  230. #pragma alloc_text(PAGE, NtfsSetSparseStream)
  231. #pragma alloc_text(PAGE, NtfsSetTotalAllocatedField)
  232. #pragma alloc_text(PAGE, NtfsUpdateDuplicateInfo)
  233. #pragma alloc_text(PAGE, NtfsUpdateFcb)
  234. #pragma alloc_text(PAGE, NtfsUpdateFcbInfoFromDisk)
  235. #pragma alloc_text(PAGE, NtfsUpdateFileNameFlags)
  236. #pragma alloc_text(PAGE, NtfsUpdateLcbDuplicateInfo)
  237. #pragma alloc_text(PAGE, NtfsUpdateScbFromAttribute)
  238. #pragma alloc_text(PAGE, NtfsUpdateStandardInformation)
  239. #pragma alloc_text(PAGE, NtfsWriteFileSizes)
  240. #pragma alloc_text(PAGE, NtfsZeroRangeInStream)
  241. #pragma alloc_text(PAGE, SplitFileRecord)
  242. #pragma alloc_text(PAGE, UpdateAttributeListEntry)
  243. #endif
  244. ATTRIBUTE_TYPE_CODE
  245. NtfsGetAttributeTypeCode (
  246. IN PVCB Vcb,
  247. IN PUNICODE_STRING AttributeTypeName
  248. )
  249. /*++
  250. Routine Description:
  251. This routine returns the attribute type code for a given attribute name.
  252. Arguments:
  253. Vcb - Pointer to the Vcb from which to consult the attribute definitions.
  254. AttributeTypeName - A string containing the attribute type name to be
  255. looked up.
  256. Return Value:
  257. The attribute type code corresponding to the specified name, or 0 if the
  258. attribute type name does not exist.
  259. --*/
  260. {
  261. PATTRIBUTE_DEFINITION_COLUMNS AttributeDef = Vcb->AttributeDefinitions;
  262. ATTRIBUTE_TYPE_CODE AttributeTypeCode = $UNUSED;
  263. UNICODE_STRING AttributeCodeName;
  264. PAGED_CODE();
  265. //
  266. // Loop through all of the definitions looking for a name match.
  267. //
  268. while (AttributeDef->AttributeName[0] != 0) {
  269. RtlInitUnicodeString( &AttributeCodeName, AttributeDef->AttributeName );
  270. //
  271. // The name lengths must match and the characters match exactly.
  272. //
  273. if ((AttributeCodeName.Length == AttributeTypeName->Length)
  274. && (RtlEqualMemory( AttributeTypeName->Buffer,
  275. AttributeDef->AttributeName,
  276. AttributeTypeName->Length ))) {
  277. AttributeTypeCode = AttributeDef->AttributeTypeCode;
  278. break;
  279. }
  280. //
  281. // Lets go to the next attribute column.
  282. //
  283. AttributeDef += 1;
  284. }
  285. return AttributeTypeCode;
  286. }
  287. VOID
  288. NtfsUpdateScbFromAttribute (
  289. IN PIRP_CONTEXT IrpContext,
  290. IN OUT PSCB Scb,
  291. IN PATTRIBUTE_RECORD_HEADER AttrHeader OPTIONAL
  292. )
  293. /*++
  294. Routine Description:
  295. This routine fills in the header of an Scb with the
  296. information from the attribute for this Scb.
  297. Arguments:
  298. Scb - Supplies the SCB to update
  299. AttrHeader - Optionally provides the attribute to update from
  300. Return Value:
  301. None
  302. --*/
  303. {
  304. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  305. BOOLEAN CleanupAttrContext = FALSE;
  306. PAGED_CODE();
  307. DebugTrace( +1, Dbg, ("NtfsUpdateScbFromAttribute: Entered\n") );
  308. //
  309. // If the attribute has been deleted, we can return immediately
  310. // claiming that the Scb has been initialized.
  311. //
  312. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  313. SetFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED );
  314. DebugTrace( -1, Dbg, ("NtfsUpdateScbFromAttribute: Exit\n") );
  315. return;
  316. }
  317. //
  318. // Use a try-finally to facilitate cleanup.
  319. //
  320. try {
  321. //
  322. // If we weren't given the attribute header, we look it up now.
  323. //
  324. if (!ARGUMENT_PRESENT( AttrHeader )) {
  325. NtfsInitializeAttributeContext( &AttrContext );
  326. CleanupAttrContext = TRUE;
  327. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  328. AttrHeader = NtfsFoundAttribute( &AttrContext );
  329. }
  330. //
  331. // Check whether this is resident or nonresident
  332. //
  333. if (NtfsIsAttributeResident( AttrHeader )) {
  334. //
  335. // Verify the resident value length.
  336. //
  337. if (AttrHeader->Form.Resident.ValueLength > AttrHeader->RecordLength - AttrHeader->Form.Resident.ValueOffset) {
  338. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  339. }
  340. Scb->Header.AllocationSize.QuadPart = AttrHeader->Form.Resident.ValueLength;
  341. if (!FlagOn( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
  342. Scb->Header.ValidDataLength =
  343. Scb->Header.FileSize = Scb->Header.AllocationSize;
  344. SetFlag(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED);
  345. }
  346. #ifdef SYSCACHE_DEBUG
  347. if (ScbIsBeingLogged( Scb )) {
  348. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_UPDATE_FROM_DISK, Scb->Header.ValidDataLength.QuadPart, 0, 0 );
  349. }
  350. #endif
  351. Scb->Header.AllocationSize.LowPart =
  352. QuadAlign( Scb->Header.AllocationSize.LowPart );
  353. Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
  354. //
  355. // Set the resident flag in the Scb.
  356. //
  357. SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT );
  358. } else {
  359. VCN FileClusters;
  360. VCN AllocationClusters;
  361. if (!FlagOn(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED)) {
  362. Scb->Header.ValidDataLength.QuadPart = AttrHeader->Form.Nonresident.ValidDataLength;
  363. Scb->Header.FileSize.QuadPart = AttrHeader->Form.Nonresident.FileSize;
  364. SetFlag(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED);
  365. if (FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  366. Scb->ValidDataToDisk = AttrHeader->Form.Nonresident.ValidDataLength;
  367. } else {
  368. Scb->ValidDataToDisk = 0;
  369. }
  370. }
  371. #ifdef SYSCACHE_DEBUG
  372. if (ScbIsBeingLogged( Scb )) {
  373. FsRtlLogSyscacheEvent( Scb, SCE_VDL_CHANGE, SCE_FLAG_UPDATE_FROM_DISK, Scb->Header.ValidDataLength.QuadPart, 1, 0 );
  374. }
  375. #endif
  376. Scb->Header.AllocationSize.QuadPart = AttrHeader->Form.Nonresident.AllocatedLength;
  377. Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
  378. //
  379. // Sanity Checks filesize lengths
  380. //
  381. if ((Scb->Header.FileSize.QuadPart < 0) ||
  382. (Scb->Header.ValidDataLength.QuadPart < 0 ) ||
  383. (Scb->Header.AllocationSize.QuadPart < 0) ||
  384. (Scb->Header.FileSize.QuadPart > Scb->Header.AllocationSize.QuadPart)) {
  385. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  386. }
  387. if (FlagOn( AttrHeader->Flags,
  388. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  389. Scb->TotalAllocated = AttrHeader->Form.Nonresident.TotalAllocated;
  390. if (Scb->TotalAllocated < 0) {
  391. Scb->TotalAllocated = 0;
  392. } else if (Scb->TotalAllocated > Scb->Header.AllocationSize.QuadPart) {
  393. Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
  394. }
  395. }
  396. ClearFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT );
  397. //
  398. // Get the size of the compression unit.
  399. //
  400. ASSERT((AttrHeader->Form.Nonresident.CompressionUnit == 0) ||
  401. (AttrHeader->Form.Nonresident.CompressionUnit == NTFS_CLUSTERS_PER_COMPRESSION) ||
  402. FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_SPARSE ));
  403. Scb->CompressionUnit = 0;
  404. Scb->CompressionUnitShift = 0;
  405. if ((AttrHeader->Form.Nonresident.CompressionUnit != 0) &&
  406. (AttrHeader->Form.Nonresident.CompressionUnit < 31)) {
  407. Scb->CompressionUnit = BytesFromClusters( Scb->Vcb,
  408. 1 << AttrHeader->Form.Nonresident.CompressionUnit );
  409. Scb->CompressionUnitShift = AttrHeader->Form.Nonresident.CompressionUnit;
  410. ASSERT( NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
  411. }
  412. //
  413. // Compute the clusters for the file and its allocation.
  414. //
  415. AllocationClusters = LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart );
  416. if (Scb->CompressionUnit == 0) {
  417. FileClusters = LlClustersFromBytes(Scb->Vcb, Scb->Header.FileSize.QuadPart);
  418. } else {
  419. FileClusters = Scb->Header.FileSize.QuadPart + Scb->CompressionUnit - 1;
  420. FileClusters &= ~((ULONG_PTR)Scb->CompressionUnit - 1);
  421. }
  422. //
  423. // If allocated clusters are greater than file clusters, mark
  424. // the Scb to truncate on close.
  425. //
  426. if (AllocationClusters > FileClusters) {
  427. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  428. }
  429. }
  430. //
  431. // Update compression information if this is not an index
  432. //
  433. if (Scb->AttributeTypeCode != $INDEX_ALLOCATION) {
  434. Scb->AttributeFlags = AttrHeader->Flags;
  435. if (FlagOn( AttrHeader->Flags,
  436. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  437. //
  438. // For sparse files indicate CC should flush when they're mapped
  439. // to keep reservations accurate
  440. //
  441. if (FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_SPARSE )) {
  442. SetFlag( Scb->Header.Flags2, FSRTL_FLAG2_PURGE_WHEN_MAPPED );
  443. }
  444. //
  445. // Only support compression on data streams.
  446. //
  447. if ((Scb->AttributeTypeCode != $DATA) &&
  448. FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  449. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  450. Scb->CompressionUnit = 0;
  451. Scb->CompressionUnitShift = 0;
  452. ClearFlag( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK );
  453. } else {
  454. ASSERT( NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
  455. //
  456. // Do not try to infer whether we are writing compressed or not
  457. // if we are actively changing the compression state.
  458. //
  459. if (!FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE )) {
  460. SetFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  461. if (!FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  462. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  463. }
  464. }
  465. //
  466. // If the attribute is resident, then we will use our current
  467. // default.
  468. //
  469. if (Scb->CompressionUnit == 0) {
  470. Scb->CompressionUnit = BytesFromClusters( Scb->Vcb, 1 << NTFS_CLUSTERS_PER_COMPRESSION );
  471. Scb->CompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
  472. while (Scb->CompressionUnit > Scb->Vcb->SparseFileUnit) {
  473. Scb->CompressionUnit >>= 1;
  474. Scb->CompressionUnitShift -= 1;
  475. }
  476. }
  477. }
  478. } else {
  479. //
  480. // If this file is NOT compressed or sparse, the WRITE_COMPRESSED flag
  481. // has no reason to be ON, irrespective of the REALLOCATE_ON_WRITE flag.
  482. // If we don't clear the flag here unconditionally, we can end up with Scbs with
  483. // WRITE_COMPRESSED flags switched on, but CompressionUnits of 0.
  484. //
  485. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  486. //
  487. // Make sure compression unit is 0
  488. //
  489. Scb->CompressionUnit = 0;
  490. Scb->CompressionUnitShift = 0;
  491. }
  492. }
  493. //
  494. // If the compression unit is non-zero or this is a resident file
  495. // then set the flag in the common header for the Modified page writer.
  496. //
  497. NtfsAcquireFsrtlHeader( Scb );
  498. if (NodeType( Scb ) == NTFS_NTC_SCB_DATA) {
  499. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  500. } else {
  501. Scb->Header.IsFastIoPossible = FastIoIsNotPossible;
  502. }
  503. NtfsReleaseFsrtlHeader( Scb );
  504. //
  505. // Set the flag indicating this is the data attribute.
  506. //
  507. if (Scb->AttributeTypeCode == $DATA
  508. && Scb->AttributeName.Length == 0) {
  509. SetFlag( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  510. } else {
  511. ClearFlag( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  512. }
  513. SetFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED );
  514. if (NtfsIsExclusiveScb(Scb)) {
  515. NtfsSnapshotScb( IrpContext, Scb );
  516. }
  517. } finally {
  518. DebugUnwind( NtfsUpdateScbFromAttribute );
  519. //
  520. // Cleanup the attribute context.
  521. //
  522. if (CleanupAttrContext) {
  523. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  524. }
  525. DebugTrace( -1, Dbg, ("NtfsUpdateScbFromAttribute: Exit\n") );
  526. }
  527. return;
  528. }
  529. BOOLEAN
  530. NtfsUpdateFcbInfoFromDisk (
  531. IN PIRP_CONTEXT IrpContext,
  532. IN BOOLEAN LoadSecurity,
  533. IN OUT PFCB Fcb,
  534. OUT POLD_SCB_SNAPSHOT UnnamedDataSizes OPTIONAL
  535. )
  536. /*++
  537. Routine Description:
  538. This routine is called to update an Fcb from the on-disk attributes
  539. for a file. We read the standard information and ea information.
  540. The first one must be present, we raise if not. The other does not
  541. have to exist. If this is not a directory, then we also need the
  542. size of the unnamed data attribute.
  543. Arguments:
  544. LoadSecurity - Indicates if we should load the security for this file
  545. if not already present.
  546. Fcb - This is the Fcb to update.
  547. UnnamedDataSizes - If specified, then we store the details of the unnamed
  548. data attribute as we encounter it.
  549. Return Value:
  550. TRUE - if we updated the unnamedatasizes
  551. --*/
  552. {
  553. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  554. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  555. BOOLEAN FoundEntry;
  556. BOOLEAN CorruptDisk = FALSE;
  557. BOOLEAN UpdatedNamedDataSizes = FALSE;
  558. PBCB Bcb = NULL;
  559. PDUPLICATED_INFORMATION Info;
  560. PAGED_CODE();
  561. DebugTrace( +1, Dbg, ("NtfsUpdateFcbInfoFromDisk: Entered\n") );
  562. NtfsInitializeAttributeContext( &AttrContext );
  563. //
  564. // Use a try-finally to facilitate cleanup.
  565. //
  566. try {
  567. //
  568. // Look for standard information. This routine assumes it must be
  569. // the first attribute.
  570. //
  571. if (FoundEntry = NtfsLookupAttribute( IrpContext,
  572. Fcb,
  573. &Fcb->FileReference,
  574. &AttrContext )) {
  575. //
  576. // Verify that we found the standard information attribute.
  577. //
  578. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  579. if (AttributeHeader->TypeCode != $STANDARD_INFORMATION) {
  580. try_return( CorruptDisk = TRUE );
  581. }
  582. } else {
  583. try_return( CorruptDisk = TRUE );
  584. }
  585. Info = &Fcb->Info;
  586. //
  587. // Copy out the standard information values.
  588. //
  589. {
  590. PSTANDARD_INFORMATION StandardInformation;
  591. StandardInformation = (PSTANDARD_INFORMATION) NtfsAttributeValue( AttributeHeader );
  592. Info->CreationTime = StandardInformation->CreationTime;
  593. Info->LastModificationTime = StandardInformation->LastModificationTime;
  594. Info->LastChangeTime = StandardInformation->LastChangeTime;
  595. Info->LastAccessTime = StandardInformation->LastAccessTime;
  596. Info->FileAttributes = StandardInformation->FileAttributes;
  597. if (AttributeHeader->Form.Resident.ValueLength >=
  598. sizeof(STANDARD_INFORMATION)) {
  599. Fcb->OwnerId = StandardInformation->OwnerId;
  600. Fcb->SecurityId = StandardInformation->SecurityId;
  601. Fcb->Usn = StandardInformation->Usn;
  602. if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_USN_DELETE )) {
  603. Fcb->Usn = 0;
  604. }
  605. SetFlag(Fcb->FcbState, FCB_STATE_LARGE_STD_INFO);
  606. }
  607. }
  608. Fcb->CurrentLastAccess = Info->LastAccessTime;
  609. //
  610. // We initialize the fields that describe the EaSize or the tag of a reparse point.
  611. // ReparsePointTag is a ULONG that is the union of PackedEaSize and Reserved.
  612. //
  613. Info->ReparsePointTag = 0;
  614. //
  615. // We get the FILE_NAME_INDEX_PRESENT bit by reading the
  616. // file record.
  617. //
  618. if (FlagOn( NtfsContainingFileRecord( &AttrContext )->Flags,
  619. FILE_FILE_NAME_INDEX_PRESENT )) {
  620. SetFlag( Info->FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  621. } else {
  622. ClearFlag( Info->FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  623. }
  624. //
  625. // Ditto for the VIEW_INDEX_PRESENT bit.
  626. //
  627. if (FlagOn( NtfsContainingFileRecord( &AttrContext )->Flags,
  628. FILE_VIEW_INDEX_PRESENT )) {
  629. SetFlag( Info->FileAttributes, DUP_VIEW_INDEX_PRESENT );
  630. } else {
  631. ClearFlag( Info->FileAttributes, DUP_VIEW_INDEX_PRESENT );
  632. }
  633. //
  634. // We now walk through all of the filename attributes, counting the
  635. // number of non-8dot3-only links.
  636. //
  637. Fcb->TotalLinks =
  638. Fcb->LinkCount = 0;
  639. FoundEntry = NtfsLookupNextAttributeByCode( IrpContext,
  640. Fcb,
  641. $FILE_NAME,
  642. &AttrContext );
  643. while (FoundEntry) {
  644. PFILE_NAME FileName;
  645. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  646. if (AttributeHeader->TypeCode != $FILE_NAME) {
  647. break;
  648. }
  649. FileName = (PFILE_NAME) NtfsAttributeValue( AttributeHeader );
  650. //
  651. // We increment the count as long as this is not a 8.3 link
  652. // only.
  653. //
  654. if (FileName->Flags != FILE_NAME_DOS) {
  655. Fcb->LinkCount += 1;
  656. Fcb->TotalLinks += 1;
  657. }
  658. //
  659. // Now look for the next link.
  660. //
  661. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  662. Fcb,
  663. &AttrContext );
  664. }
  665. //
  666. // There better be at least one unless this is a system file.
  667. //
  668. if ((Fcb->LinkCount == 0) &&
  669. (NtfsSegmentNumber( &Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER)) {
  670. try_return( CorruptDisk = TRUE );
  671. }
  672. //
  673. // If we are to load the security and it is not already present we
  674. // find the security attribute.
  675. //
  676. if (LoadSecurity && Fcb->SharedSecurity == NULL) {
  677. //
  678. // We have two sources of security descriptors. First, we have
  679. // the SecurityId that is present in a large $STANDARD_INFORMATION.
  680. // The other case is where we don't have such a security Id and must
  681. // retrieve it from the $SECURITY_DESCRIPTOR attribute
  682. //
  683. // In the case where we have the Id, we load it from the volume
  684. // cache or index.
  685. //
  686. if (FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO ) &&
  687. (Fcb->SecurityId != SECURITY_ID_INVALID) &&
  688. (Fcb->Vcb->SecurityDescriptorStream != NULL)) {
  689. ASSERT( Fcb->SharedSecurity == NULL );
  690. Fcb->SharedSecurity = NtfsCacheSharedSecurityBySecurityId( IrpContext,
  691. Fcb->Vcb,
  692. Fcb->SecurityId );
  693. ASSERT( Fcb->SharedSecurity != NULL );
  694. } else {
  695. PSECURITY_DESCRIPTOR SecurityDescriptor;
  696. ULONG SecurityDescriptorLength;
  697. //
  698. // We may have to walk forward to the security descriptor.
  699. //
  700. while (FoundEntry) {
  701. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  702. if (AttributeHeader->TypeCode == $SECURITY_DESCRIPTOR) {
  703. NtfsMapAttributeValue( IrpContext,
  704. Fcb,
  705. (PVOID *)&SecurityDescriptor,
  706. &SecurityDescriptorLength,
  707. &Bcb,
  708. &AttrContext );
  709. NtfsSetFcbSecurityFromDescriptor(
  710. IrpContext,
  711. Fcb,
  712. SecurityDescriptor,
  713. SecurityDescriptorLength,
  714. FALSE );
  715. //
  716. // If the security descriptor was resident then the Bcb field
  717. // in the attribute context was stored in the returned Bcb and
  718. // the Bcb in the attribute context was cleared. In that case
  719. // the resumption of the attribute search will fail because
  720. // this module using the Bcb field to determine if this
  721. // is the initial enumeration.
  722. //
  723. if (NtfsIsAttributeResident( AttributeHeader )) {
  724. NtfsFoundBcb( &AttrContext ) = Bcb;
  725. Bcb = NULL;
  726. }
  727. } else if (AttributeHeader->TypeCode > $SECURITY_DESCRIPTOR) {
  728. break;
  729. }
  730. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  731. Fcb,
  732. &AttrContext );
  733. }
  734. }
  735. }
  736. //
  737. // If this is not a directory, we need the file size.
  738. //
  739. if (!IsDirectory( Info ) && !IsViewIndex( Info )) {
  740. BOOLEAN FoundData = FALSE;
  741. //
  742. // Look for the unnamed data attribute.
  743. //
  744. while (FoundEntry) {
  745. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  746. if (AttributeHeader->TypeCode > $DATA) {
  747. break;
  748. }
  749. if ((AttributeHeader->TypeCode == $DATA) &&
  750. (AttributeHeader->NameLength == 0)) {
  751. //
  752. // This can vary depending whether the attribute is resident
  753. // or nonresident.
  754. //
  755. if (NtfsIsAttributeResident( AttributeHeader )) {
  756. //
  757. // Verify the resident value length.
  758. //
  759. if (AttributeHeader->Form.Resident.ValueLength > AttributeHeader->RecordLength - AttributeHeader->Form.Resident.ValueOffset) {
  760. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  761. }
  762. Info->AllocatedLength = AttributeHeader->Form.Resident.ValueLength;
  763. Info->FileSize = Info->AllocatedLength;
  764. ((ULONG)Info->AllocatedLength) = QuadAlign( (ULONG)(Info->AllocatedLength) );
  765. //
  766. // If the user passed in a ScbSnapshot, then copy the attribute
  767. // sizes to that. We use the trick of setting the low bit of the
  768. // attribute size to indicate a resident attribute.
  769. //
  770. if (ARGUMENT_PRESENT( UnnamedDataSizes )) {
  771. UnnamedDataSizes->TotalAllocated =
  772. UnnamedDataSizes->AllocationSize = Info->AllocatedLength;
  773. UnnamedDataSizes->FileSize = Info->FileSize;
  774. UnnamedDataSizes->ValidDataLength = Info->FileSize;
  775. UnnamedDataSizes->Resident = TRUE;
  776. UnnamedDataSizes->CompressionUnit = 0;
  777. UnnamedDataSizes->AttributeFlags = AttributeHeader->Flags;
  778. NtfsVerifySizesLongLong( UnnamedDataSizes );
  779. UpdatedNamedDataSizes = TRUE;
  780. }
  781. FoundData = TRUE;
  782. } else if (AttributeHeader->Form.Nonresident.LowestVcn == 0) {
  783. Info->AllocatedLength = AttributeHeader->Form.Nonresident.AllocatedLength;
  784. Info->FileSize = AttributeHeader->Form.Nonresident.FileSize;
  785. if (ARGUMENT_PRESENT( UnnamedDataSizes )) {
  786. UnnamedDataSizes->TotalAllocated =
  787. UnnamedDataSizes->AllocationSize = Info->AllocatedLength;
  788. UnnamedDataSizes->FileSize = Info->FileSize;
  789. UnnamedDataSizes->ValidDataLength = AttributeHeader->Form.Nonresident.ValidDataLength;
  790. UnnamedDataSizes->Resident = FALSE;
  791. UnnamedDataSizes->CompressionUnit = AttributeHeader->Form.Nonresident.CompressionUnit;
  792. NtfsVerifySizesLongLong( UnnamedDataSizes );
  793. //
  794. // Remember if it is compressed.
  795. //
  796. UnnamedDataSizes->AttributeFlags = AttributeHeader->Flags;
  797. UpdatedNamedDataSizes = TRUE;
  798. }
  799. if (FlagOn( AttributeHeader->Flags,
  800. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  801. Info->AllocatedLength = AttributeHeader->Form.Nonresident.TotalAllocated;
  802. if (ARGUMENT_PRESENT( UnnamedDataSizes )) {
  803. UnnamedDataSizes->TotalAllocated = Info->AllocatedLength;
  804. if (UnnamedDataSizes->TotalAllocated < 0) {
  805. UnnamedDataSizes->TotalAllocated = 0;
  806. } else if (UnnamedDataSizes->TotalAllocated > Info->AllocatedLength) {
  807. UnnamedDataSizes->TotalAllocated = Info->AllocatedLength;
  808. }
  809. }
  810. }
  811. FoundData = TRUE;
  812. }
  813. break;
  814. }
  815. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  816. Fcb,
  817. &AttrContext );
  818. }
  819. //
  820. // The following test is bad for the 5.0 support. Assume if someone is actually
  821. // trying to open the unnamed data attribute, that the right thing will happen.
  822. //
  823. //
  824. // if (!FoundData) {
  825. //
  826. // try_return( CorruptDisk = TRUE );
  827. // }
  828. } else {
  829. //
  830. // Since it is a directory, try to find the $INDEX_ROOT.
  831. //
  832. while (FoundEntry) {
  833. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  834. if (AttributeHeader->TypeCode > $INDEX_ROOT) {
  835. //
  836. // We thought this was a directory, yet it has now index
  837. // root. That's not a legal state to be in, so let's
  838. // take the corrupt disk path out of here.
  839. //
  840. ASSERT( FALSE );
  841. try_return( CorruptDisk = TRUE );
  842. break;
  843. }
  844. //
  845. // Look for encryption bit and store in Fcb.
  846. //
  847. if (AttributeHeader->TypeCode == $INDEX_ROOT) {
  848. if (FlagOn( AttributeHeader->Flags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  849. SetFlag( Fcb->FcbState, FCB_STATE_DIRECTORY_ENCRYPTED );
  850. }
  851. break;
  852. }
  853. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  854. Fcb,
  855. &AttrContext );
  856. }
  857. Info->AllocatedLength = 0;
  858. Info->FileSize = 0;
  859. }
  860. //
  861. // Now we look for a reparse point attribute. This one doesn't have to
  862. // be there. It may also not be resident.
  863. //
  864. while (FoundEntry) {
  865. PREPARSE_DATA_BUFFER ReparseInformation;
  866. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  867. if (AttributeHeader->TypeCode > $REPARSE_POINT) {
  868. break;
  869. } else if (AttributeHeader->TypeCode == $REPARSE_POINT) {
  870. if (NtfsIsAttributeResident( AttributeHeader )) {
  871. ReparseInformation = (PREPARSE_DATA_BUFFER) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  872. } else {
  873. ULONG Length;
  874. if (AttributeHeader->Form.Nonresident.FileSize > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) {
  875. NtfsRaiseStatus( IrpContext,STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  876. }
  877. NtfsMapAttributeValue( IrpContext,
  878. Fcb,
  879. (PVOID *)&ReparseInformation, // point to the value
  880. &Length,
  881. &Bcb,
  882. &AttrContext );
  883. }
  884. Info->ReparsePointTag = ReparseInformation->ReparseTag;
  885. break;
  886. }
  887. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  888. Fcb,
  889. &AttrContext );
  890. }
  891. //
  892. // Now we look for an Ea information attribute. This one doesn't have to
  893. // be there.
  894. //
  895. while (FoundEntry) {
  896. PEA_INFORMATION EaInformation;
  897. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  898. if (AttributeHeader->TypeCode > $EA_INFORMATION) {
  899. break;
  900. } else if (AttributeHeader->TypeCode == $EA_INFORMATION) {
  901. EaInformation = (PEA_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  902. Info->PackedEaSize = EaInformation->PackedEaSize;
  903. break;
  904. }
  905. FoundEntry = NtfsLookupNextAttributeByCode( IrpContext,
  906. Fcb,
  907. $EA_INFORMATION,
  908. &AttrContext );
  909. }
  910. //
  911. // Set the flag in the Fcb to indicate that we set these fields.
  912. //
  913. SetFlag( Fcb->FcbState, FCB_STATE_DUP_INITIALIZED );
  914. try_exit: NOTHING;
  915. } finally {
  916. DebugUnwind( NtfsUpdateFcbInfoFromDisk );
  917. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  918. NtfsUnpinBcb( IrpContext, &Bcb );
  919. DebugTrace( -1, Dbg, ("NtfsUpdateFcbInfoFromDisk: Exit\n") );
  920. }
  921. //
  922. // If we encountered a corrupt disk, we generate a popup and raise the file
  923. // corrupt error.
  924. //
  925. if (CorruptDisk) {
  926. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  927. }
  928. return UpdatedNamedDataSizes;
  929. }
  930. VOID
  931. NtfsCleanupAttributeContext (
  932. IN OUT PIRP_CONTEXT IrpContext,
  933. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT AttributeContext
  934. )
  935. /*++
  936. Routine Description:
  937. This routine is called to free any resources claimed within an enumeration
  938. context and to unpin mapped or pinned data.
  939. Arguments:
  940. IrpContext - context of the call
  941. AttributeContext - Pointer to the enumeration context to perform cleanup
  942. on.
  943. Return Value:
  944. None.
  945. --*/
  946. {
  947. UNREFERENCED_PARAMETER( IrpContext );
  948. PAGED_CODE();
  949. DebugTrace( +1, Dbg, ("NtfsCleanupAttributeContext\n") );
  950. //
  951. // TEMPCODE We need a call to cleanup any Scb's created.
  952. //
  953. //
  954. // Unpin any Bcb's pinned here.
  955. //
  956. NtfsUnpinBcb( IrpContext, &AttributeContext->FoundAttribute.Bcb );
  957. NtfsUnpinBcb( IrpContext, &AttributeContext->AttributeList.Bcb );
  958. NtfsUnpinBcb( IrpContext, &AttributeContext->AttributeList.NonresidentListBcb );
  959. //
  960. // Originally, we zeroed the entire context at this point. This is
  961. // wildly inefficient since the context is either deallocated soon thereafter
  962. // or is initialized again.
  963. //
  964. // RtlZeroMemory( AttributeContext, sizeof(ATTRIBUTE_ENUMERATION_CONTEXT) );
  965. //
  966. // Set entire contents to -1 (and reset Bcb's to NULL) to verify
  967. // that no one reuses this data structure
  968. #if DBG
  969. RtlFillMemory( AttributeContext, sizeof( *AttributeContext ), -1 );
  970. AttributeContext->FoundAttribute.Bcb = NULL;
  971. AttributeContext->AttributeList.Bcb = NULL;
  972. AttributeContext->AttributeList.NonresidentListBcb = NULL;
  973. #endif
  974. DebugTrace( -1, Dbg, ("NtfsCleanupAttributeContext -> VOID\n") );
  975. return;
  976. }
  977. BOOLEAN
  978. NtfsWriteFileSizes (
  979. IN PIRP_CONTEXT IrpContext,
  980. IN PSCB Scb,
  981. IN PLONGLONG ValidDataLength,
  982. IN BOOLEAN AdvanceOnly,
  983. IN BOOLEAN LogIt,
  984. IN BOOLEAN RollbackMemStructures
  985. )
  986. /*++
  987. Routine Description:
  988. This routine is called to modify the filesize and valid data size
  989. on the disk from the Scb.
  990. Arguments:
  991. Scb - Scb whose attribute is being modified.
  992. ValidDataLength - Supplies pointer to the new desired ValidDataLength
  993. AdvanceOnly - TRUE if the valid data length should be set only if
  994. greater than the current value on disk. FALSE if
  995. the valid data length should be set only if
  996. less than the current value on disk.
  997. LogIt - Indicates whether we should log this change.
  998. RollbackMemStructures - If true then there had better be snapshots to support doing this
  999. if not this indicates we're transferring persisted in memory
  1000. changes to disk. I.e a the final writefilesizes at close time
  1001. or the check_attribute_sizes related calls
  1002. Return Value:
  1003. TRUE if a log record was written out
  1004. --*/
  1005. {
  1006. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1007. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  1008. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1009. NEW_ATTRIBUTE_SIZES OldAttributeSizes;
  1010. NEW_ATTRIBUTE_SIZES NewAttributeSizes;
  1011. ULONG LogRecordSize = SIZEOF_PARTIAL_ATTRIBUTE_SIZES;
  1012. BOOLEAN SparseAllocation = FALSE;
  1013. BOOLEAN UpdateMft = FALSE;
  1014. BOOLEAN Logged = FALSE;
  1015. PAGED_CODE();
  1016. UNREFERENCED_PARAMETER( RollbackMemStructures );
  1017. //
  1018. // Return immediately if the volume is locked unless we have grown the Mft or the Bitmap.
  1019. // In some cases the user can grow the Mft with the volume locked (i.e. add
  1020. // a Usn journal).
  1021. //
  1022. if (FlagOn( Scb->Vcb->VcbState, VCB_STATE_LOCKED ) &&
  1023. (Scb != Scb->Vcb->MftScb) && (Scb != Scb->Vcb->BitmapScb)) {
  1024. return Logged;
  1025. }
  1026. DebugTrace( +1, Dbg, ("NtfsWriteFileSizes: Entered\n") );
  1027. ASSERT( (Scb->ScbSnapshot != NULL) || !RollbackMemStructures );
  1028. //
  1029. // Use a try_finally to facilitate cleanup.
  1030. //
  1031. try {
  1032. //
  1033. // Find the attribute on the disk.
  1034. //
  1035. NtfsInitializeAttributeContext( &AttrContext );
  1036. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  1037. //
  1038. // Pull the pointers out of the attribute context.
  1039. //
  1040. FileRecord = NtfsContainingFileRecord( &AttrContext );
  1041. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  1042. //
  1043. // Check if this is a resident attribute, and if it is then we only
  1044. // want to assert that the file sizes match and then return to
  1045. // our caller
  1046. //
  1047. if (NtfsIsAttributeResident( AttributeHeader )) {
  1048. try_return( NOTHING );
  1049. }
  1050. //
  1051. // Remember the existing values.
  1052. //
  1053. OldAttributeSizes.TotalAllocated =
  1054. OldAttributeSizes.AllocationSize = AttributeHeader->Form.Nonresident.AllocatedLength;
  1055. OldAttributeSizes.ValidDataLength = AttributeHeader->Form.Nonresident.ValidDataLength;
  1056. OldAttributeSizes.FileSize = AttributeHeader->Form.Nonresident.FileSize;
  1057. NtfsVerifySizesLongLong( &OldAttributeSizes );
  1058. if (FlagOn( AttributeHeader->Flags,
  1059. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  1060. SparseAllocation = TRUE;
  1061. OldAttributeSizes.TotalAllocated = AttributeHeader->Form.Nonresident.TotalAllocated;
  1062. }
  1063. //
  1064. // Copy these values.
  1065. //
  1066. NewAttributeSizes = OldAttributeSizes;
  1067. //
  1068. // We only want to modify the sizes if the current thread owns the
  1069. // EOF. The exception is the TotalAllocated field for a compressed file.
  1070. // Otherwise this transaction might update the file size on disk at the
  1071. // same time an operation on EOF might roll back the Scb value. The
  1072. // two resulting numbers would conflict.
  1073. //
  1074. // Use the same test that NtfsRestoreScbSnapshots uses.
  1075. //
  1076. /*
  1077. //
  1078. // Disable for now - fix for attribute lists in server release
  1079. //
  1080. ASSERT( !RollbackMemStructures ||
  1081. !NtfsSnapshotFileSizesTest( IrpContext, Scb ) ||
  1082. (Scb->ScbSnapshot->OwnerIrpContext == IrpContext) ||
  1083. (Scb->ScbSnapshot->OwnerIrpContext == IrpContext->TopLevelIrpContext));
  1084. */
  1085. if (((Scb->ScbSnapshot != NULL) &&
  1086. ((Scb->ScbSnapshot->OwnerIrpContext == IrpContext) ||
  1087. (Scb->ScbSnapshot->OwnerIrpContext == IrpContext->TopLevelIrpContext))) ||
  1088. (!RollbackMemStructures && NtfsSnapshotFileSizesTest( IrpContext, Scb ))) {
  1089. //
  1090. // Check if we will be modifying the valid data length on
  1091. // disk. Don't acquire this for the paging file in case the
  1092. // current code block needs to be paged in.
  1093. //
  1094. if (!FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  1095. NtfsAcquireFsrtlHeader(Scb);
  1096. }
  1097. if ((AdvanceOnly
  1098. && (*ValidDataLength > OldAttributeSizes.ValidDataLength))
  1099. || (!AdvanceOnly
  1100. && (*ValidDataLength < OldAttributeSizes.ValidDataLength))) {
  1101. //
  1102. // Copy the valid data length into the new size structure.
  1103. //
  1104. NewAttributeSizes.ValidDataLength = *ValidDataLength;
  1105. UpdateMft = TRUE;
  1106. }
  1107. //
  1108. // Now check if we're modifying the filesize.
  1109. //
  1110. if (Scb->Header.FileSize.QuadPart != OldAttributeSizes.FileSize) {
  1111. NewAttributeSizes.FileSize = Scb->Header.FileSize.QuadPart;
  1112. UpdateMft = TRUE;
  1113. }
  1114. if (!FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  1115. NtfsReleaseFsrtlHeader(Scb);
  1116. }
  1117. //
  1118. // Finally, update the allocated length from the Scb if it is different.
  1119. //
  1120. if (Scb->Header.AllocationSize.QuadPart != AttributeHeader->Form.Nonresident.AllocatedLength) {
  1121. NewAttributeSizes.AllocationSize = Scb->Header.AllocationSize.QuadPart;
  1122. UpdateMft = TRUE;
  1123. }
  1124. }
  1125. //
  1126. // If this is compressed then check if totally allocated has changed.
  1127. //
  1128. if (SparseAllocation) {
  1129. LogRecordSize = SIZEOF_FULL_ATTRIBUTE_SIZES;
  1130. if (Scb->TotalAllocated != OldAttributeSizes.TotalAllocated) {
  1131. ASSERT( !RollbackMemStructures || (Scb->ScbSnapshot != NULL) );
  1132. NewAttributeSizes.TotalAllocated = Scb->TotalAllocated;
  1133. UpdateMft = TRUE;
  1134. }
  1135. }
  1136. //
  1137. // Continue on if we need to update the Mft.
  1138. //
  1139. if (UpdateMft) {
  1140. //
  1141. // Pin the attribute.
  1142. //
  1143. NtfsPinMappedAttribute( IrpContext,
  1144. Scb->Vcb,
  1145. &AttrContext );
  1146. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  1147. if (NewAttributeSizes.ValidDataLength > NewAttributeSizes.FileSize) {
  1148. // ASSERT(XxLeq(NewAttributeSizes.ValidDataLength,NewAttributeSizes.FileSize));
  1149. NewAttributeSizes.ValidDataLength = NewAttributeSizes.FileSize;
  1150. }
  1151. ASSERT(NewAttributeSizes.FileSize <= NewAttributeSizes.AllocationSize);
  1152. ASSERT(NewAttributeSizes.ValidDataLength <= NewAttributeSizes.AllocationSize);
  1153. NtfsVerifySizesLongLong( &NewAttributeSizes );
  1154. //
  1155. // Log this change to the attribute header.
  1156. //
  1157. if (LogIt) {
  1158. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  1159. Scb->Vcb->MftScb,
  1160. NtfsFoundBcb( &AttrContext ),
  1161. SetNewAttributeSizes,
  1162. &NewAttributeSizes,
  1163. LogRecordSize,
  1164. SetNewAttributeSizes,
  1165. &OldAttributeSizes,
  1166. LogRecordSize,
  1167. NtfsMftOffset( &AttrContext ),
  1168. PtrOffset( FileRecord, AttributeHeader ),
  1169. 0,
  1170. Scb->Vcb->BytesPerFileRecordSegment );
  1171. Logged = TRUE;
  1172. } else {
  1173. CcSetDirtyPinnedData( NtfsFoundBcb( &AttrContext ), NULL );
  1174. }
  1175. AttributeHeader->Form.Nonresident.AllocatedLength = NewAttributeSizes.AllocationSize;
  1176. AttributeHeader->Form.Nonresident.FileSize = NewAttributeSizes.FileSize;
  1177. AttributeHeader->Form.Nonresident.ValidDataLength = NewAttributeSizes.ValidDataLength;
  1178. //
  1179. // Don't modify the total allocated field unless there is an actual field for it.
  1180. //
  1181. if (SparseAllocation &&
  1182. ((AttributeHeader->NameOffset >= SIZEOF_FULL_NONRES_ATTR_HEADER) ||
  1183. ((AttributeHeader->NameOffset == 0) &&
  1184. (AttributeHeader->Form.Nonresident.MappingPairsOffset >= SIZEOF_FULL_NONRES_ATTR_HEADER)))) {
  1185. AttributeHeader->Form.Nonresident.TotalAllocated = NewAttributeSizes.TotalAllocated;
  1186. }
  1187. }
  1188. try_exit: NOTHING;
  1189. } finally {
  1190. DebugUnwind( NtfsWriteFileSizes );
  1191. //
  1192. // Cleanup the attribute context.
  1193. //
  1194. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1195. DebugTrace( -1, Dbg, ("NtfsWriteFileSizes: Exit\n") );
  1196. }
  1197. return Logged;
  1198. }
  1199. VOID
  1200. NtfsUpdateStandardInformation (
  1201. IN PIRP_CONTEXT IrpContext,
  1202. IN PFCB Fcb
  1203. )
  1204. /*++
  1205. Routine Description:
  1206. This routine is called to update the standard information attribute
  1207. for a file from the information in the Fcb. The fields being modified
  1208. are the time fields and the file attributes.
  1209. Arguments:
  1210. Fcb - Fcb for the file to modify.
  1211. Return Value:
  1212. None
  1213. --*/
  1214. {
  1215. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1216. STANDARD_INFORMATION StandardInformation;
  1217. ULONG Length;
  1218. PAGED_CODE();
  1219. DebugTrace( +1, Dbg, ("NtfsUpdateStandardInformation: Entered\n") );
  1220. //
  1221. // Return immediately if the volume is mounted readonly.
  1222. //
  1223. if (NtfsIsVolumeReadOnly( Fcb->Vcb )) {
  1224. return;
  1225. }
  1226. //
  1227. // Use a try-finally to cleanup the attribute context.
  1228. //
  1229. try {
  1230. //
  1231. // Initialize the context structure.
  1232. //
  1233. NtfsInitializeAttributeContext( &AttrContext );
  1234. //
  1235. // Locate the standard information, it must be there.
  1236. //
  1237. if (!NtfsLookupAttributeByCode( IrpContext,
  1238. Fcb,
  1239. &Fcb->FileReference,
  1240. $STANDARD_INFORMATION,
  1241. &AttrContext )) {
  1242. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  1243. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1244. }
  1245. Length = NtfsFoundAttribute( &AttrContext )->Form.Resident.ValueLength;
  1246. //
  1247. // Copy the existing standard information to our buffer.
  1248. //
  1249. RtlCopyMemory( &StandardInformation,
  1250. NtfsAttributeValue( NtfsFoundAttribute( &AttrContext )),
  1251. Length);
  1252. //
  1253. // Since we are updating standard information, make sure the last
  1254. // access time is up-to-date.
  1255. //
  1256. if (Fcb->Info.LastAccessTime != Fcb->CurrentLastAccess) {
  1257. Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
  1258. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
  1259. }
  1260. //
  1261. // No need to update last access standard information later.
  1262. //
  1263. ClearFlag( Fcb->InfoFlags, FCB_INFO_UPDATE_LAST_ACCESS );
  1264. //
  1265. // Change the relevant time fields.
  1266. //
  1267. StandardInformation.CreationTime = Fcb->Info.CreationTime;
  1268. StandardInformation.LastModificationTime = Fcb->Info.LastModificationTime;
  1269. StandardInformation.LastChangeTime = Fcb->Info.LastChangeTime;
  1270. StandardInformation.LastAccessTime = Fcb->Info.LastAccessTime;
  1271. StandardInformation.FileAttributes = Fcb->Info.FileAttributes;
  1272. //
  1273. // We clear the directory bit.
  1274. //
  1275. ClearFlag( StandardInformation.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  1276. //
  1277. // Fill in the new fields if necessary.
  1278. //
  1279. if (FlagOn(Fcb->FcbState, FCB_STATE_LARGE_STD_INFO)) {
  1280. StandardInformation.ClassId = 0;
  1281. StandardInformation.OwnerId = Fcb->OwnerId;
  1282. StandardInformation.SecurityId = Fcb->SecurityId;
  1283. StandardInformation.Usn = Fcb->Usn;
  1284. }
  1285. //
  1286. // Call to change the attribute value.
  1287. //
  1288. NtfsChangeAttributeValue( IrpContext,
  1289. Fcb,
  1290. 0,
  1291. &StandardInformation,
  1292. Length,
  1293. FALSE,
  1294. FALSE,
  1295. FALSE,
  1296. FALSE,
  1297. &AttrContext );
  1298. } finally {
  1299. DebugUnwind( NtfsUpdateStandadInformation );
  1300. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1301. DebugTrace( -1, Dbg, ("NtfsUpdateStandardInformation: Exit\n") );
  1302. }
  1303. return;
  1304. }
  1305. VOID
  1306. NtfsGrowStandardInformation (
  1307. IN PIRP_CONTEXT IrpContext,
  1308. IN PFCB Fcb
  1309. )
  1310. /*++
  1311. Routine Description:
  1312. This routine is called to grow and update the standard information
  1313. attribute for a file from the information in the Fcb.
  1314. Arguments:
  1315. Fcb - Fcb for the file to modify.
  1316. Return Value:
  1317. None
  1318. --*/
  1319. {
  1320. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1321. STANDARD_INFORMATION StandardInformation;
  1322. PAGED_CODE();
  1323. DebugTrace( +1, Dbg, ("NtfsGrowStandardInformation: Entered\n") );
  1324. //
  1325. // Use a try-finally to cleanup the attribute context.
  1326. //
  1327. try {
  1328. //
  1329. // Initialize the context structure.
  1330. //
  1331. NtfsInitializeAttributeContext( &AttrContext );
  1332. //
  1333. // Locate the standard information, it must be there.
  1334. //
  1335. if (!NtfsLookupAttributeByCode( IrpContext,
  1336. Fcb,
  1337. &Fcb->FileReference,
  1338. $STANDARD_INFORMATION,
  1339. &AttrContext )) {
  1340. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  1341. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1342. }
  1343. if (NtfsFoundAttribute( &AttrContext )->Form.Resident.ValueLength ==
  1344. SIZEOF_OLD_STANDARD_INFORMATION) {
  1345. //
  1346. // Copy the existing standard information to our buffer.
  1347. //
  1348. RtlCopyMemory( &StandardInformation,
  1349. NtfsAttributeValue( NtfsFoundAttribute( &AttrContext )),
  1350. SIZEOF_OLD_STANDARD_INFORMATION);
  1351. RtlZeroMemory((PCHAR) &StandardInformation +
  1352. SIZEOF_OLD_STANDARD_INFORMATION,
  1353. sizeof( STANDARD_INFORMATION) -
  1354. SIZEOF_OLD_STANDARD_INFORMATION);
  1355. }
  1356. //
  1357. // Since we are updating standard information, make sure the last
  1358. // access time is up-to-date.
  1359. //
  1360. if (Fcb->Info.LastAccessTime != Fcb->CurrentLastAccess) {
  1361. Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
  1362. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
  1363. }
  1364. //
  1365. // Change the relevant time fields.
  1366. //
  1367. StandardInformation.CreationTime = Fcb->Info.CreationTime;
  1368. StandardInformation.LastModificationTime = Fcb->Info.LastModificationTime;
  1369. StandardInformation.LastChangeTime = Fcb->Info.LastChangeTime;
  1370. StandardInformation.LastAccessTime = Fcb->Info.LastAccessTime;
  1371. StandardInformation.FileAttributes = Fcb->Info.FileAttributes;
  1372. //
  1373. // We clear the directory bit.
  1374. //
  1375. ClearFlag( StandardInformation.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  1376. //
  1377. // Fill in the new fields.
  1378. //
  1379. StandardInformation.ClassId = 0;
  1380. StandardInformation.OwnerId = Fcb->OwnerId;
  1381. StandardInformation.SecurityId = Fcb->SecurityId;
  1382. StandardInformation.Usn = Fcb->Usn;
  1383. //
  1384. // Call to change the attribute value.
  1385. //
  1386. NtfsChangeAttributeValue( IrpContext,
  1387. Fcb,
  1388. 0,
  1389. &StandardInformation,
  1390. sizeof( STANDARD_INFORMATION),
  1391. TRUE,
  1392. FALSE,
  1393. FALSE,
  1394. FALSE,
  1395. &AttrContext );
  1396. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  1397. SetFlag( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO );
  1398. } finally {
  1399. DebugUnwind( NtfsGrowStandadInformation );
  1400. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1401. DebugTrace( -1, Dbg, ("NtfsGrowStandardInformation: Exit\n") );
  1402. }
  1403. return;
  1404. }
  1405. BOOLEAN
  1406. NtfsLookupEntry (
  1407. IN PIRP_CONTEXT IrpContext,
  1408. IN PSCB ParentScb,
  1409. IN BOOLEAN IgnoreCase,
  1410. IN OUT PUNICODE_STRING Name,
  1411. IN OUT PFILE_NAME *FileNameAttr,
  1412. IN OUT PUSHORT FileNameAttrLength,
  1413. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  1414. OUT PINDEX_ENTRY *IndexEntry,
  1415. OUT PBCB *IndexEntryBcb,
  1416. OUT PINDEX_CONTEXT IndexContext OPTIONAL
  1417. )
  1418. /*++
  1419. Routine Description:
  1420. This routine is called to look up a particular file name in a directory.
  1421. It takes a single component name and a parent Scb to search in.
  1422. To do the search, we need to construct a FILE_NAME attribute.
  1423. We use a reusable buffer to do this, to avoid constantly allocating
  1424. and deallocating pool. We try to keep this larger than we will ever need.
  1425. When we find a match on disk, we copy over the name we were called with so
  1426. we have a record of the actual case on the disk. In this way we can
  1427. be case perserving.
  1428. Arguments:
  1429. ParentScb - This is the Scb for the parent directory.
  1430. IgnoreCase - Indicates if we should ignore case while searching through
  1431. the index.
  1432. Name - This is the path component to search for. We will overwrite this
  1433. in place if a match is found.
  1434. FileNameAttr - Address of the buffer we will use to create the file name
  1435. attribute. We will free this buffer and allocate a new buffer
  1436. if needed.
  1437. FileNameAttrLength - This is the length of the FileNameAttr buffer above.
  1438. QuickIndex - If specified, supplies a pointer to a quik lookup structure
  1439. to be updated by this routine.
  1440. IndexEntry - Address to store the cache address of the matching entry.
  1441. IndexEntryBcb - Address to store the Bcb for the IndexEntry above.
  1442. IndexContext - Initialized IndexContext used for the lookup. Can be used
  1443. later when inserting an entry on a miss.
  1444. Return Value:
  1445. BOOLEAN - TRUE if a match was found, FALSE otherwise.
  1446. --*/
  1447. {
  1448. BOOLEAN FoundEntry;
  1449. USHORT Size;
  1450. PAGED_CODE();
  1451. DebugTrace( +1, Dbg, ("NtfsLookupEntry: Entered\n") );
  1452. //
  1453. // We compute the size of the buffer needed to build the filename
  1454. // attribute. If the current buffer is too small we deallocate it
  1455. // and allocate a new one. We always allocate twice the size we
  1456. // need in order to minimize the number of allocations.
  1457. //
  1458. Size = (USHORT)(sizeof( FILE_NAME ) + Name->Length - sizeof(WCHAR));
  1459. if (Size > *FileNameAttrLength) {
  1460. if (*FileNameAttr != NULL) {
  1461. DebugTrace( 0, Dbg, ("Deallocating previous file name attribute buffer\n") );
  1462. NtfsFreePool( *FileNameAttr );
  1463. *FileNameAttr = NULL;
  1464. }
  1465. *FileNameAttr = NtfsAllocatePool(PagedPool, Size << 1 );
  1466. *FileNameAttrLength = Size << 1;
  1467. }
  1468. //
  1469. // We build the filename attribute. If this operation is ignore case,
  1470. // we upcase the expression in the filename attribute.
  1471. //
  1472. NtfsBuildFileNameAttribute( IrpContext,
  1473. &ParentScb->Fcb->FileReference,
  1474. *Name,
  1475. 0,
  1476. *FileNameAttr );
  1477. //
  1478. // Now we call the index routine to perform the search.
  1479. //
  1480. FoundEntry = NtfsFindIndexEntry( IrpContext,
  1481. ParentScb,
  1482. *FileNameAttr,
  1483. IgnoreCase,
  1484. QuickIndex,
  1485. IndexEntryBcb,
  1486. IndexEntry,
  1487. IndexContext );
  1488. //
  1489. // We always restore the name in the filename attribute to the original
  1490. // name in case we upcased it in the lookup.
  1491. //
  1492. if (IgnoreCase) {
  1493. RtlCopyMemory( (*FileNameAttr)->FileName,
  1494. Name->Buffer,
  1495. Name->Length );
  1496. }
  1497. DebugTrace( -1, Dbg, ("NtfsLookupEntry: Exit -> %04x\n", FoundEntry) );
  1498. return FoundEntry;
  1499. }
  1500. VOID
  1501. NtfsCreateAttributeWithValue (
  1502. IN PIRP_CONTEXT IrpContext,
  1503. IN PFCB Fcb,
  1504. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  1505. IN PUNICODE_STRING AttributeName OPTIONAL,
  1506. IN PVOID Value OPTIONAL,
  1507. IN ULONG ValueLength,
  1508. IN USHORT AttributeFlags,
  1509. IN PFILE_REFERENCE WhereIndexed OPTIONAL,
  1510. IN BOOLEAN LogIt,
  1511. OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  1512. )
  1513. /*++
  1514. Routine Description:
  1515. This routine creates the specified attribute with the specified value,
  1516. and returns a description of it via the attribute context. If no
  1517. value is specified, then the attribute is created with the specified
  1518. number of zero bytes.
  1519. On successful return, it is up to the caller to clean up the attribute
  1520. context.
  1521. Arguments:
  1522. Fcb - Current file.
  1523. AttributeTypeCode - Type code of the attribute to create.
  1524. AttributeName - Optional name for attribute.
  1525. Value - Pointer to the buffer containing the desired attribute value,
  1526. or a NULL if zeros are desired.
  1527. ValueLength - Length of value in bytes.
  1528. AttributeFlags - Desired flags for the created attribute.
  1529. WhereIndexed - Optionally supplies the file reference to the file where
  1530. this attribute is indexed.
  1531. LogIt - Most callers should specify TRUE, to have the change logged. However,
  1532. we can specify FALSE if we are creating a new file record, and
  1533. will be logging the entire new file record.
  1534. Context - A handle to the created attribute. This must be cleaned up upon
  1535. return. Callers who may have made an attribute nonresident may
  1536. not count on accessing the created attribute via this context upon
  1537. return.
  1538. Return Value:
  1539. None.
  1540. --*/
  1541. {
  1542. UCHAR AttributeBuffer[SIZEOF_FULL_NONRES_ATTR_HEADER];
  1543. ULONG RecordOffset;
  1544. PATTRIBUTE_RECORD_HEADER Attribute;
  1545. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1546. ULONG SizeNeeded;
  1547. ULONG AttrSizeNeeded;
  1548. PVCB Vcb;
  1549. ULONG Passes = 0;
  1550. ASSERT_IRP_CONTEXT( IrpContext );
  1551. ASSERT_FCB( Fcb );
  1552. PAGED_CODE();
  1553. ASSERT( (AttributeFlags == 0) ||
  1554. (AttributeTypeCode == $INDEX_ROOT) ||
  1555. NtfsIsTypeCodeCompressible( AttributeTypeCode ));
  1556. Vcb = Fcb->Vcb;
  1557. DebugTrace( +1, Dbg, ("NtfsCreateAttributeWithValue\n") );
  1558. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  1559. DebugTrace( 0, Dbg, ("ValueLength = %08lx\n", ValueLength) );
  1560. //
  1561. // Clear out the invalid attribute flags for this volume.
  1562. //
  1563. ClearFlag( AttributeFlags, ~Vcb->AttributeFlagsMask );
  1564. //
  1565. // Calculate the size needed for this attribute
  1566. //
  1567. SizeNeeded = SIZEOF_RESIDENT_ATTRIBUTE_HEADER + QuadAlign( ValueLength ) +
  1568. (ARGUMENT_PRESENT( AttributeName ) ?
  1569. QuadAlign( AttributeName->Length ) : 0);
  1570. //
  1571. // Loop until we find all the space we need.
  1572. //
  1573. do {
  1574. //
  1575. // Reinitialize context if this is not the first pass.
  1576. //
  1577. if (Passes != 0) {
  1578. NtfsCleanupAttributeContext( IrpContext, Context );
  1579. NtfsInitializeAttributeContext( Context );
  1580. }
  1581. Passes += 1;
  1582. ASSERT( Passes < 5 );
  1583. //
  1584. // If the attribute is not indexed, then we will position to the
  1585. // insertion point by type code and name.
  1586. //
  1587. if (!ARGUMENT_PRESENT( WhereIndexed )) {
  1588. if (NtfsLookupAttributeByName( IrpContext,
  1589. Fcb,
  1590. &Fcb->FileReference,
  1591. AttributeTypeCode,
  1592. AttributeName,
  1593. NULL,
  1594. FALSE,
  1595. Context )) {
  1596. DebugTrace( 0, 0,
  1597. ("Nonindexed attribute already exists, TypeCode = %08lx\n",
  1598. AttributeTypeCode ));
  1599. ASSERTMSG("Nonindexed attribute already exists, About to raise corrupt ", FALSE);
  1600. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1601. }
  1602. //
  1603. // Check here if the attribute needs to be nonresident and if so just
  1604. // pass this off.
  1605. //
  1606. FileRecord = NtfsContainingFileRecord(Context);
  1607. if ((SizeNeeded > (FileRecord->BytesAvailable - FileRecord->FirstFreeByte)) &&
  1608. (SizeNeeded >= Vcb->BigEnoughToMove) &&
  1609. !FlagOn( NtfsGetAttributeDefinition( Vcb,
  1610. AttributeTypeCode)->Flags,
  1611. ATTRIBUTE_DEF_MUST_BE_RESIDENT)) {
  1612. NtfsCreateNonresidentWithValue( IrpContext,
  1613. Fcb,
  1614. AttributeTypeCode,
  1615. AttributeName,
  1616. Value,
  1617. ValueLength,
  1618. AttributeFlags,
  1619. FALSE,
  1620. NULL,
  1621. LogIt,
  1622. Context );
  1623. return;
  1624. }
  1625. //
  1626. // Otherwise, if the attribute is indexed, then we position by the
  1627. // attribute value.
  1628. //
  1629. } else {
  1630. ASSERT(ARGUMENT_PRESENT(Value));
  1631. if (NtfsLookupAttributeByValue( IrpContext,
  1632. Fcb,
  1633. &Fcb->FileReference,
  1634. AttributeTypeCode,
  1635. Value,
  1636. ValueLength,
  1637. Context )) {
  1638. DebugTrace( 0, 0,
  1639. ("Indexed attribute already exists, TypeCode = %08lx\n",
  1640. AttributeTypeCode ));
  1641. ASSERTMSG("Indexed attribute already exists, About to raise corrupt ", FALSE);
  1642. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1643. }
  1644. }
  1645. //
  1646. // If this attribute is being positioned in the base file record and
  1647. // there is an attribute list then we need to ask for enough space
  1648. // for the attribute list entry now.
  1649. //
  1650. FileRecord = NtfsContainingFileRecord( Context );
  1651. Attribute = NtfsFoundAttribute( Context );
  1652. AttrSizeNeeded = SizeNeeded;
  1653. if (Context->AttributeList.Bcb != NULL
  1654. && (ULONG_PTR) FileRecord <= (ULONG_PTR) Context->AttributeList.AttributeList
  1655. && (ULONG_PTR) Attribute >= (ULONG_PTR) Context->AttributeList.AttributeList) {
  1656. //
  1657. // If the attribute list is non-resident then add a fudge factor of
  1658. // 16 bytes for any new retrieval information.
  1659. //
  1660. if (NtfsIsAttributeResident( Context->AttributeList.AttributeList )) {
  1661. AttrSizeNeeded += QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  1662. + (ARGUMENT_PRESENT( AttributeName ) ?
  1663. (ULONG) AttributeName->Length :
  1664. sizeof( WCHAR )));
  1665. } else {
  1666. AttrSizeNeeded += 0x10;
  1667. }
  1668. }
  1669. //
  1670. // Ask for the space we need.
  1671. //
  1672. } while (!NtfsGetSpaceForAttribute( IrpContext, Fcb, AttrSizeNeeded, Context ));
  1673. //
  1674. // Now point to the file record and calculate the record offset where
  1675. // our attribute will go. And point to our local buffer.
  1676. //
  1677. RecordOffset = (ULONG)((PCHAR)NtfsFoundAttribute(Context) - (PCHAR)FileRecord);
  1678. Attribute = (PATTRIBUTE_RECORD_HEADER)AttributeBuffer;
  1679. if (RecordOffset >= Fcb->Vcb->BytesPerFileRecordSegment) {
  1680. ASSERTMSG("RecordOffset beyond FRS size, About to raise corrupt ", FALSE);
  1681. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1682. }
  1683. RtlZeroMemory( Attribute, SIZEOF_RESIDENT_ATTRIBUTE_HEADER );
  1684. Attribute->TypeCode = AttributeTypeCode;
  1685. Attribute->RecordLength = SizeNeeded;
  1686. Attribute->FormCode = RESIDENT_FORM;
  1687. if (ARGUMENT_PRESENT(AttributeName)) {
  1688. ASSERT( AttributeName->Length <= 0x1FF );
  1689. Attribute->NameLength = (UCHAR)(AttributeName->Length / sizeof(WCHAR));
  1690. Attribute->NameOffset = (USHORT)SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  1691. }
  1692. Attribute->Flags = AttributeFlags;
  1693. Attribute->Instance = FileRecord->NextAttributeInstance;
  1694. //
  1695. // If someone repeatedly adds and removes attributes from a file record we could
  1696. // hit a case where the sequence number will overflow. In this case we
  1697. // want to scan the file record and find an earlier free instance number.
  1698. //
  1699. if (Attribute->Instance > NTFS_CHECK_INSTANCE_ROLLOVER) {
  1700. Attribute->Instance = NtfsScanForFreeInstance( IrpContext, Vcb, FileRecord );
  1701. }
  1702. Attribute->Form.Resident.ValueLength = ValueLength;
  1703. Attribute->Form.Resident.ValueOffset =
  1704. (USHORT)(SIZEOF_RESIDENT_ATTRIBUTE_HEADER +
  1705. QuadAlign( Attribute->NameLength << 1) );
  1706. //
  1707. // If this attribute is indexed, then we have to set the right flag
  1708. // and update the file record reference count.
  1709. //
  1710. if (ARGUMENT_PRESENT(WhereIndexed)) {
  1711. Attribute->Form.Resident.ResidentFlags = RESIDENT_FORM_INDEXED;
  1712. }
  1713. //
  1714. // Now we will actually create the attribute in place, so that we
  1715. // save copying everything twice, and can point to the final image
  1716. // for the log write below.
  1717. //
  1718. NtfsRestartInsertAttribute( IrpContext,
  1719. FileRecord,
  1720. RecordOffset,
  1721. Attribute,
  1722. AttributeName,
  1723. Value,
  1724. ValueLength );
  1725. //
  1726. // Finally, log the creation of this attribute
  1727. //
  1728. if (LogIt) {
  1729. //
  1730. // We have actually created the attribute above, but the write
  1731. // log below could fail. The reason we did the create already
  1732. // was to avoid having to allocate pool and copy everything
  1733. // twice (header, name and value). Our normal error recovery
  1734. // just recovers from the log file. But if we fail to write
  1735. // the log, we have to remove this attribute by hand, and
  1736. // raise the condition again.
  1737. //
  1738. try {
  1739. FileRecord->Lsn =
  1740. NtfsWriteLog( IrpContext,
  1741. Vcb->MftScb,
  1742. NtfsFoundBcb(Context),
  1743. CreateAttribute,
  1744. Add2Ptr(FileRecord, RecordOffset),
  1745. Attribute->RecordLength,
  1746. DeleteAttribute,
  1747. NULL,
  1748. 0,
  1749. NtfsMftOffset( Context ),
  1750. RecordOffset,
  1751. 0,
  1752. Vcb->BytesPerFileRecordSegment );
  1753. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  1754. NtfsRestartRemoveAttribute( IrpContext, FileRecord, RecordOffset );
  1755. NtfsRaiseStatus( IrpContext, GetExceptionCode(), NULL, NULL );
  1756. }
  1757. }
  1758. //
  1759. // Now add it to the attribute list if necessary
  1760. //
  1761. if (Context->AttributeList.Bcb != NULL) {
  1762. MFT_SEGMENT_REFERENCE SegmentReference;
  1763. *(PLONGLONG)&SegmentReference = LlFileRecordsFromBytes( Vcb, NtfsMftOffset( Context ));
  1764. SegmentReference.SequenceNumber = FileRecord->SequenceNumber;
  1765. NtfsAddToAttributeList( IrpContext, Fcb, SegmentReference, Context );
  1766. }
  1767. DebugTrace( -1, Dbg, ("NtfsCreateAttributeWithValue -> VOID\n") );
  1768. return;
  1769. }
  1770. VOID
  1771. NtfsCreateNonresidentWithValue (
  1772. IN PIRP_CONTEXT IrpContext,
  1773. IN PFCB Fcb,
  1774. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  1775. IN PUNICODE_STRING AttributeName OPTIONAL,
  1776. IN PVOID Value OPTIONAL,
  1777. IN ULONG ValueLength,
  1778. IN USHORT AttributeFlags,
  1779. IN BOOLEAN WriteClusters,
  1780. IN PSCB ThisScb OPTIONAL,
  1781. IN BOOLEAN LogIt,
  1782. IN PATTRIBUTE_ENUMERATION_CONTEXT Context
  1783. )
  1784. /*++
  1785. Routine Description:
  1786. This routine creates the specified nonresident attribute with the specified
  1787. value, and returns a description of it via the attribute context. If no
  1788. value is specified, then the attribute is created with the specified
  1789. number of zero bytes.
  1790. On successful return, it is up to the caller to clean up the attribute
  1791. context.
  1792. Arguments:
  1793. Fcb - Current file.
  1794. AttributeTypeCode - Type code of the attribute to create.
  1795. AttributeName - Optional name for attribute.
  1796. Value - Pointer to the buffer containing the desired attribute value,
  1797. or a NULL if zeros are desired.
  1798. ValueLength - Length of value in bytes.
  1799. AttributeFlags - Desired flags for the created attribute.
  1800. WriteClusters - if supplied as TRUE, then we cannot write the data into the
  1801. cache but must write the clusters directly to the disk. The value buffer
  1802. in this case must be quad-aligned and a multiple of cluster size in size.
  1803. If TRUE it also means we are being called during the NtfsConvertToNonresident
  1804. path. We need to set a flag in the Scb in that case.
  1805. ThisScb - If present, this is the Scb to use for the create. It also indicates
  1806. that this call is from convert to non-resident.
  1807. LogIt - Most callers should specify TRUE, to have the change logged. However,
  1808. we can specify FALSE if we are creating a new file record, and
  1809. will be logging the entire new file record.
  1810. Context - This is the location to create the new attribute.
  1811. Return Value:
  1812. None.
  1813. --*/
  1814. {
  1815. PSCB Scb;
  1816. BOOLEAN ReturnedExistingScb;
  1817. UNICODE_STRING LocalName;
  1818. PVCB Vcb = Fcb->Vcb;
  1819. BOOLEAN LogNonresidentToo;
  1820. BOOLEAN AdvanceOnly;
  1821. PAGED_CODE();
  1822. DebugTrace( +1, Dbg, ("NtfsCreateNonresidentWithValue\n") );
  1823. //
  1824. // When we're updating the attribute definition table, we want that operation
  1825. // to be logged, even though it's a $DATA attribute.
  1826. //
  1827. //
  1828. // TODO: post nt5.1 change chkdsk so it can recognize an attrdef table with $EA
  1829. // log non-resident
  1830. //
  1831. AdvanceOnly =
  1832. LogNonresidentToo = (BooleanFlagOn( NtfsGetAttributeDefinition( Vcb, AttributeTypeCode )->Flags,
  1833. ATTRIBUTE_DEF_LOG_NONRESIDENT) ||
  1834. NtfsEqualMftRef( &Fcb->FileReference, &AttrDefFileReference ) ||
  1835. ($EA == AttributeTypeCode) );
  1836. ASSERT( (AttributeFlags == 0) || NtfsIsTypeCodeCompressible( AttributeTypeCode ));
  1837. //
  1838. // Clear out the invalid attribute flags for this volume.
  1839. //
  1840. AttributeFlags &= Vcb->AttributeFlagsMask;
  1841. if (ARGUMENT_PRESENT(AttributeName)) {
  1842. LocalName = *AttributeName;
  1843. } else {
  1844. LocalName.Length = LocalName.MaximumLength = 0;
  1845. LocalName.Buffer = NULL;
  1846. }
  1847. if (ARGUMENT_PRESENT( ThisScb )) {
  1848. Scb = ThisScb;
  1849. ReturnedExistingScb = TRUE;
  1850. } else {
  1851. Scb = NtfsCreateScb( IrpContext,
  1852. Fcb,
  1853. AttributeTypeCode,
  1854. &LocalName,
  1855. FALSE,
  1856. &ReturnedExistingScb );
  1857. //
  1858. // An attribute has gone away but the Scb hasn't left yet.
  1859. // Also mark the header as unitialized.
  1860. //
  1861. ClearFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED |
  1862. SCB_STATE_ATTRIBUTE_RESIDENT |
  1863. SCB_STATE_FILE_SIZE_LOADED );
  1864. //
  1865. // Set a flag in the Scb to indicate that we are converting to non-resident.
  1866. //
  1867. if (WriteClusters) { SetFlag( Scb->ScbState, SCB_STATE_CONVERT_UNDERWAY ); }
  1868. }
  1869. //
  1870. // Allocate the record for the size we need.
  1871. //
  1872. NtfsAllocateAttribute( IrpContext,
  1873. Scb,
  1874. AttributeTypeCode,
  1875. AttributeName,
  1876. AttributeFlags,
  1877. TRUE,
  1878. LogIt,
  1879. (LONGLONG) ValueLength,
  1880. Context );
  1881. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  1882. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  1883. //
  1884. // We need to be careful here, if this call is due to MM creating a
  1885. // section, we don't want to call into the cache manager or we
  1886. // will deadlock on the create section call.
  1887. //
  1888. if (!WriteClusters && !ARGUMENT_PRESENT( ThisScb )) {
  1889. //
  1890. // This call will initialize a stream for use below.
  1891. //
  1892. NtfsCreateInternalAttributeStream( IrpContext,
  1893. Scb,
  1894. TRUE,
  1895. &NtfsInternalUseFile[CREATENONRESIDENTWITHVALUE_FILE_NUMBER] );
  1896. }
  1897. //
  1898. // Now, write in the data.
  1899. //
  1900. Scb->Header.FileSize.QuadPart = ValueLength;
  1901. if ((ARGUMENT_PRESENT( Value )) && (ValueLength != 0)) {
  1902. if (LogNonresidentToo || !WriteClusters) {
  1903. ULONG BytesThisPage;
  1904. PVOID Buffer;
  1905. PBCB Bcb = NULL;
  1906. LONGLONG CurrentFileOffset = 0;
  1907. ULONG RemainingBytes = ValueLength;
  1908. PVOID CurrentValue = Value;
  1909. //
  1910. // While there is more to write, pin the next page and
  1911. // write a log record.
  1912. //
  1913. try {
  1914. CC_FILE_SIZES FileSizes;
  1915. //
  1916. // Call the Cache Manager to truncate and reestablish the FileSize,
  1917. // so that we are guaranteed to get a valid data length call when
  1918. // the data goes out. Otherwise he will likely think he does not
  1919. // have to call us.
  1920. //
  1921. RtlCopyMemory( &FileSizes, &Scb->Header.AllocationSize, sizeof( CC_FILE_SIZES ));
  1922. FileSizes.FileSize.QuadPart = 0;
  1923. CcSetFileSizes( Scb->FileObject, &FileSizes );
  1924. CcSetFileSizes( Scb->FileObject,
  1925. (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
  1926. while (RemainingBytes) {
  1927. BytesThisPage = (RemainingBytes < PAGE_SIZE ? RemainingBytes : PAGE_SIZE);
  1928. NtfsUnpinBcb( IrpContext, &Bcb );
  1929. NtfsPinStream( IrpContext,
  1930. Scb,
  1931. CurrentFileOffset,
  1932. BytesThisPage,
  1933. &Bcb,
  1934. &Buffer );
  1935. if (ARGUMENT_PRESENT(ThisScb)) {
  1936. //
  1937. // Set the address range modified so that the data will get
  1938. // written to its new "home".
  1939. //
  1940. MmSetAddressRangeModified( Buffer, BytesThisPage );
  1941. } else {
  1942. RtlCopyMemory( Buffer, CurrentValue, BytesThisPage );
  1943. }
  1944. if (LogNonresidentToo) {
  1945. (VOID)
  1946. NtfsWriteLog( IrpContext,
  1947. Scb,
  1948. Bcb,
  1949. UpdateNonresidentValue,
  1950. Buffer,
  1951. BytesThisPage,
  1952. Noop,
  1953. NULL,
  1954. 0,
  1955. CurrentFileOffset,
  1956. 0,
  1957. 0,
  1958. BytesThisPage );
  1959. } else {
  1960. CcSetDirtyPinnedData( Bcb, NULL );
  1961. }
  1962. RemainingBytes -= BytesThisPage;
  1963. CurrentValue = (PVOID) Add2Ptr( CurrentValue, BytesThisPage );
  1964. (ULONG)CurrentFileOffset += BytesThisPage;
  1965. }
  1966. } finally {
  1967. NtfsUnpinBcb( IrpContext, &Bcb );
  1968. }
  1969. } else {
  1970. //
  1971. // We are going to write the old data directly to disk.
  1972. //
  1973. NtfsWriteClusters( IrpContext,
  1974. Vcb,
  1975. Scb,
  1976. (LONGLONG)0,
  1977. Value,
  1978. ClustersFromBytes( Vcb, ValueLength ));
  1979. //
  1980. // Be sure to note that the data is actually on disk.
  1981. //
  1982. AdvanceOnly = TRUE;
  1983. }
  1984. }
  1985. //
  1986. // We need to maintain the file size and valid data length in the
  1987. // Scb and attribute record. For this attribute, the valid data
  1988. // size and the file size are now the value length.
  1989. //
  1990. Scb->Header.ValidDataLength = Scb->Header.FileSize;
  1991. NtfsVerifySizes( &Scb->Header );
  1992. NtfsWriteFileSizes( IrpContext,
  1993. Scb,
  1994. &Scb->Header.ValidDataLength.QuadPart,
  1995. AdvanceOnly,
  1996. LogIt,
  1997. FALSE );
  1998. if (!WriteClusters) {
  1999. //
  2000. // Let the cache manager know the new size for this attribute.
  2001. //
  2002. CcSetFileSizes( Scb->FileObject, (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
  2003. }
  2004. //
  2005. // If this is the unnamed data attribute, we need to mark this
  2006. // change in the Fcb.
  2007. //
  2008. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  2009. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  2010. Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
  2011. SetFlag( Fcb->InfoFlags,
  2012. (FCB_INFO_CHANGED_ALLOC_SIZE | FCB_INFO_CHANGED_FILE_SIZE) );
  2013. }
  2014. DebugTrace( -1, Dbg, ("NtfsCreateNonresidentWithValue -> VOID\n") );
  2015. }
  2016. VOID
  2017. NtfsMapAttributeValue (
  2018. IN PIRP_CONTEXT IrpContext,
  2019. IN PFCB Fcb,
  2020. OUT PVOID *Buffer,
  2021. OUT PULONG Length,
  2022. OUT PBCB *Bcb,
  2023. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  2024. )
  2025. /*++
  2026. Routine Description:
  2027. This routine may be called to map an entire attribute value. It works
  2028. whether the attribute is resident or nonresident. It is intended for
  2029. general handling of system-defined attributes which are small to medium
  2030. in size, i.e. 0-64KB. This routine will not work for attributes larger
  2031. than the Cache Manager's virtual address granularity (currently 256KB),
  2032. and this will be detected by the Cache Manager who will raise an error.
  2033. Note that this routine only maps the data for read-only access. To modify
  2034. the data, the caller must call NtfsChangeAttributeValue AFTER UNPINNING
  2035. THE BCB (IF THE SIZE IS CHANGING) returned from this routine.
  2036. Arguments:
  2037. Fcb - Current file.
  2038. Buffer - returns a pointer to the mapped attribute value.
  2039. Length - returns the attribute value length in bytes.
  2040. Bcb - Returns a Bcb which must be unpinned when done with the data, and
  2041. before modifying the attribute value with a size change.
  2042. Context - Attribute Context positioned at the attribute to change.
  2043. Return Value:
  2044. None.
  2045. --*/
  2046. {
  2047. PATTRIBUTE_RECORD_HEADER Attribute;
  2048. PSCB Scb;
  2049. UNICODE_STRING AttributeName;
  2050. BOOLEAN ReturnedExistingScb;
  2051. ASSERT_IRP_CONTEXT( IrpContext );
  2052. ASSERT_FCB( Fcb );
  2053. PAGED_CODE();
  2054. DebugTrace( +1, Dbg, ("NtfsMapAttributeValue\n") );
  2055. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  2056. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  2057. Attribute = NtfsFoundAttribute(Context);
  2058. //
  2059. // For the resident case, everything we need is in the
  2060. // attribute enumeration context.
  2061. //
  2062. if (NtfsIsAttributeResident(Attribute)) {
  2063. *Buffer = NtfsAttributeValue( Attribute );
  2064. *Length = Attribute->Form.Resident.ValueLength;
  2065. *Bcb = NtfsFoundBcb(Context);
  2066. NtfsFoundBcb(Context) = NULL;
  2067. DebugTrace( 0, Dbg, ("Buffer < %08lx\n", *Buffer) );
  2068. DebugTrace( 0, Dbg, ("Length < %08lx\n", *Length) );
  2069. DebugTrace( 0, Dbg, ("Bcb < %08lx\n", *Bcb) );
  2070. DebugTrace( -1, Dbg, ("NtfsMapAttributeValue -> VOID\n") );
  2071. return;
  2072. }
  2073. //
  2074. // Otherwise, this is a nonresident attribute. First create
  2075. // the Scb and stream. Note we do not use any try-finally
  2076. // around this because we currently expect cleanup to get
  2077. // rid of these streams.
  2078. //
  2079. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  2080. Scb = NtfsCreateScb( IrpContext,
  2081. Fcb,
  2082. Attribute->TypeCode,
  2083. &AttributeName,
  2084. FALSE,
  2085. &ReturnedExistingScb );
  2086. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  2087. NtfsUpdateScbFromAttribute( IrpContext, Scb, Attribute );
  2088. }
  2089. NtfsCreateInternalAttributeStream( IrpContext,
  2090. Scb,
  2091. FALSE,
  2092. &NtfsInternalUseFile[MAPATTRIBUTEVALUE_FILE_NUMBER] );
  2093. //
  2094. // Now just try to map the whole thing. Count on the Cache Manager
  2095. // to complain if the attribute is too big to map all at once.
  2096. //
  2097. NtfsMapStream( IrpContext,
  2098. Scb,
  2099. (LONGLONG)0,
  2100. ((ULONG)Attribute->Form.Nonresident.FileSize),
  2101. Bcb,
  2102. Buffer );
  2103. *Length = ((ULONG)Attribute->Form.Nonresident.FileSize);
  2104. DebugTrace( 0, Dbg, ("Buffer < %08lx\n", *Buffer) );
  2105. DebugTrace( 0, Dbg, ("Length < %08lx\n", *Length) );
  2106. DebugTrace( 0, Dbg, ("Bcb < %08lx\n", *Bcb) );
  2107. DebugTrace( -1, Dbg, ("NtfsMapAttributeValue -> VOID\n") );
  2108. }
  2109. VOID
  2110. NtfsChangeAttributeValue (
  2111. IN PIRP_CONTEXT IrpContext,
  2112. IN PFCB Fcb,
  2113. IN ULONG ValueOffset,
  2114. IN PVOID Value OPTIONAL,
  2115. IN ULONG ValueLength,
  2116. IN BOOLEAN SetNewLength,
  2117. IN BOOLEAN LogNonresidentToo,
  2118. IN BOOLEAN CreateSectionUnderway,
  2119. IN BOOLEAN PreserveContext,
  2120. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  2121. )
  2122. /*++
  2123. Routine Description:
  2124. This routine changes the value of the specified attribute, optionally
  2125. changing its size.
  2126. The caller specifies the attribute to be changed via the attribute context,
  2127. and must be prepared to clean up this context no matter how this routine
  2128. returns.
  2129. There are three byte ranges of interest for this routine. The first is
  2130. existing bytes to be perserved at the beginning of the attribute. It
  2131. begins a byte 0 and extends to the point where the attribute is being
  2132. changed or the current end of the attribute, which ever is smaller.
  2133. The second is the range of bytes which needs to be zeroed if the modified
  2134. bytes begin past the current end of the file. This range will be
  2135. of length 0 if the modified range begins within the current range
  2136. of bytes for the attribute. The final range is the modified byte range.
  2137. This is zeroed if no value pointer was specified.
  2138. Ranges of zero bytes at the end of the attribute can be represented in
  2139. non-resident attributes by a valid data length set to the beginning
  2140. of what would be zero bytes.
  2141. The following pictures illustrates these ranges when we writing data
  2142. beyond the current end of the file.
  2143. Current attribute
  2144. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2145. Value
  2146. VVVVVVVVVVVVVVVVVVVVVVVV
  2147. Byte range to save
  2148. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2149. Byte range to zero
  2150. 0000
  2151. Resulting attribute
  2152. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ0000VVVVVVVVVVVVVVVVVVVVVVVV
  2153. The following picture illustrates these ranges when we writing data
  2154. which begins at or before the current end of the file.
  2155. Current attribute
  2156. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2157. Value
  2158. VVVVVVVVVVVVVVVVVVVVVVVV
  2159. Byte range to save
  2160. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2161. Byte range to zero (None)
  2162. Resulting attribute
  2163. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZVVVVVVVVVVVVVVVVVVVVVVVV
  2164. The following picture illustrates these ranges when we writing data
  2165. totally within the current range of the file without setting
  2166. a new size.
  2167. Current attribute
  2168. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2169. Value
  2170. VVVVVVVVVVVVVVVVVVVVVVVV
  2171. Byte range to save (Save the whole range and then write over it)
  2172. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2173. Byte range to zero (None)
  2174. Resulting attribute
  2175. ZZZZVVVVVVVVVVVVVVVVVVVVVVVVZZZZ
  2176. The following picture illustrates these ranges when we writing data
  2177. totally within the current range of the file while setting
  2178. a new size.
  2179. Current attribute
  2180. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2181. Value
  2182. VVVVVVVVVVVVVVVVVVVVVVVV
  2183. Byte range to save (Only save the beginning)
  2184. ZZZZ
  2185. Byte range to zero (None)
  2186. Resulting attribute
  2187. ZZZZVVVVVVVVVVVVVVVVVVVVVVVV
  2188. Any of the 'V' values above will be replaced by zeroes if the 'Value'
  2189. parameter is not passed in.
  2190. Arguments:
  2191. Fcb - Current file.
  2192. ValueOffset - Byte offset within the attribute at which the value change is
  2193. to begin.
  2194. Value - Pointer to the buffer containing the new value, if present. Otherwise
  2195. zeroes are desired.
  2196. ValueLength - Length of the value in the above buffer.
  2197. SetNewLength - FALSE if the size of the value is not changing, or TRUE if
  2198. the value length should be changed to ValueOffset + ValueLength.
  2199. LogNonresidentToo - supplies TRUE if the update should be logged even if
  2200. the attribute is nonresident (such as for the
  2201. SECURITY_DESCRIPTOR).
  2202. CreateSectionUnderway - if supplied as TRUE, then to the best of the caller's
  2203. knowledge, an MM Create Section could be underway,
  2204. which means that we cannot initiate caching on
  2205. this attribute, as that could cause deadlock. The
  2206. value buffer in this case must be quad-aligned and
  2207. a multiple of cluster size in size.
  2208. PreserveContext - Indicates if we need to lookup the attribute in case it
  2209. might move.
  2210. Context - Attribute Context positioned at the attribute to change.
  2211. Return Value:
  2212. None.
  2213. --*/
  2214. {
  2215. PATTRIBUTE_RECORD_HEADER Attribute;
  2216. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  2217. ATTRIBUTE_TYPE_CODE AttributeTypeCode;
  2218. UNICODE_STRING AttributeName;
  2219. ULONG NewSize;
  2220. PVCB Vcb;
  2221. BOOLEAN ReturnedExistingScb;
  2222. BOOLEAN GoToNonResident = FALSE;
  2223. PVOID Buffer;
  2224. ULONG CurrentLength;
  2225. LONG SizeChange, QuadSizeChange;
  2226. ULONG RecordOffset;
  2227. ULONG ZeroLength = 0;
  2228. ULONG UnchangedSize = 0;
  2229. PBCB Bcb = NULL;
  2230. PSCB Scb = NULL;
  2231. PVOID SaveBuffer = NULL;
  2232. PVOID CopyInputBuffer = NULL;
  2233. WCHAR NameBuffer[8];
  2234. UNICODE_STRING SavedName;
  2235. ATTRIBUTE_TYPE_CODE TypeCode;
  2236. ASSERT_IRP_CONTEXT( IrpContext );
  2237. ASSERT_FCB( Fcb );
  2238. Vcb = Fcb->Vcb;
  2239. PAGED_CODE();
  2240. DebugTrace( +1, Dbg, ("NtfsChangeAttributeValue\n") );
  2241. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  2242. DebugTrace( 0, Dbg, ("ValueOffset = %08lx\n", ValueOffset) );
  2243. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  2244. DebugTrace( 0, Dbg, ("ValueLength = %08lx\n", ValueLength) );
  2245. DebugTrace( 0, Dbg, ("SetNewLength = %02lx\n", SetNewLength) );
  2246. DebugTrace( 0, Dbg, ("LogNonresidentToo = %02lx\n", LogNonresidentToo) );
  2247. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  2248. //
  2249. // Get the file record and attribute pointers.
  2250. //
  2251. FileRecord = NtfsContainingFileRecord(Context);
  2252. Attribute = NtfsFoundAttribute(Context);
  2253. TypeCode = Attribute->TypeCode;
  2254. //
  2255. // Set up a pointer to the name buffer in case we have to use it.
  2256. //
  2257. SavedName.Buffer = NameBuffer;
  2258. //
  2259. // Get the current attribute value length.
  2260. //
  2261. if (NtfsIsAttributeResident(Attribute)) {
  2262. CurrentLength = Attribute->Form.Resident.ValueLength;
  2263. } else {
  2264. if (((PLARGE_INTEGER)&Attribute->Form.Nonresident.AllocatedLength)->HighPart != 0) {
  2265. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2266. }
  2267. CurrentLength = ((ULONG)Attribute->Form.Nonresident.AllocatedLength);
  2268. }
  2269. ASSERT( SetNewLength || ((ValueOffset + ValueLength) <= CurrentLength) );
  2270. //
  2271. // Calculate how much the file record is changing by, and its new
  2272. // size. We also compute the size of the range of zero bytes.
  2273. //
  2274. if (SetNewLength) {
  2275. NewSize = ValueOffset + ValueLength;
  2276. SizeChange = NewSize - CurrentLength;
  2277. QuadSizeChange = QuadAlign( NewSize ) - QuadAlign( CurrentLength );
  2278. //
  2279. // If the new size is large enough, the size change may appear to be negative.
  2280. // In this case we go directly to the non-resident path.
  2281. //
  2282. if (NewSize > Vcb->BytesPerFileRecordSegment) {
  2283. GoToNonResident = TRUE;
  2284. }
  2285. } else {
  2286. NewSize = CurrentLength;
  2287. SizeChange = 0;
  2288. QuadSizeChange = 0;
  2289. }
  2290. //
  2291. // If we are zeroing a range in the file and it extends to the
  2292. // end of the file or beyond then make this a single zeroed run.
  2293. //
  2294. if (!ARGUMENT_PRESENT( Value )
  2295. && ValueOffset >= CurrentLength) {
  2296. ZeroLength = ValueOffset + ValueLength - CurrentLength;
  2297. ValueOffset = ValueOffset + ValueLength;
  2298. ValueLength = 0;
  2299. //
  2300. // If we are writing data starting beyond the end of the
  2301. // file then we have a range of bytes to zero.
  2302. //
  2303. } else if (ValueOffset > CurrentLength) {
  2304. ZeroLength = ValueOffset - CurrentLength;
  2305. }
  2306. //
  2307. // At this point we know the following ranges:
  2308. //
  2309. // Range to save: Not needed unless going resident to non-resident
  2310. //
  2311. // Zero range: From Zero offset for length ZeroLength
  2312. //
  2313. // Modified range: From ValueOffset to NewSize, this length may
  2314. // be zero.
  2315. //
  2316. //
  2317. // If the attribute is resident, and it will stay resident, then we will
  2318. // handle that case first, and return.
  2319. //
  2320. if (NtfsIsAttributeResident( Attribute )
  2321. &&
  2322. !GoToNonResident
  2323. &&
  2324. ((QuadSizeChange <= (LONG)(FileRecord->BytesAvailable - FileRecord->FirstFreeByte))
  2325. || ((Attribute->RecordLength + SizeChange) < Vcb->BigEnoughToMove))) {
  2326. PVOID UndoBuffer;
  2327. ULONG UndoLength;
  2328. ULONG AttributeOffset;
  2329. //
  2330. // If the attribute record is growing, then we have to get the new space
  2331. // now.
  2332. //
  2333. if (QuadSizeChange > 0) {
  2334. BOOLEAN FirstPass = TRUE;
  2335. ASSERT( !FlagOn(Attribute->Form.Resident.ResidentFlags, RESIDENT_FORM_INDEXED) );
  2336. //
  2337. // Save a description of the attribute in case we have to look it up
  2338. // again.
  2339. //
  2340. SavedName.Length =
  2341. SavedName.MaximumLength = (USHORT)(Attribute->NameLength * sizeof(WCHAR));
  2342. if (SavedName.Length > sizeof(NameBuffer)) {
  2343. SavedName.Buffer = NtfsAllocatePool( NonPagedPool, SavedName.Length );
  2344. }
  2345. //
  2346. // Copy the name into the buffer.
  2347. //
  2348. if (SavedName.Length != 0) {
  2349. RtlCopyMemory( SavedName.Buffer,
  2350. Add2Ptr( Attribute, Attribute->NameOffset ),
  2351. SavedName.Length );
  2352. }
  2353. //
  2354. // Make sure we deallocate the name buffer.
  2355. //
  2356. try {
  2357. do {
  2358. //
  2359. // If not the first pass, we have to lookup the attribute
  2360. // again.
  2361. //
  2362. if (!FirstPass) {
  2363. NtfsCleanupAttributeContext( IrpContext, Context );
  2364. NtfsInitializeAttributeContext( Context );
  2365. if (!NtfsLookupAttributeByName( IrpContext,
  2366. Fcb,
  2367. &Fcb->FileReference,
  2368. TypeCode,
  2369. &SavedName,
  2370. NULL,
  2371. FALSE,
  2372. Context )) {
  2373. ASSERT(FALSE);
  2374. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2375. }
  2376. //
  2377. // Now we have to reload our attribute pointer
  2378. //
  2379. Attribute = NtfsFoundAttribute( Context );
  2380. }
  2381. FirstPass = FALSE;
  2382. //
  2383. // If FALSE is returned, then the space was not allocated and
  2384. // we have too loop back and try again. Second time must work.
  2385. //
  2386. } while (!NtfsChangeAttributeSize( IrpContext,
  2387. Fcb,
  2388. QuadAlign( Attribute->Form.Resident.ValueOffset + NewSize),
  2389. Context ));
  2390. } finally {
  2391. if (SavedName.Buffer != NameBuffer) {
  2392. NtfsFreePool(SavedName.Buffer);
  2393. }
  2394. }
  2395. //
  2396. // Now we have to reload our attribute pointer
  2397. //
  2398. FileRecord = NtfsContainingFileRecord(Context);
  2399. Attribute = NtfsFoundAttribute(Context);
  2400. } else {
  2401. //
  2402. // Make sure the buffer is pinned if we are not changing size, because
  2403. // we begin to modify it below.
  2404. //
  2405. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  2406. //
  2407. // We can eliminate some/all of the value if it has not changed.
  2408. //
  2409. if (ARGUMENT_PRESENT(Value)) {
  2410. UnchangedSize = (ULONG)RtlCompareMemory( Add2Ptr(Attribute,
  2411. Attribute->Form.Resident.ValueOffset +
  2412. ValueOffset),
  2413. Value,
  2414. ValueLength );
  2415. Value = Add2Ptr(Value, UnchangedSize);
  2416. ValueOffset += UnchangedSize;
  2417. ValueLength -= UnchangedSize;
  2418. }
  2419. }
  2420. RecordOffset = PtrOffset(FileRecord, Attribute);
  2421. //
  2422. // If there is a zero range of bytes, deal with it now.
  2423. // If we are zeroing data then we must be growing the
  2424. // file.
  2425. //
  2426. if (ZeroLength != 0) {
  2427. //
  2428. // We always start zeroing at the zeroing offset.
  2429. //
  2430. AttributeOffset = Attribute->Form.Resident.ValueOffset +
  2431. CurrentLength;
  2432. //
  2433. // If we are starting at the end of the file the undo
  2434. // buffer is NULL and the length is zero.
  2435. //
  2436. FileRecord->Lsn =
  2437. NtfsWriteLog( IrpContext,
  2438. Vcb->MftScb,
  2439. NtfsFoundBcb(Context),
  2440. UpdateResidentValue,
  2441. NULL,
  2442. ZeroLength,
  2443. UpdateResidentValue,
  2444. NULL,
  2445. 0,
  2446. NtfsMftOffset( Context ),
  2447. RecordOffset,
  2448. AttributeOffset,
  2449. Vcb->BytesPerFileRecordSegment );
  2450. //
  2451. // Now zero this data by calling the same routine as restart.
  2452. //
  2453. NtfsRestartChangeValue( IrpContext,
  2454. FileRecord,
  2455. RecordOffset,
  2456. AttributeOffset,
  2457. NULL,
  2458. ZeroLength,
  2459. TRUE );
  2460. #ifdef SYSCACHE_DEBUG
  2461. {
  2462. PSCB TempScb;
  2463. TempScb = CONTAINING_RECORD( Fcb->ScbQueue.Flink, SCB, FcbLinks );
  2464. while (&TempScb->FcbLinks != &Fcb->ScbQueue) {
  2465. if (ScbIsBeingLogged( TempScb )) {
  2466. FsRtlLogSyscacheEvent( TempScb, SCE_ZERO_MF, 0, AttributeOffset, ZeroLength, 0 );
  2467. }
  2468. TempScb = CONTAINING_RECORD( TempScb->FcbLinks.Flink, SCB, FcbLinks );
  2469. }
  2470. }
  2471. #endif
  2472. }
  2473. //
  2474. // Now log the new data for the file. This range will always begin
  2475. // within the current range of bytes for the file. Because of this
  2476. // there is an undo action.
  2477. //
  2478. // Even if there is not a nonzero ValueLength, we still have to
  2479. // execute this code if the attribute is being truncated.
  2480. // The only exception is if we logged some zero data and have
  2481. // nothing left to log.
  2482. //
  2483. if ((ValueLength != 0)
  2484. || (ZeroLength == 0
  2485. && SizeChange != 0)) {
  2486. //
  2487. // The attribute offset is always at the value offset.
  2488. //
  2489. AttributeOffset = Attribute->Form.Resident.ValueOffset + ValueOffset;
  2490. //
  2491. // There are 3 possible cases for the undo action to
  2492. // log.
  2493. //
  2494. //
  2495. // If we are growing the file starting beyond the end of
  2496. // the file then undo buffer is NULL and the length is
  2497. // zero. This will still allow us to shrink the file
  2498. // on abort.
  2499. //
  2500. if (ValueOffset >= CurrentLength) {
  2501. UndoBuffer = NULL;
  2502. UndoLength = 0;
  2503. //
  2504. // For the other cases the undo buffer begins at the
  2505. // point of the change.
  2506. //
  2507. } else {
  2508. UndoBuffer = Add2Ptr( Attribute,
  2509. Attribute->Form.Resident.ValueOffset + ValueOffset );
  2510. //
  2511. // If the size isn't changing then the undo length is the same as
  2512. // the redo length.
  2513. //
  2514. if (SizeChange == 0) {
  2515. UndoLength = ValueLength;
  2516. //
  2517. // Otherwise the length is the range between the end of the
  2518. // file and the start of the new data.
  2519. //
  2520. } else {
  2521. UndoLength = CurrentLength - ValueOffset;
  2522. }
  2523. }
  2524. FileRecord->Lsn =
  2525. NtfsWriteLog( IrpContext,
  2526. Vcb->MftScb,
  2527. NtfsFoundBcb(Context),
  2528. UpdateResidentValue,
  2529. Value,
  2530. ValueLength,
  2531. UpdateResidentValue,
  2532. UndoBuffer,
  2533. UndoLength,
  2534. NtfsMftOffset( Context ),
  2535. RecordOffset,
  2536. AttributeOffset,
  2537. Vcb->BytesPerFileRecordSegment );
  2538. //
  2539. // Now update this data by calling the same routine as restart.
  2540. //
  2541. NtfsRestartChangeValue( IrpContext,
  2542. FileRecord,
  2543. RecordOffset,
  2544. AttributeOffset,
  2545. Value,
  2546. ValueLength,
  2547. (BOOLEAN)(SizeChange != 0) );
  2548. }
  2549. DebugTrace( -1, Dbg, ("NtfsChangeAttributeValue -> VOID\n") );
  2550. return;
  2551. }
  2552. //
  2553. // Nonresident case. Create the Scb and attributestream.
  2554. //
  2555. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  2556. AttributeTypeCode = Attribute->TypeCode;
  2557. Scb = NtfsCreateScb( IrpContext,
  2558. Fcb,
  2559. AttributeTypeCode,
  2560. &AttributeName,
  2561. FALSE,
  2562. &ReturnedExistingScb );
  2563. //
  2564. // Use try-finally for cleanup.
  2565. //
  2566. try {
  2567. BOOLEAN AllocateBufferCopy = FALSE;
  2568. BOOLEAN DeleteAllocation = FALSE;
  2569. BOOLEAN LookupAttribute = FALSE;
  2570. BOOLEAN AdvanceValidData = FALSE;
  2571. LONGLONG NewValidDataLength;
  2572. LONGLONG LargeValueOffset;
  2573. LONGLONG LargeNewSize;
  2574. if (SetNewLength
  2575. && NewSize > Scb->Header.FileSize.LowPart
  2576. && TypeCode == $ATTRIBUTE_LIST) {
  2577. AllocateBufferCopy = TRUE;
  2578. }
  2579. LargeNewSize = NewSize;
  2580. LargeValueOffset = ValueOffset;
  2581. //
  2582. // Well, the attribute is either changing to nonresident, or it is already
  2583. // nonresident. First we will handle the conversion to nonresident case.
  2584. // We can detect this case by whether or not the attribute is currently
  2585. // resident.
  2586. //
  2587. if (NtfsIsAttributeResident(Attribute)) {
  2588. NtfsConvertToNonresident( IrpContext,
  2589. Fcb,
  2590. Attribute,
  2591. CreateSectionUnderway,
  2592. Context );
  2593. //
  2594. // Reload the attribute pointer from the context.
  2595. //
  2596. Attribute = NtfsFoundAttribute( Context );
  2597. //
  2598. // The process of creating a non resident attribute will also create
  2599. // and initialize a stream file for the Scb. If the file is already
  2600. // non-resident we also need a stream file.
  2601. //
  2602. } else {
  2603. NtfsCreateInternalAttributeStream( IrpContext,
  2604. Scb,
  2605. TRUE,
  2606. &NtfsInternalUseFile[CHANGEATTRIBUTEVALUE_FILE_NUMBER] );
  2607. }
  2608. //
  2609. // If the attribute is already nonresident, make sure the allocation
  2610. // is the right size. We grow it before we log the data to be sure
  2611. // we have the space for the new data. We shrink it after we log the
  2612. // new data so we have the old data available for the undo.
  2613. //
  2614. if (((PLARGE_INTEGER)&Attribute->Form.Nonresident.AllocatedLength)->HighPart != 0) {
  2615. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2616. }
  2617. if (NewSize > ((ULONG)Attribute->Form.Nonresident.AllocatedLength)) {
  2618. LONGLONG NewAllocation;
  2619. if (PreserveContext) {
  2620. //
  2621. // Save a description of the attribute in case we have to look it up
  2622. // again.
  2623. //
  2624. SavedName.Length =
  2625. SavedName.MaximumLength = (USHORT)(Attribute->NameLength * sizeof(WCHAR));
  2626. if (SavedName.Length > sizeof(NameBuffer)) {
  2627. SavedName.Buffer = NtfsAllocatePool( NonPagedPool, SavedName.Length );
  2628. }
  2629. //
  2630. // Copy the name into the buffer.
  2631. //
  2632. if (SavedName.Length != 0) {
  2633. RtlCopyMemory( SavedName.Buffer,
  2634. Add2Ptr( Attribute, Attribute->NameOffset ),
  2635. SavedName.Length );
  2636. }
  2637. LookupAttribute = TRUE;
  2638. }
  2639. //
  2640. // If this is the attribute list then check if we want to allocate a larger block.
  2641. // This way the attribute list doesn't get too fragmented.
  2642. //
  2643. NewAllocation = NewSize - ((ULONG)Attribute->Form.Nonresident.AllocatedLength);
  2644. if (Scb->AttributeTypeCode == $ATTRIBUTE_LIST) {
  2645. if ((ULONG) Attribute->Form.Nonresident.AllocatedLength > (4 * PAGE_SIZE)) {
  2646. NewAllocation += (2 * PAGE_SIZE);
  2647. } else if ((ULONG) Attribute->Form.Nonresident.AllocatedLength > PAGE_SIZE) {
  2648. NewAllocation += PAGE_SIZE;
  2649. }
  2650. }
  2651. NtfsAddAllocation( IrpContext,
  2652. Scb->FileObject,
  2653. Scb,
  2654. LlClustersFromBytes( Vcb, Attribute->Form.Nonresident.AllocatedLength ),
  2655. LlClustersFromBytes( Vcb, NewAllocation ),
  2656. FALSE,
  2657. NULL );
  2658. //
  2659. // AddAllocation will adjust the sizes in the Scb and report
  2660. // the new size to the cache manager. We need to remember if
  2661. // we changed the sizes for the unnamed data attribute.
  2662. //
  2663. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  2664. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  2665. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  2666. }
  2667. } else if (Vcb->BytesPerCluster <=
  2668. ((ULONG)Attribute->Form.Nonresident.AllocatedLength) - NewSize) {
  2669. if ((Scb->AttributeTypeCode != $ATTRIBUTE_LIST) ||
  2670. ((NewSize + Vcb->BytesPerCluster) * 2 < ((ULONG) Attribute->Form.Nonresident.AllocatedLength))) {
  2671. DeleteAllocation = TRUE;
  2672. }
  2673. }
  2674. //
  2675. // Now, write in the data.
  2676. //
  2677. if ((ValueLength != 0
  2678. && ARGUMENT_PRESENT( Value ))
  2679. || (LogNonresidentToo
  2680. && SetNewLength)) {
  2681. BOOLEAN BytesToUndo;
  2682. //
  2683. // We have to compute the amount of data to zero in a different
  2684. // way than we did for the resident case. For the non-resident
  2685. // case we need to zero the data between the old valid data
  2686. // length and the offset in the file for the new data.
  2687. //
  2688. if (LargeValueOffset >= Scb->Header.ValidDataLength.QuadPart) {
  2689. ZeroLength = (ULONG)(LargeValueOffset - Scb->Header.ValidDataLength.QuadPart);
  2690. BytesToUndo = FALSE;
  2691. } else {
  2692. ZeroLength = 0;
  2693. BytesToUndo = TRUE;
  2694. }
  2695. //
  2696. // Update existing nonresident attribute. (We may have just created it
  2697. // above.)
  2698. //
  2699. // If we are supposed to log it, then pin, log and do the update here.
  2700. //
  2701. if (LogNonresidentToo) {
  2702. //
  2703. // At this point the attribute is non-resident and contains
  2704. // its previous value. If the new data lies beyond the
  2705. // previous valid data, we need to zero this data. This
  2706. // action won't require any undo. Otherwise the new data
  2707. // lies within the existing data. In this case we need to
  2708. // log the previous data for possible undo. Finally, the
  2709. // tail of the new data may extend beyond the end of the
  2710. // previous data. There is no undo requirement for these
  2711. // bytes.
  2712. //
  2713. // We do the logging operation in three steps:
  2714. //
  2715. // 1 - We find the all the pages in the attribute that
  2716. // we need to zero any bytes for. There is no
  2717. // undo for these bytes.
  2718. //
  2719. // 2 - Find all pages where we have to perform undo and
  2720. // log the changes to those pages. Note only
  2721. // step 1 or step 2 will be performed as they
  2722. // are mutually exclusive.
  2723. //
  2724. // 3 - Finally, we may have pages where the new data
  2725. // extends beyond the current final page in the
  2726. // attribute. We log the new data but there is
  2727. // no undo.
  2728. //
  2729. // 4 - We may have pages where the old data extends
  2730. // beyond the new data. We will log this old
  2731. // data in the event that we grow and shrink
  2732. // this attribute several times in the same
  2733. // transaction (changes to the attribute list).
  2734. // In this case there is redo but no undo.
  2735. //
  2736. LONGLONG CurrentPage;
  2737. ULONG PageOffset;
  2738. ULONG ByteCountToUndo;
  2739. ULONG NewBytesRemaining;
  2740. //
  2741. // Find the starting page for this operation. It is the
  2742. // ValidDataLength rounded down to a page boundary.
  2743. //
  2744. CurrentPage = Scb->Header.ValidDataLength.QuadPart;
  2745. PageOffset = (ULONG)CurrentPage & (PAGE_SIZE - 1);
  2746. (ULONG)CurrentPage = ((ULONG)CurrentPage & ~(PAGE_SIZE - 1));
  2747. //
  2748. // Loop until there are no more bytes to zero.
  2749. //
  2750. while (ZeroLength != 0) {
  2751. ULONG ZeroBytesThisPage;
  2752. ZeroBytesThisPage = PAGE_SIZE - PageOffset;
  2753. if (ZeroBytesThisPage > ZeroLength) {
  2754. ZeroBytesThisPage = ZeroLength;
  2755. }
  2756. //
  2757. // Pin the desired page and compute a buffer into the
  2758. // page. Also compute how many bytes we we zero on
  2759. // this page.
  2760. //
  2761. NtfsUnpinBcb( IrpContext, &Bcb );
  2762. NtfsPinStream( IrpContext,
  2763. Scb,
  2764. CurrentPage,
  2765. ZeroBytesThisPage + PageOffset,
  2766. &Bcb,
  2767. &Buffer );
  2768. Buffer = Add2Ptr( Buffer, PageOffset );
  2769. //
  2770. // Now write the zeros into the log.
  2771. //
  2772. (VOID)
  2773. NtfsWriteLog( IrpContext,
  2774. Scb,
  2775. Bcb,
  2776. UpdateNonresidentValue,
  2777. NULL,
  2778. ZeroBytesThisPage,
  2779. Noop,
  2780. NULL,
  2781. 0,
  2782. CurrentPage,
  2783. PageOffset,
  2784. 0,
  2785. ZeroBytesThisPage + PageOffset );
  2786. //
  2787. // Zero any data necessary.
  2788. //
  2789. RtlZeroMemory( Buffer, ZeroBytesThisPage );
  2790. #ifdef SYSCACHE_DEBUG
  2791. if (ScbIsBeingLogged( Scb )) {
  2792. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_MF, 0, CurrentPage, ZeroBytesThisPage, 1 );
  2793. }
  2794. #endif
  2795. //
  2796. // Now move through the file.
  2797. //
  2798. ZeroLength -= ZeroBytesThisPage;
  2799. CurrentPage = CurrentPage + PAGE_SIZE;
  2800. PageOffset = 0;
  2801. }
  2802. //
  2803. // Find the starting page for this operation. It is the
  2804. // ValueOffset rounded down to a page boundary.
  2805. //
  2806. CurrentPage = LargeValueOffset;
  2807. (ULONG)CurrentPage = ((ULONG)CurrentPage & ~(PAGE_SIZE - 1));
  2808. PageOffset = (ULONG)LargeValueOffset & (PAGE_SIZE - 1);
  2809. //
  2810. // Now loop until there are no more pages with undo
  2811. // bytes to log.
  2812. //
  2813. NewBytesRemaining = ValueLength;
  2814. if (BytesToUndo) {
  2815. ByteCountToUndo = (ULONG)(Scb->Header.ValidDataLength.QuadPart - LargeValueOffset);
  2816. //
  2817. // If we are spanning pages, growing the file and the
  2818. // input buffer points into the cache, we could lose
  2819. // data as we cross a page boundary. In that case
  2820. // we need to allocate a separate buffer.
  2821. //
  2822. if (AllocateBufferCopy
  2823. && NewBytesRemaining + PageOffset > PAGE_SIZE) {
  2824. CopyInputBuffer = NtfsAllocatePool(PagedPool, NewBytesRemaining );
  2825. RtlCopyMemory( CopyInputBuffer,
  2826. Value,
  2827. NewBytesRemaining );
  2828. Value = CopyInputBuffer;
  2829. AllocateBufferCopy = FALSE;
  2830. }
  2831. //
  2832. // If we aren't setting a new length then limit the
  2833. // undo bytes to those being overwritten.
  2834. //
  2835. if (!SetNewLength
  2836. && ByteCountToUndo > NewBytesRemaining) {
  2837. ByteCountToUndo = NewBytesRemaining;
  2838. }
  2839. while (ByteCountToUndo != 0) {
  2840. ULONG UndoBytesThisPage;
  2841. ULONG RedoBytesThisPage;
  2842. ULONG BytesThisPage;
  2843. NTFS_LOG_OPERATION RedoOperation;
  2844. PVOID RedoBuffer;
  2845. //
  2846. // Also compute the number of bytes of undo and
  2847. // redo on this page.
  2848. //
  2849. RedoBytesThisPage = UndoBytesThisPage = PAGE_SIZE - PageOffset;
  2850. if (RedoBytesThisPage > NewBytesRemaining) {
  2851. RedoBytesThisPage = NewBytesRemaining;
  2852. }
  2853. if (UndoBytesThisPage >= ByteCountToUndo) {
  2854. UndoBytesThisPage = ByteCountToUndo;
  2855. }
  2856. //
  2857. // We pin enough bytes on this page to cover both the
  2858. // redo and undo bytes.
  2859. //
  2860. if (UndoBytesThisPage > RedoBytesThisPage) {
  2861. BytesThisPage = PageOffset + UndoBytesThisPage;
  2862. } else {
  2863. BytesThisPage = PageOffset + RedoBytesThisPage;
  2864. }
  2865. //
  2866. // If there is no redo (we are shrinking the data),
  2867. // then make the redo a noop.
  2868. //
  2869. if (RedoBytesThisPage == 0) {
  2870. RedoOperation = Noop;
  2871. RedoBuffer = NULL;
  2872. } else {
  2873. RedoOperation = UpdateNonresidentValue;
  2874. RedoBuffer = Value;
  2875. }
  2876. //
  2877. // Now we pin the page and calculate the beginning
  2878. // buffer in the page.
  2879. //
  2880. NtfsUnpinBcb( IrpContext, &Bcb );
  2881. NtfsPinStream( IrpContext,
  2882. Scb,
  2883. CurrentPage,
  2884. BytesThisPage,
  2885. &Bcb,
  2886. &Buffer );
  2887. Buffer = Add2Ptr( Buffer, PageOffset );
  2888. //
  2889. // Now log the changes to this page.
  2890. //
  2891. (VOID)
  2892. NtfsWriteLog( IrpContext,
  2893. Scb,
  2894. Bcb,
  2895. RedoOperation,
  2896. RedoBuffer,
  2897. RedoBytesThisPage,
  2898. UpdateNonresidentValue,
  2899. Buffer,
  2900. UndoBytesThisPage,
  2901. CurrentPage,
  2902. PageOffset,
  2903. 0,
  2904. BytesThisPage );
  2905. //
  2906. // Move the data into place if we have new data.
  2907. //
  2908. if (RedoBytesThisPage != 0) {
  2909. RtlMoveMemory( Buffer, Value, RedoBytesThisPage );
  2910. }
  2911. //
  2912. // Now decrement the counts and move through the
  2913. // caller's buffer.
  2914. //
  2915. ByteCountToUndo -= UndoBytesThisPage;
  2916. NewBytesRemaining -= RedoBytesThisPage;
  2917. CurrentPage = PAGE_SIZE + CurrentPage;
  2918. PageOffset = 0;
  2919. Value = Add2Ptr( Value, RedoBytesThisPage );
  2920. }
  2921. }
  2922. //
  2923. // Now loop until there are no more pages with new data
  2924. // to log.
  2925. //
  2926. while (NewBytesRemaining != 0) {
  2927. ULONG RedoBytesThisPage;
  2928. //
  2929. // Also compute the number of bytes of redo on this page.
  2930. //
  2931. RedoBytesThisPage = PAGE_SIZE - PageOffset;
  2932. if (RedoBytesThisPage > NewBytesRemaining) {
  2933. RedoBytesThisPage = NewBytesRemaining;
  2934. }
  2935. //
  2936. // Now we pin the page and calculate the beginning
  2937. // buffer in the page.
  2938. //
  2939. NtfsUnpinBcb( IrpContext, &Bcb );
  2940. NtfsPinStream( IrpContext,
  2941. Scb,
  2942. CurrentPage,
  2943. RedoBytesThisPage,
  2944. &Bcb,
  2945. &Buffer );
  2946. Buffer = Add2Ptr( Buffer, PageOffset );
  2947. //
  2948. // Now log the changes to this page.
  2949. //
  2950. (VOID)
  2951. NtfsWriteLog( IrpContext,
  2952. Scb,
  2953. Bcb,
  2954. UpdateNonresidentValue,
  2955. Value,
  2956. RedoBytesThisPage,
  2957. Noop,
  2958. NULL,
  2959. 0,
  2960. CurrentPage,
  2961. PageOffset,
  2962. 0,
  2963. PageOffset + RedoBytesThisPage );
  2964. //
  2965. // Move the data into place.
  2966. //
  2967. RtlMoveMemory( Buffer, Value, RedoBytesThisPage );
  2968. //
  2969. // Now decrement the counts and move through the
  2970. // caller's buffer.
  2971. //
  2972. NewBytesRemaining -= RedoBytesThisPage;
  2973. CurrentPage = PAGE_SIZE + CurrentPage;
  2974. PageOffset = 0;
  2975. Value = Add2Ptr( Value, RedoBytesThisPage );
  2976. }
  2977. //
  2978. // If we have values to write, we write them to the cache now.
  2979. //
  2980. } else {
  2981. //
  2982. // If we have data to zero, we do no now.
  2983. //
  2984. #ifdef SYSCACHE_DEBUG
  2985. if (ScbIsBeingLogged( Scb )) {
  2986. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_MF, 0, Scb->Header.ValidDataLength.QuadPart, ZeroLength, 2 );
  2987. }
  2988. #endif
  2989. if (ZeroLength != 0) {
  2990. if (!NtfsZeroData( IrpContext,
  2991. Scb,
  2992. Scb->FileObject,
  2993. Scb->Header.ValidDataLength.QuadPart,
  2994. (LONGLONG)ZeroLength,
  2995. NULL )) {
  2996. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2997. }
  2998. }
  2999. if (!CcCopyWrite( Scb->FileObject,
  3000. (PLARGE_INTEGER)&LargeValueOffset,
  3001. ValueLength,
  3002. (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  3003. Value )) {
  3004. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3005. }
  3006. }
  3007. //
  3008. // We need to remember the new valid data length in the
  3009. // Scb if it is greater than the existing.
  3010. //
  3011. NewValidDataLength = LargeValueOffset + ValueLength;
  3012. if (NewValidDataLength > Scb->Header.ValidDataLength.QuadPart) {
  3013. #ifdef SYSCACHE_DEBUG
  3014. if (ScbIsBeingLogged( Scb )) {
  3015. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_MF, SCE_FLAG_SET_VDL, Scb->Header.ValidDataLength.QuadPart, NewValidDataLength, 0 );
  3016. }
  3017. #endif
  3018. Scb->Header.ValidDataLength.QuadPart = NewValidDataLength;
  3019. //
  3020. // If we took the log non-resident path, then we
  3021. // want to advance this on the disk as well.
  3022. //
  3023. if (LogNonresidentToo) {
  3024. AdvanceValidData = TRUE;
  3025. }
  3026. SetFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  3027. }
  3028. //
  3029. // We need to maintain the file size in the Scb. If we grow the
  3030. // file, we extend the cache file size. We always set the
  3031. // valid data length in the Scb to the new file size. The
  3032. // 'AdvanceValidData' boolean and the current size on the
  3033. // disk will determine if it changes on disk.
  3034. //
  3035. if (SetNewLength) {
  3036. Scb->Header.ValidDataLength.QuadPart = NewValidDataLength;
  3037. }
  3038. }
  3039. if (SetNewLength) {
  3040. Scb->Header.FileSize.QuadPart = LargeNewSize;
  3041. if (LogNonresidentToo) {
  3042. Scb->Header.ValidDataLength.QuadPart = LargeNewSize;
  3043. }
  3044. }
  3045. //
  3046. // Note VDD is nonzero only for compressed files
  3047. //
  3048. if (Scb->Header.ValidDataLength.QuadPart < Scb->ValidDataToDisk) {
  3049. Scb->ValidDataToDisk = Scb->Header.ValidDataLength.QuadPart;
  3050. }
  3051. //
  3052. // If there is allocation to delete, we do so now.
  3053. //
  3054. if (DeleteAllocation) {
  3055. //
  3056. // If this is an attribute list then leave at least one full cluster at the
  3057. // end. We don't want to trim off a cluster and then try to regrow the attribute
  3058. // list within the same transaction.
  3059. //
  3060. if (Scb->AttributeTypeCode == $ATTRIBUTE_LIST) {
  3061. LargeNewSize += Vcb->BytesPerCluster;
  3062. ASSERT( LargeNewSize <= Scb->Header.AllocationSize.QuadPart );
  3063. }
  3064. NtfsDeleteAllocation( IrpContext,
  3065. Scb->FileObject,
  3066. Scb,
  3067. LlClustersFromBytes( Vcb, LargeNewSize ),
  3068. MAXLONGLONG,
  3069. TRUE,
  3070. FALSE );
  3071. //
  3072. // DeleteAllocation will adjust the sizes in the Scb and report
  3073. // the new size to the cache manager. We need to remember if
  3074. // we changed the sizes for the unnamed data attribute.
  3075. //
  3076. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  3077. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  3078. Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
  3079. SetFlag( Fcb->InfoFlags,
  3080. (FCB_INFO_CHANGED_ALLOC_SIZE | FCB_INFO_CHANGED_FILE_SIZE) );
  3081. }
  3082. if (AdvanceValidData) {
  3083. NtfsWriteFileSizes( IrpContext,
  3084. Scb,
  3085. &Scb->Header.ValidDataLength.QuadPart,
  3086. TRUE,
  3087. TRUE,
  3088. TRUE );
  3089. }
  3090. } else if (SetNewLength) {
  3091. PFILE_OBJECT CacheFileObject = NULL;
  3092. //
  3093. // If there is no file object, we will create a stream file
  3094. // now,
  3095. //
  3096. if (Scb->FileObject != NULL) {
  3097. CacheFileObject = Scb->FileObject;
  3098. } else if (!CreateSectionUnderway) {
  3099. NtfsCreateInternalAttributeStream( IrpContext,
  3100. Scb,
  3101. FALSE,
  3102. &NtfsInternalUseFile[CHANGEATTRIBUTEVALUE2_FILE_NUMBER] );
  3103. CacheFileObject = Scb->FileObject;
  3104. } else {
  3105. PIO_STACK_LOCATION IrpSp;
  3106. IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
  3107. if (IrpSp->FileObject->SectionObjectPointer == &Scb->NonpagedScb->SegmentObject) {
  3108. CacheFileObject = IrpSp->FileObject;
  3109. }
  3110. }
  3111. ASSERT( CacheFileObject != NULL );
  3112. NtfsSetBothCacheSizes( CacheFileObject,
  3113. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  3114. Scb );
  3115. //
  3116. // If this is the unnamed data attribute, we need to mark this
  3117. // change in the Fcb.
  3118. //
  3119. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  3120. Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
  3121. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_SIZE );
  3122. }
  3123. //
  3124. // Now update the sizes on the disk.
  3125. // The new sizes will already be in the Scb.
  3126. //
  3127. NtfsWriteFileSizes( IrpContext,
  3128. Scb,
  3129. &Scb->Header.ValidDataLength.QuadPart,
  3130. AdvanceValidData,
  3131. TRUE,
  3132. TRUE );
  3133. } else if (AdvanceValidData) {
  3134. NtfsWriteFileSizes( IrpContext,
  3135. Scb,
  3136. &Scb->Header.ValidDataLength.QuadPart,
  3137. TRUE,
  3138. TRUE,
  3139. TRUE );
  3140. }
  3141. //
  3142. // Look up the attribute again in case it moved.
  3143. //
  3144. if (LookupAttribute) {
  3145. NtfsCleanupAttributeContext( IrpContext, Context );
  3146. NtfsInitializeAttributeContext( Context );
  3147. if (!NtfsLookupAttributeByName( IrpContext,
  3148. Fcb,
  3149. &Fcb->FileReference,
  3150. TypeCode,
  3151. &SavedName,
  3152. NULL,
  3153. FALSE,
  3154. Context )) {
  3155. ASSERT( FALSE );
  3156. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3157. }
  3158. }
  3159. } finally {
  3160. DebugUnwind( NtfsChangeAttributeValue );
  3161. if (CopyInputBuffer != NULL) {
  3162. NtfsFreePool( CopyInputBuffer );
  3163. }
  3164. if (SaveBuffer != NULL) {
  3165. NtfsFreePool( SaveBuffer );
  3166. }
  3167. NtfsUnpinBcb( IrpContext, &Bcb );
  3168. DebugTrace( -1, Dbg, ("NtfsChangeAttributeValue -> VOID\n") );
  3169. }
  3170. }
  3171. VOID
  3172. NtfsConvertToNonresident (
  3173. IN PIRP_CONTEXT IrpContext,
  3174. IN PFCB Fcb,
  3175. IN OUT PATTRIBUTE_RECORD_HEADER Attribute,
  3176. IN BOOLEAN CreateSectionUnderway,
  3177. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context OPTIONAL
  3178. )
  3179. /*++
  3180. Routine Description:
  3181. This routine converts a resident attribute to nonresident. It does so
  3182. by allocating a buffer and copying the data and attribute name away,
  3183. deleting the attribute, allocating a new attribute of the right size,
  3184. and then copying the data back out again.
  3185. Arguments:
  3186. Fcb - Requested file.
  3187. Attribute - Supplies a pointer to the attribute to convert.
  3188. CreateSectionUnderway - if supplied as TRUE, then to the best of the caller's
  3189. knowledge, an MM Create Section could be underway,
  3190. which means that we cannot initiate caching on
  3191. this attribute, as that could cause deadlock. The
  3192. value buffer in this case must be quad-aligned and
  3193. a multiple of cluster size in size.
  3194. Context - An attribute context to look up another attribute in the same
  3195. file record. If supplied, we insure that the context is valid
  3196. for converted attribute.
  3197. Return Value:
  3198. None
  3199. --*/
  3200. {
  3201. PVOID Buffer;
  3202. PVOID AllocatedBuffer = NULL;
  3203. ULONG AllocatedLength;
  3204. ULONG AttributeNameOffset;
  3205. ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
  3206. BOOLEAN CleanupLocalContext = FALSE;
  3207. BOOLEAN ReturnedExistingScb;
  3208. ATTRIBUTE_TYPE_CODE AttributeTypeCode = Attribute->TypeCode;
  3209. USHORT AttributeFlags = Attribute->Flags;
  3210. PVOID AttributeValue = NULL;
  3211. ULONG ValueLength;
  3212. UNICODE_STRING AttributeName;
  3213. WCHAR AttributeNameBuffer[16];
  3214. BOOLEAN WriteClusters = CreateSectionUnderway;
  3215. PBCB ResidentBcb = NULL;
  3216. PSCB Scb = NULL;
  3217. PAGED_CODE();
  3218. //
  3219. // Use a try-finally to facilitate cleanup.
  3220. //
  3221. try {
  3222. //
  3223. // Build a temporary copy of the name out of the attribute.
  3224. //
  3225. AttributeName.MaximumLength =
  3226. AttributeName.Length = Attribute->NameLength * sizeof( WCHAR );
  3227. AttributeName.Buffer = Add2Ptr( Attribute, Attribute->NameOffset );
  3228. //
  3229. // If we don't have an attribute context for this attribute then look it
  3230. // up now.
  3231. //
  3232. if (!ARGUMENT_PRESENT( Context )) {
  3233. Context = &LocalContext;
  3234. NtfsInitializeAttributeContext( Context );
  3235. CleanupLocalContext = TRUE;
  3236. //
  3237. // Lookup the first occurence of this attribute.
  3238. //
  3239. if (!NtfsLookupAttributeByName( IrpContext,
  3240. Fcb,
  3241. &Fcb->FileReference,
  3242. AttributeTypeCode,
  3243. &AttributeName,
  3244. NULL,
  3245. FALSE,
  3246. Context )) {
  3247. DebugTrace( 0, 0, ("Could not find attribute being converted\n") );
  3248. ASSERTMSG("Could not find attribute being converted, About to raise corrupt ", FALSE);
  3249. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3250. }
  3251. }
  3252. //
  3253. // We need to figure out how much pool to allocate. If there is a mapped
  3254. // view of this section or a section is being created we will allocate a buffer
  3255. // and copy the data into the buffer. Otherwise we will pin the data in
  3256. // the cache, mark it dirty and use that buffer to perform the conversion.
  3257. //
  3258. AllocatedLength = AttributeName.Length;
  3259. Scb = NtfsCreateScb( IrpContext,
  3260. Fcb,
  3261. AttributeTypeCode,
  3262. &AttributeName,
  3263. FALSE,
  3264. &ReturnedExistingScb );
  3265. //
  3266. // Clear the file size loaded flag for resident attributes non-user data because these
  3267. // values are not kept current in the scb and must be loaded off the attribute
  3268. // This situation only occurs when the user has opened the attribute explicitly
  3269. //
  3270. if (ReturnedExistingScb &&
  3271. FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT ) &&
  3272. !NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
  3273. ClearFlag( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED | SCB_STATE_HEADER_INITIALIZED );
  3274. }
  3275. //
  3276. // Make sure the Scb is up-to-date.
  3277. //
  3278. NtfsUpdateScbFromAttribute( IrpContext,
  3279. Scb,
  3280. Attribute );
  3281. //
  3282. // Set the flag in the Scb to indicate that we are converting this to
  3283. // non resident.
  3284. //
  3285. SetFlag( Scb->ScbState, SCB_STATE_CONVERT_UNDERWAY );
  3286. if (Scb->ScbSnapshot) {
  3287. ASSERT( (NULL == Scb->ScbSnapshot->OwnerIrpContext) || (IrpContext == Scb->ScbSnapshot->OwnerIrpContext) );
  3288. Scb->ScbSnapshot->OwnerIrpContext = IrpContext;
  3289. }
  3290. //
  3291. // Now check if the file is mapped by a user.
  3292. //
  3293. if (CreateSectionUnderway ||
  3294. !MmCanFileBeTruncated( &Scb->NonpagedScb->SegmentObject, NULL )) {
  3295. AttributeNameOffset = ClusterAlign( Fcb->Vcb,
  3296. Attribute->Form.Resident.ValueLength );
  3297. AllocatedLength += AttributeNameOffset;
  3298. ValueLength = Attribute->Form.Resident.ValueLength;
  3299. WriteClusters = TRUE;
  3300. if ((ValueLength != 0) &&
  3301. (IrpContext->MajorFunction == IRP_MJ_WRITE) &&
  3302. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX ) &&
  3303. (IrpContext->OriginatingIrp != NULL) &&
  3304. !FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ) &&
  3305. (Scb->Header.PagingIoResource != NULL)) {
  3306. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  3307. //
  3308. // If we fault the data into the section then we better have
  3309. // the paging io resource exclusive. Otherwise we could hit
  3310. // a collided page fault.
  3311. //
  3312. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3313. }
  3314. Scb = NULL;
  3315. } else {
  3316. volatile UCHAR VolatileUchar;
  3317. AttributeNameOffset = 0;
  3318. NtfsCreateInternalAttributeStream( IrpContext,
  3319. Scb,
  3320. TRUE,
  3321. &NtfsInternalUseFile[CONVERTTONONRESIDENT_FILE_NUMBER] );
  3322. //
  3323. // Make sure the cache is up-to-date.
  3324. //
  3325. NtfsSetBothCacheSizes( Scb->FileObject,
  3326. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  3327. Scb );
  3328. ValueLength = Scb->Header.ValidDataLength.LowPart;
  3329. if (ValueLength != 0) {
  3330. ULONG WaitState;
  3331. //
  3332. // There is a deadlock possibility if there is already a Bcb for
  3333. // this page. If the lazy writer has acquire the Bcb to flush the
  3334. // page then he can be blocked behind the current request which is
  3335. // trying to perform the convert. This thread will complete the
  3336. // deadlock by trying to acquire the Bcb to pin the page.
  3337. //
  3338. // If there is a possible deadlock then we will pin in two stages:
  3339. // First map the page (while waiting) to bring the page into memory,
  3340. // then pin it without waiting. If we are unable to acquire the
  3341. // Bcb then mark the Irp Context to acquire the paging io resource
  3342. // exclusively on the retry.
  3343. //
  3344. // We only do this for ConvertToNonResident which come from a user
  3345. // write. Otherwise the correct synchronization should already be done.
  3346. //
  3347. // Either the top level already has the paging io resource or there
  3348. // is no paging io resource.
  3349. //
  3350. // We might hit this point in the Hotfix path if we need to convert
  3351. // the bad cluster attribute list to non-resident. It that case
  3352. // we won't have an originating Irp.
  3353. //
  3354. if ((IrpContext->MajorFunction == IRP_MJ_WRITE) &&
  3355. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX ) &&
  3356. (IrpContext->OriginatingIrp != NULL) &&
  3357. !FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ) &&
  3358. (Scb->Header.PagingIoResource != NULL)) {
  3359. LONGLONG FileOffset = 0;
  3360. //
  3361. // Now capture the wait state and set the IrpContext flag
  3362. // to handle a failure when mapping or pinning.
  3363. //
  3364. WaitState = FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  3365. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  3366. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  3367. //
  3368. // If we fault the data into the section then we better have
  3369. // the paging io resource exclusive. Otherwise we could hit
  3370. // a collided page fault.
  3371. //
  3372. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3373. } else {
  3374. NtfsPinStream( IrpContext,
  3375. Scb,
  3376. (LONGLONG)0,
  3377. ValueLength,
  3378. &ResidentBcb,
  3379. &AttributeValue );
  3380. }
  3381. //
  3382. // Close the window where this page can leave memory before we
  3383. // have the new attribute initialized. The result will be that
  3384. // we may fault in this page again and read uninitialized data
  3385. // out of the newly allocated sectors.
  3386. //
  3387. // Make the page dirty so that the cache manager will write it out
  3388. // and update the valid data length.
  3389. //
  3390. VolatileUchar = *((PUCHAR) AttributeValue);
  3391. *((PUCHAR) AttributeValue) = VolatileUchar;
  3392. }
  3393. }
  3394. if (AllocatedLength > 8) {
  3395. Buffer = AllocatedBuffer = NtfsAllocatePool(PagedPool, AllocatedLength );
  3396. } else {
  3397. Buffer = &AttributeNameBuffer;
  3398. }
  3399. //
  3400. // Now update the attribute name in the buffer.
  3401. //
  3402. AttributeName.Buffer = Add2Ptr( Buffer, AttributeNameOffset );
  3403. RtlCopyMemory( AttributeName.Buffer,
  3404. Add2Ptr( Attribute, Attribute->NameOffset ),
  3405. AttributeName.Length );
  3406. //
  3407. // If we are going to write the clusters directly to the disk then copy
  3408. // the bytes into the buffer.
  3409. //
  3410. if (WriteClusters) {
  3411. AttributeValue = Buffer;
  3412. RtlCopyMemory( AttributeValue, NtfsAttributeValue( Attribute ), ValueLength );
  3413. }
  3414. //
  3415. // Now just delete the current record and create it nonresident.
  3416. // Create nonresident with attribute does the right thing if we
  3417. // are being called by MM. Preserve the file record but release
  3418. // any and all allocation.
  3419. //
  3420. NtfsDeleteAttributeRecord( IrpContext,
  3421. Fcb,
  3422. DELETE_LOG_OPERATION | DELETE_RELEASE_ALLOCATION,
  3423. Context );
  3424. NtfsCreateNonresidentWithValue( IrpContext,
  3425. Fcb,
  3426. AttributeTypeCode,
  3427. &AttributeName,
  3428. AttributeValue,
  3429. ValueLength,
  3430. AttributeFlags,
  3431. WriteClusters,
  3432. Scb,
  3433. TRUE,
  3434. Context );
  3435. //
  3436. // If we were passed an attribute context, then we want to
  3437. // reload the context with the new location of the file.
  3438. //
  3439. if (!CleanupLocalContext) {
  3440. NtfsCleanupAttributeContext( IrpContext, Context );
  3441. NtfsInitializeAttributeContext( Context );
  3442. if (!NtfsLookupAttributeByName( IrpContext,
  3443. Fcb,
  3444. &Fcb->FileReference,
  3445. AttributeTypeCode,
  3446. &AttributeName,
  3447. NULL,
  3448. FALSE,
  3449. Context )) {
  3450. DebugTrace( 0, 0, ("Could not find attribute being converted\n") );
  3451. ASSERTMSG("Could not find attribute being converted, About to raise corrupt ", FALSE);
  3452. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3453. }
  3454. }
  3455. } finally {
  3456. DebugUnwind( NtfsConvertToNonresident );
  3457. if (AllocatedBuffer != NULL) {
  3458. NtfsFreePool( AllocatedBuffer );
  3459. }
  3460. if (CleanupLocalContext) {
  3461. NtfsCleanupAttributeContext( IrpContext, Context );
  3462. }
  3463. NtfsUnpinBcb( IrpContext, &ResidentBcb );
  3464. }
  3465. }
  3466. VOID
  3467. NtfsDeleteAttributeRecord (
  3468. IN PIRP_CONTEXT IrpContext,
  3469. IN PFCB Fcb,
  3470. IN ULONG Flags,
  3471. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  3472. )
  3473. /*++
  3474. Routine Description:
  3475. This routine deletes an existing attribute removing it from the file record.
  3476. The caller specifies the attribute to be deleted via the attribute context,
  3477. and must be prepared to clean up this context no matter how this routine
  3478. returns.
  3479. Note that currently this routine does not deallocate any clusters allocated
  3480. to a nonresident attribute; it expects the caller to already have done so.
  3481. Arguments:
  3482. Fcb - Current file.
  3483. Flags - Bitmask that modifies behaviour:
  3484. DELETE_LOG_OPERATION Most callers should specify this, to have the
  3485. change logged. However, we can omit it if we are deleting an entire
  3486. file record, and will be logging that.
  3487. DELETE_RELEASE_FILE_RECORD Indicates that we should release the file record.
  3488. Most callers will not specify this. (Convert to non-resident will omit).
  3489. DELETE_RELEASE_ALLOCATION Indicates that we should free up any allocation.
  3490. Most callers will specify this.
  3491. Context - Attribute Context positioned at the attribute to delete.
  3492. Return Value:
  3493. None.
  3494. --*/
  3495. {
  3496. PATTRIBUTE_RECORD_HEADER Attribute;
  3497. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  3498. PVCB Vcb;
  3499. ATTRIBUTE_TYPE_CODE AttributeTypeCode;
  3500. ASSERT_IRP_CONTEXT( IrpContext );
  3501. ASSERT_FCB( Fcb );
  3502. Vcb = Fcb->Vcb;
  3503. PAGED_CODE();
  3504. DebugTrace( +1, Dbg, ("NtfsDeleteAttribute\n") );
  3505. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  3506. DebugTrace( 0, Dbg, ("Context =%08lx\n", Context) );
  3507. //
  3508. // Get the pointers we need.
  3509. //
  3510. Attribute = NtfsFoundAttribute(Context);
  3511. AttributeTypeCode = Attribute->TypeCode;
  3512. FileRecord = NtfsContainingFileRecord(Context);
  3513. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  3514. if (!NtfsIsAttributeResident( Attribute ) &&
  3515. FlagOn( Flags, DELETE_RELEASE_ALLOCATION)) {
  3516. ASSERT( (NULL == IrpContext->CleanupStructure) || (Fcb == IrpContext->CleanupStructure) );
  3517. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, Context, TRUE, FALSE );
  3518. //
  3519. // Reload our local pointers.
  3520. //
  3521. Attribute = NtfsFoundAttribute(Context);
  3522. FileRecord = NtfsContainingFileRecord(Context);
  3523. }
  3524. //
  3525. // If this is a resident stream then release the quota. Quota for
  3526. // non-resident streams is handled by NtfsDeleteAllocaiton.
  3527. //
  3528. if (NtfsIsTypeCodeSubjectToQuota( Attribute->TypeCode) &&
  3529. (NtfsIsAttributeResident( Attribute ) ||
  3530. (Attribute->Form.Nonresident.LowestVcn == 0))) {
  3531. LONGLONG Delta = -NtfsResidentStreamQuota( Vcb );
  3532. NtfsConditionallyUpdateQuota( IrpContext,
  3533. Fcb,
  3534. &Delta,
  3535. FlagOn( Flags, DELETE_LOG_OPERATION ),
  3536. FALSE );
  3537. }
  3538. //
  3539. // Be sure the attribute is pinned.
  3540. //
  3541. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  3542. //
  3543. // Log the change.
  3544. //
  3545. if (FlagOn( Flags, DELETE_LOG_OPERATION )) {
  3546. FileRecord->Lsn =
  3547. NtfsWriteLog( IrpContext,
  3548. Vcb->MftScb,
  3549. NtfsFoundBcb(Context),
  3550. DeleteAttribute,
  3551. NULL,
  3552. 0,
  3553. CreateAttribute,
  3554. Attribute,
  3555. Attribute->RecordLength,
  3556. NtfsMftOffset( Context ),
  3557. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  3558. 0,
  3559. Vcb->BytesPerFileRecordSegment );
  3560. }
  3561. NtfsRestartRemoveAttribute( IrpContext,
  3562. FileRecord,
  3563. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord) );
  3564. Context->FoundAttribute.AttributeDeleted = TRUE;
  3565. if (FlagOn( Flags, DELETE_LOG_OPERATION ) &&
  3566. (Context->AttributeList.Bcb != NULL)) {
  3567. //
  3568. // Now delete the attribute list entry, if there is one. Do it
  3569. // after freeing space above, because we assume the list has not moved.
  3570. // Note we only do this if DELETE_LOG_OPERATION was specified, assuming
  3571. // that otherwise the entire file is going away anyway, so there is no
  3572. // need to fix up the list.
  3573. //
  3574. NtfsDeleteFromAttributeList( IrpContext, Fcb, Context );
  3575. }
  3576. //
  3577. // Delete the file record if it happened to go empty. (Note that
  3578. // delete file does not call this routine and deletes its own file
  3579. // records.)
  3580. //
  3581. if (FlagOn( Flags, DELETE_RELEASE_FILE_RECORD ) &&
  3582. FileRecord->FirstFreeByte == ((ULONG)FileRecord->FirstAttributeOffset +
  3583. QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE )))) {
  3584. ASSERT( NtfsFullSegmentNumber( &Fcb->FileReference ) ==
  3585. NtfsUnsafeSegmentNumber( &Fcb->FileReference ) );
  3586. NtfsDeallocateMftRecord( IrpContext,
  3587. Vcb,
  3588. (ULONG) LlFileRecordsFromBytes( Vcb, Context->FoundAttribute.MftFileOffset ));
  3589. }
  3590. DebugTrace( -1, Dbg, ("NtfsDeleteAttributeRecord -> VOID\n") );
  3591. return;
  3592. }
  3593. VOID
  3594. NtfsDeleteAllocationFromRecord (
  3595. PIRP_CONTEXT IrpContext,
  3596. IN PFCB Fcb,
  3597. IN PATTRIBUTE_ENUMERATION_CONTEXT Context,
  3598. IN BOOLEAN BreakupAllowed,
  3599. IN BOOLEAN LogIt
  3600. )
  3601. /*++
  3602. Routine Description:
  3603. This routine may be called to delete the allocation of an attribute
  3604. from its attribute record. It does nothing to the attribute record
  3605. itself - the caller must deal with that.
  3606. Arguments:
  3607. Fcb - Current file.
  3608. Context - Attribute enumeration context positioned to the attribute
  3609. whose allocation is to be deleted.
  3610. BreakupAllowed - TRUE if the caller can tolerate breaking up the deletion of
  3611. allocation into multiple transactions, if there are a large
  3612. number of runs.
  3613. LogIt - Indicates if we need to log the change to the mapping pairs.
  3614. Return Value:
  3615. None
  3616. --*/
  3617. {
  3618. PATTRIBUTE_RECORD_HEADER Attribute;
  3619. PSCB Scb;
  3620. UNICODE_STRING AttributeName;
  3621. PFILE_OBJECT TempFileObject;
  3622. BOOLEAN ScbExisted;
  3623. BOOLEAN ScbAcquired = FALSE;
  3624. BOOLEAN ReinitializeContext = FALSE;
  3625. BOOLEAN FcbHadPaging;
  3626. PAGED_CODE();
  3627. //
  3628. // Point to the current attribute.
  3629. //
  3630. Attribute = NtfsFoundAttribute( Context );
  3631. //
  3632. // If the attribute is nonresident, then delete its allocation.
  3633. //
  3634. ASSERT(Attribute->FormCode == NONRESIDENT_FORM);
  3635. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  3636. if (Fcb->PagingIoResource != NULL) {
  3637. FcbHadPaging = TRUE;
  3638. } else {
  3639. FcbHadPaging = FALSE;
  3640. }
  3641. //
  3642. // Decode the file object
  3643. //
  3644. Scb = NtfsCreateScb( IrpContext,
  3645. Fcb,
  3646. Attribute->TypeCode,
  3647. &AttributeName,
  3648. FALSE,
  3649. &ScbExisted );
  3650. try {
  3651. //
  3652. // If the scb is new and that caused a paging resource to be created
  3653. // E.g. a named data stream in a directory raise because our state is now
  3654. // incosistent. We need to acquire that paging resource first
  3655. //
  3656. if (!FcbHadPaging && (Fcb->PagingIoResource != NULL)) {
  3657. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3658. }
  3659. //
  3660. // Acquire the Scb Exclusive
  3661. //
  3662. NtfsAcquireExclusiveScb( IrpContext, Scb );
  3663. ScbAcquired = TRUE;
  3664. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  3665. NtfsUpdateScbFromAttribute( IrpContext, Scb, Attribute );
  3666. }
  3667. //
  3668. // If we created the Scb, then this is the only case where
  3669. // it is legal for us to omit the File Object in the delete
  3670. // allocation call, because there cannot possibly be a section.
  3671. //
  3672. // Also if there is not a section and this thread owns everything
  3673. // for this file then we can neglect the file object.
  3674. //
  3675. if (!ScbExisted ||
  3676. ((Scb->NonpagedScb->SegmentObject.DataSectionObject == NULL) &&
  3677. ((Scb->Header.PagingIoResource == NULL) ||
  3678. (NtfsIsExclusiveScbPagingIo( Scb ))))) {
  3679. TempFileObject = NULL;
  3680. //
  3681. // Else, if there is already a stream file object, we can just
  3682. // use it.
  3683. //
  3684. } else if (Scb->FileObject != NULL) {
  3685. TempFileObject = Scb->FileObject;
  3686. //
  3687. // Else the Scb existed and we did not already have a stream,
  3688. // so we have to create one and delete it on the way out.
  3689. //
  3690. } else {
  3691. NtfsCreateInternalAttributeStream( IrpContext,
  3692. Scb,
  3693. TRUE,
  3694. &NtfsInternalUseFile[DELETEALLOCATIONFROMRECORD_FILE_NUMBER] );
  3695. TempFileObject = Scb->FileObject;
  3696. }
  3697. //
  3698. // Before we make this call, we need to check if we will have to
  3699. // reread the current attribute. This could be necessary if
  3700. // we remove any records for this attribute in the delete case.
  3701. //
  3702. // We only do this under the following conditions.
  3703. //
  3704. // 1 - There is an attribute list present.
  3705. // 2 - There is an entry following the current entry in
  3706. // the attribute list.
  3707. // 3 - The lowest Vcn for that following entry is non-zero.
  3708. //
  3709. if (Context->AttributeList.Bcb != NULL) {
  3710. PATTRIBUTE_LIST_ENTRY NextEntry;
  3711. NextEntry = (PATTRIBUTE_LIST_ENTRY) NtfsGetNextRecord( Context->AttributeList.Entry );
  3712. if (NextEntry < Context->AttributeList.BeyondFinalEntry) {
  3713. if ( NextEntry->LowestVcn != 0) {
  3714. ReinitializeContext = TRUE;
  3715. }
  3716. }
  3717. }
  3718. //
  3719. // Before we delete the allocation and purge the cache - flush any metadata in case
  3720. // we fail at some point later so we don't lose anything due to the purge. This
  3721. // is extra i/o when the delete works as expected but the amount of dirty metadata
  3722. // is limited by both metadata size and the fact its aggressively flushed by cc anyway
  3723. // the only case when this results in a real flush would be when an attribute like
  3724. // a reparse point is very quickly created and deleted
  3725. //
  3726. if (TempFileObject && (!NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) || FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE ))) {
  3727. IO_STATUS_BLOCK Iosb;
  3728. CcFlushCache( TempFileObject->SectionObjectPointer, NULL, 0, &Iosb );
  3729. if (Iosb.Status != STATUS_SUCCESS) {
  3730. NtfsRaiseStatus( IrpContext, Iosb.Status, &Scb->Fcb->FileReference, Scb->Fcb );
  3731. }
  3732. }
  3733. NtfsDeleteAllocation( IrpContext,
  3734. TempFileObject,
  3735. Scb,
  3736. *(PVCN)&Li0,
  3737. MAXLONGLONG,
  3738. LogIt,
  3739. BreakupAllowed );
  3740. //
  3741. // Purge all the data - if any is left in case the cache manager didn't
  3742. // due to the attribute being accessed with the pin interface
  3743. //
  3744. if (TempFileObject) {
  3745. CcPurgeCacheSection( TempFileObject->SectionObjectPointer, NULL, 0, FALSE );
  3746. }
  3747. //
  3748. // Reread the attribute if we need to.
  3749. //
  3750. if (ReinitializeContext) {
  3751. NtfsCleanupAttributeContext( IrpContext, Context );
  3752. NtfsInitializeAttributeContext( Context );
  3753. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, Context );
  3754. }
  3755. } finally {
  3756. DebugUnwind( NtfsDeleteAllocationFromRecord );
  3757. if (ScbAcquired) {
  3758. NtfsReleaseScb( IrpContext, Scb );
  3759. }
  3760. }
  3761. return;
  3762. }
  3763. //
  3764. // This routine is intended for use by allocsup.c. Other callers should use
  3765. // the routines in allocsup.
  3766. //
  3767. BOOLEAN
  3768. NtfsCreateAttributeWithAllocation (
  3769. IN PIRP_CONTEXT IrpContext,
  3770. IN PSCB Scb,
  3771. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  3772. IN PUNICODE_STRING AttributeName OPTIONAL,
  3773. IN USHORT AttributeFlags,
  3774. IN BOOLEAN LogIt,
  3775. IN BOOLEAN UseContext,
  3776. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  3777. )
  3778. /*++
  3779. Routine Description:
  3780. This routine creates the specified attribute with allocation, and returns a
  3781. description of it via the attribute context. If the amount of space being
  3782. created is small enough, we do all of the work here. Otherwise we create the
  3783. initial attribute and call NtfsAddAttributeAllocation to add the rest (in order
  3784. to keep the more complex logic in one place).
  3785. On successful return, it is up to the caller to clean up the attribute
  3786. context.
  3787. Arguments:
  3788. Scb - Current stream.
  3789. AttributeTypeCode - Type code of the attribute to create.
  3790. AttributeName - Optional name for attribute.
  3791. AttributeFlags - Desired flags for the created attribute.
  3792. WhereIndexed - Optionally supplies the file reference to the file where
  3793. this attribute is indexed.
  3794. LogIt - Most callers should specify TRUE, to have the change logged. However,
  3795. we can specify FALSE if we are creating a new file record, and
  3796. will be logging the entire new file record.
  3797. UseContext - Indicates if the context is pointing at the location for the attribute.
  3798. Context - A handle to the created attribute. This context is in a indeterminate
  3799. state on return.
  3800. Return Value:
  3801. BOOLEAN - TRUE if we created the attribute with all the allocation. FALSE
  3802. otherwise. We should only return FALSE if we are creating a file
  3803. and don't want to log any of the changes to the file record.
  3804. --*/
  3805. {
  3806. UCHAR AttributeBuffer[SIZEOF_FULL_NONRES_ATTR_HEADER];
  3807. UCHAR MappingPairsBuffer[64];
  3808. ULONG RecordOffset;
  3809. PATTRIBUTE_RECORD_HEADER Attribute;
  3810. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  3811. ULONG SizeNeeded;
  3812. ULONG AttrSizeNeeded;
  3813. PCHAR MappingPairs;
  3814. ULONG MappingPairsLength;
  3815. LCN Lcn;
  3816. VCN LastVcn;
  3817. VCN HighestVcn;
  3818. PVCB Vcb;
  3819. ULONG Passes = 0;
  3820. PFCB Fcb = Scb->Fcb;
  3821. PNTFS_MCB Mcb = &Scb->Mcb;
  3822. ULONG AttributeHeaderSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  3823. BOOLEAN AllocateAll = TRUE;
  3824. UCHAR CompressionShift = 0;
  3825. ASSERT_IRP_CONTEXT( IrpContext );
  3826. ASSERT_FCB( Fcb );
  3827. PAGED_CODE();
  3828. ASSERT( (AttributeFlags == 0) ||
  3829. NtfsIsTypeCodeCompressible( AttributeTypeCode ));
  3830. Vcb = Fcb->Vcb;
  3831. //
  3832. // Clear out the invalid attribute flags for this volume.
  3833. //
  3834. AttributeFlags &= Vcb->AttributeFlagsMask;
  3835. DebugTrace( +1, Dbg, ("NtfsCreateAttributeWithAllocation\n") );
  3836. DebugTrace( 0, Dbg, ("Mcb = %08lx\n", Mcb) );
  3837. //
  3838. // Calculate the size needed for this attribute. (We say we have
  3839. // Vcb->BigEnoughToMove bytes available as a short cut, since we
  3840. // will extend later as required anyway. It should be extremely
  3841. // unusual that we would really have to extend.)
  3842. //
  3843. MappingPairsLength = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  3844. Vcb->BigEnoughToMove,
  3845. (LONGLONG)0,
  3846. NULL,
  3847. &LastVcn ));
  3848. //
  3849. // Extra work for compressed / sparse files
  3850. //
  3851. if (FlagOn( AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  3852. LONGLONG ClustersInCompressionUnit;
  3853. //
  3854. // Calculate the compression unit size.
  3855. //
  3856. CompressionShift = NTFS_CLUSTERS_PER_COMPRESSION;
  3857. //
  3858. // If this generates a compression unit past 64K then we need to shrink
  3859. // the shift value. This can only happen for sparse files.
  3860. //
  3861. if (!FlagOn( AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  3862. while (Vcb->SparseFileClusters < (ULONG) (1 << CompressionShift)) {
  3863. CompressionShift -= 1;
  3864. }
  3865. }
  3866. ClustersInCompressionUnit = 1 << CompressionShift;
  3867. //
  3868. // Round the LastVcn down to a compression unit and recalc the size
  3869. // needed for the mapping pairs if it was truncated. Note LastVcn = 1 + actual stop pt
  3870. // if we didn't allocate everything in which case it == maxlonglong
  3871. //
  3872. if (LastVcn != MAXLONGLONG) {
  3873. VCN RoundedLastVcn;
  3874. //
  3875. // LastVcn is the cluster beyond allocation or the allocation size i.e stop at 0 we have 1 cluster
  3876. // we want the new allocation to be a compression unit mult so the stop point should be
  3877. // a compression unit rounded allocation - 1
  3878. // Note LastVcn will == RoundedLastVcn + 1 on exit from GetSizeForMappingPairs
  3879. //
  3880. RoundedLastVcn = (LastVcn & ~(ClustersInCompressionUnit - 1)) - 1;
  3881. MappingPairsLength = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  3882. Vcb->BigEnoughToMove,
  3883. (LONGLONG)0,
  3884. &RoundedLastVcn,
  3885. &LastVcn ));
  3886. ASSERT( (LastVcn & (ClustersInCompressionUnit - 1)) == 0 );
  3887. }
  3888. //
  3889. // Remember the size of the attribute header needed for this file.
  3890. //
  3891. AttributeHeaderSize = SIZEOF_FULL_NONRES_ATTR_HEADER;
  3892. }
  3893. SizeNeeded = AttributeHeaderSize +
  3894. MappingPairsLength +
  3895. (ARGUMENT_PRESENT(AttributeName) ?
  3896. QuadAlign( AttributeName->Length ) : 0);
  3897. AttrSizeNeeded = SizeNeeded;
  3898. //
  3899. // Loop until we find all the space we need.
  3900. //
  3901. do {
  3902. //
  3903. // Reinitialize context if this is not the first pass.
  3904. //
  3905. if (Passes != 0) {
  3906. NtfsCleanupAttributeContext( IrpContext, Context );
  3907. NtfsInitializeAttributeContext( Context );
  3908. }
  3909. Passes += 1;
  3910. ASSERT( Passes < 5 );
  3911. //
  3912. // If the attribute is not indexed, then we will position to the
  3913. // insertion point by type code and name.
  3914. //
  3915. if (!UseContext &&
  3916. NtfsLookupAttributeByName( IrpContext,
  3917. Fcb,
  3918. &Fcb->FileReference,
  3919. AttributeTypeCode,
  3920. AttributeName,
  3921. NULL,
  3922. FALSE,
  3923. Context )) {
  3924. DebugTrace( 0, 0,
  3925. ("Nonresident attribute already exists, TypeCode = %08lx\n",
  3926. AttributeTypeCode) );
  3927. ASSERTMSG("Nonresident attribute already exists, About to raise corrupt ", FALSE);
  3928. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3929. }
  3930. //
  3931. // If this attribute is being positioned in the base file record and
  3932. // there is an attribute list then we need to ask for enough space
  3933. // for the attribute list entry now.
  3934. //
  3935. FileRecord = NtfsContainingFileRecord( Context );
  3936. Attribute = NtfsFoundAttribute( Context );
  3937. AttrSizeNeeded = SizeNeeded;
  3938. if (Context->AttributeList.Bcb != NULL
  3939. && (ULONG_PTR) FileRecord <= (ULONG_PTR) Context->AttributeList.AttributeList
  3940. && (ULONG_PTR) Attribute >= (ULONG_PTR) Context->AttributeList.AttributeList) {
  3941. //
  3942. // If the attribute list is non-resident then add a fudge factor of
  3943. // 16 bytes for any new retrieval information.
  3944. //
  3945. if (NtfsIsAttributeResident( Context->AttributeList.AttributeList )) {
  3946. AttrSizeNeeded += QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  3947. + (ARGUMENT_PRESENT( AttributeName ) ?
  3948. (ULONG) AttributeName->Length :
  3949. sizeof( WCHAR )));
  3950. } else {
  3951. AttrSizeNeeded += 0x10;
  3952. }
  3953. }
  3954. UseContext = FALSE;
  3955. //
  3956. // Ask for the space we need.
  3957. //
  3958. } while (!NtfsGetSpaceForAttribute( IrpContext, Fcb, AttrSizeNeeded, Context ));
  3959. //
  3960. // Now get the attribute pointer and fill it in.
  3961. //
  3962. FileRecord = NtfsContainingFileRecord(Context);
  3963. RecordOffset = (ULONG)((PCHAR)NtfsFoundAttribute(Context) - (PCHAR)FileRecord);
  3964. Attribute = (PATTRIBUTE_RECORD_HEADER)AttributeBuffer;
  3965. RtlZeroMemory( Attribute, SIZEOF_FULL_NONRES_ATTR_HEADER );
  3966. Attribute->TypeCode = AttributeTypeCode;
  3967. Attribute->RecordLength = SizeNeeded;
  3968. Attribute->FormCode = NONRESIDENT_FORM;
  3969. //
  3970. // Assume no attribute name, and calculate where the Mapping Pairs
  3971. // will go. (Update below if we are wrong.)
  3972. //
  3973. MappingPairs = Add2Ptr( Attribute, AttributeHeaderSize );
  3974. //
  3975. // If the attribute has a name, take care of that now.
  3976. //
  3977. if (ARGUMENT_PRESENT(AttributeName)
  3978. && AttributeName->Length != 0) {
  3979. ASSERT( AttributeName->Length <= 0x1FF );
  3980. Attribute->NameLength = (UCHAR)(AttributeName->Length / sizeof(WCHAR));
  3981. Attribute->NameOffset = (USHORT)AttributeHeaderSize;
  3982. MappingPairs += QuadAlign( AttributeName->Length );
  3983. }
  3984. Attribute->Flags = AttributeFlags;
  3985. Attribute->Instance = FileRecord->NextAttributeInstance;
  3986. //
  3987. // If someone repeatedly adds and removes attributes from a file record we could
  3988. // hit a case where the sequence number will overflow. In this case we
  3989. // want to scan the file record and find an earlier free instance number.
  3990. //
  3991. if (Attribute->Instance > NTFS_CHECK_INSTANCE_ROLLOVER) {
  3992. Attribute->Instance = NtfsScanForFreeInstance( IrpContext, Vcb, FileRecord );
  3993. }
  3994. //
  3995. // We always need the mapping pairs offset.
  3996. //
  3997. Attribute->Form.Nonresident.MappingPairsOffset = (USHORT)(MappingPairs -
  3998. (PCHAR)Attribute);
  3999. //
  4000. // Set up the compression unit size.
  4001. //
  4002. Attribute->Form.Nonresident.CompressionUnit = CompressionShift;
  4003. //
  4004. // Now we need to point to the real place to build the mapping pairs buffer.
  4005. // If they will not be too big we can use our internal buffer.
  4006. //
  4007. MappingPairs = MappingPairsBuffer;
  4008. if (MappingPairsLength > 64) {
  4009. MappingPairs = NtfsAllocatePool( NonPagedPool, MappingPairsLength );
  4010. }
  4011. *MappingPairs = 0;
  4012. //
  4013. // Find how much space is allocated by finding the last Mcb entry and
  4014. // looking it up. If there are no entries, all of the subsequent
  4015. // fields are already zeroed.
  4016. //
  4017. Attribute->Form.Nonresident.HighestVcn =
  4018. HighestVcn = -1;
  4019. if (NtfsLookupLastNtfsMcbEntry( Mcb, &HighestVcn, &Lcn )) {
  4020. ASSERT_LCN_RANGE_CHECKING( Vcb, Lcn );
  4021. //
  4022. // Now build the mapping pairs in place.
  4023. //
  4024. NtfsBuildMappingPairs( Mcb,
  4025. 0,
  4026. &LastVcn,
  4027. MappingPairs );
  4028. Attribute->Form.Nonresident.HighestVcn = LastVcn;
  4029. //
  4030. // Fill in the nonresident-specific fields. We set the allocation
  4031. // size to only include the Vcn's we included in the mapping pairs.
  4032. //
  4033. Attribute->Form.Nonresident.AllocatedLength =
  4034. Int64ShllMod32((LastVcn + 1 ), Vcb->ClusterShift);
  4035. //
  4036. // The totally allocated field in the Scb will contain the current allocated
  4037. // value for this stream.
  4038. //
  4039. if (FlagOn( AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  4040. ASSERT( Scb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA );
  4041. Attribute->Form.Nonresident.TotalAllocated = Scb->TotalAllocated;
  4042. ASSERT( ((LastVcn + 1) & ((1 << CompressionShift) - 1)) == 0 );
  4043. }
  4044. //
  4045. // We are creating a attribute with zero allocation. Make the Vcn sizes match
  4046. // so we don't make the call below to AddAttributeAllocation.
  4047. //
  4048. } else {
  4049. LastVcn = HighestVcn;
  4050. }
  4051. //
  4052. // Now we will actually create the attribute in place, so that we
  4053. // save copying everything twice, and can point to the final image
  4054. // for the log write below.
  4055. //
  4056. NtfsRestartInsertAttribute( IrpContext,
  4057. FileRecord,
  4058. RecordOffset,
  4059. Attribute,
  4060. AttributeName,
  4061. MappingPairs,
  4062. MappingPairsLength );
  4063. //
  4064. // Finally, log the creation of this attribute
  4065. //
  4066. if (LogIt) {
  4067. //
  4068. // We have actually created the attribute above, but the write
  4069. // log below could fail. The reason we did the create already
  4070. // was to avoid having to allocate pool and copy everything
  4071. // twice (header, name and value). Our normal error recovery
  4072. // just recovers from the log file. But if we fail to write
  4073. // the log, we have to remove this attribute by hand, and
  4074. // raise the condition again.
  4075. //
  4076. try {
  4077. FileRecord->Lsn =
  4078. NtfsWriteLog( IrpContext,
  4079. Vcb->MftScb,
  4080. NtfsFoundBcb(Context),
  4081. CreateAttribute,
  4082. Add2Ptr(FileRecord, RecordOffset),
  4083. Attribute->RecordLength,
  4084. DeleteAttribute,
  4085. NULL,
  4086. 0,
  4087. NtfsMftOffset( Context ),
  4088. RecordOffset,
  4089. 0,
  4090. Vcb->BytesPerFileRecordSegment );
  4091. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  4092. NtfsRestartRemoveAttribute( IrpContext, FileRecord, RecordOffset );
  4093. if (MappingPairs != MappingPairsBuffer) {
  4094. NtfsFreePool( MappingPairs );
  4095. }
  4096. NtfsRaiseStatus( IrpContext, GetExceptionCode(), NULL, NULL );
  4097. }
  4098. }
  4099. //
  4100. // Free the mapping pairs buffer if we allocated one.
  4101. //
  4102. if (MappingPairs != MappingPairsBuffer) {
  4103. NtfsFreePool( MappingPairs );
  4104. }
  4105. //
  4106. // Now add it to the attribute list if necessary
  4107. //
  4108. if (Context->AttributeList.Bcb != NULL) {
  4109. MFT_SEGMENT_REFERENCE SegmentReference;
  4110. *(PLONGLONG)&SegmentReference = LlFileRecordsFromBytes( Vcb, NtfsMftOffset( Context ));
  4111. SegmentReference.SequenceNumber = FileRecord->SequenceNumber;
  4112. NtfsAddToAttributeList( IrpContext, Fcb, SegmentReference, Context );
  4113. }
  4114. //
  4115. // Reflect the current allocation in the scb - in case we take the path below
  4116. //
  4117. Scb->Header.AllocationSize.QuadPart = Attribute->Form.Nonresident.AllocatedLength;
  4118. //
  4119. // We couldn't create all of the mapping for the allocation above. If
  4120. // this is a create then we want to truncate the allocation to what we
  4121. // have already allocated. Otherwise we want to call
  4122. // NtfsAddAttributeAllocation to map the remaining allocation.
  4123. //
  4124. if (LastVcn != HighestVcn) {
  4125. if (LogIt ||
  4126. !NtfsIsTypeCodeUserData( AttributeTypeCode ) ||
  4127. IrpContext->MajorFunction != IRP_MJ_CREATE) {
  4128. NtfsAddAttributeAllocation( IrpContext, Scb, Context, NULL, NULL );
  4129. } else {
  4130. //
  4131. // Truncate away the clusters beyond the last Vcn and set the
  4132. // flag in the IrpContext indicating there is more allocation
  4133. // to do.
  4134. //
  4135. NtfsDeallocateClusters( IrpContext,
  4136. Fcb->Vcb,
  4137. Scb,
  4138. LastVcn + 1,
  4139. MAXLONGLONG,
  4140. NULL );
  4141. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  4142. LastVcn + 1,
  4143. MAXLONGLONG,
  4144. TRUE,
  4145. FALSE );
  4146. if (FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA )) {
  4147. LONGLONG Delta = LlBytesFromClusters( Fcb->Vcb, LastVcn - HighestVcn );
  4148. ASSERT( NtfsIsTypeCodeSubjectToQuota( AttributeTypeCode ));
  4149. ASSERT( NtfsIsTypeCodeSubjectToQuota( Scb->AttributeTypeCode ));
  4150. //
  4151. // Return any quota charged.
  4152. //
  4153. NtfsConditionallyUpdateQuota( IrpContext,
  4154. Fcb,
  4155. &Delta,
  4156. LogIt,
  4157. TRUE );
  4158. }
  4159. AllocateAll = FALSE;
  4160. }
  4161. }
  4162. DebugTrace( -1, Dbg, ("NtfsCreateAttributeWithAllocation -> VOID\n") );
  4163. return AllocateAll;
  4164. }
  4165. //
  4166. // This routine is intended for use by allocsup.c. Other callers should use
  4167. // the routines in allocsup.
  4168. //
  4169. VOID
  4170. NtfsAddAttributeAllocation (
  4171. IN PIRP_CONTEXT IrpContext,
  4172. IN PSCB Scb,
  4173. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context,
  4174. IN PVCN StartingVcn OPTIONAL,
  4175. IN PVCN ClusterCount OPTIONAL
  4176. )
  4177. /*++
  4178. Routine Description:
  4179. This routine adds space to an existing nonresident attribute.
  4180. The caller specifies the attribute to be changed via the attribute context,
  4181. and must be prepared to clean up this context no matter how this routine
  4182. returns.
  4183. This routine procedes in the following steps, whose numbers correspond
  4184. to the numbers in comments below:
  4185. 1. Save a description of the current attribute.
  4186. 2. Figure out how big the attribute would have to be to store all
  4187. of the new run information.
  4188. 3. Find the last occurrence of the attribute, to which the new
  4189. allocation is to be appended.
  4190. 4. If the attribute is getting very large and will not fit, then
  4191. move it to its own file record. In any case grow the attribute
  4192. enough to fit either all of the new allocation, or as much as
  4193. possible.
  4194. 5. Construct the new mapping pairs in place, and log the change.
  4195. 6. If there is still more allocation to describe, then loop to
  4196. create new file records and initialize them to describe additional
  4197. allocation until all of the allocation is described.
  4198. Arguments:
  4199. Scb - Current stream.
  4200. Context - Attribute Context positioned at the attribute to change. Note
  4201. that unlike other routines, this parameter is left in an
  4202. indeterminate state upon return. The caller should plan on
  4203. doing nothing other than cleaning it up.
  4204. StartingVcn - Supplies Vcn to start on, if not the new highest vcn
  4205. ClusterCount - Supplies count of clusters being added, if not the new highest vcn
  4206. Return Value:
  4207. None.
  4208. --*/
  4209. {
  4210. PATTRIBUTE_RECORD_HEADER Attribute;
  4211. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  4212. ULONG NewSize, MappingPairsSize;
  4213. LONG SizeChange;
  4214. PCHAR MappingPairs;
  4215. ULONG SizeAvailable;
  4216. PVCB Vcb;
  4217. VCN LowestVcnRemapped;
  4218. LONGLONG LocalClusterCount;
  4219. VCN OldHighestVcn;
  4220. VCN NewHighestVcn;
  4221. VCN LastVcn;
  4222. BOOLEAN IsHotFixScb;
  4223. PBCB NewBcb = NULL;
  4224. LONGLONG MftReferenceNumber;
  4225. PFCB Fcb = Scb->Fcb;
  4226. PNTFS_MCB Mcb = &Scb->Mcb;
  4227. ULONG AttributeHeaderSize;
  4228. BOOLEAN SingleHole;
  4229. ASSERT_IRP_CONTEXT( IrpContext );
  4230. PAGED_CODE();
  4231. Vcb = Fcb->Vcb;
  4232. DebugTrace( +1, Dbg, ("NtfsAddAttributeAllocation\n") );
  4233. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  4234. DebugTrace( 0, Dbg, ("Mcb = %08lx\n", Mcb) );
  4235. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  4236. //
  4237. // Make a local copy of cluster count, if given. We will use this local
  4238. // copy to determine the shrinking range if we move to a previous file
  4239. // record on a second pass through this loop.
  4240. //
  4241. if (ARGUMENT_PRESENT( ClusterCount )) {
  4242. LocalClusterCount = *ClusterCount;
  4243. }
  4244. while (TRUE) {
  4245. //
  4246. // Make sure the buffer is pinned.
  4247. //
  4248. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  4249. //
  4250. // Make sure we cleanup on the way out
  4251. //
  4252. try {
  4253. //
  4254. // Step 1.
  4255. //
  4256. // Save a description of the attribute to help us look it up
  4257. // again, and to make clones if necessary.
  4258. //
  4259. Attribute = NtfsFoundAttribute(Context);
  4260. //
  4261. // Do some basic verification of the on disk and in memory filesizes
  4262. // If they're disjoint - usually due to a failed abort raise corrupt again
  4263. //
  4264. if ((Attribute->FormCode != NONRESIDENT_FORM) ||
  4265. (Attribute->Form.Nonresident.AllocatedLength != Scb->Header.AllocationSize.QuadPart)) {
  4266. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4267. }
  4268. ASSERT(Attribute->Form.Nonresident.LowestVcn == 0);
  4269. OldHighestVcn = LlClustersFromBytes(Vcb, Attribute->Form.Nonresident.AllocatedLength) - 1;
  4270. //
  4271. // Get the file record pointer.
  4272. //
  4273. FileRecord = NtfsContainingFileRecord( Context );
  4274. //
  4275. // Step 2.
  4276. //
  4277. // Come up with the Vcn we will stop on. If a StartingVcn and ClusterCount
  4278. // were specified, then use them to calculate where we will stop. Otherwise
  4279. // lookup the largest Vcn in this Mcb, so that we will know when we are done.
  4280. // We will also write the new allocation size here.
  4281. //
  4282. {
  4283. LCN TempLcn;
  4284. BOOLEAN UpdateFileSizes = FALSE;
  4285. NewHighestVcn = -1;
  4286. //
  4287. // If a StartingVcn and ClusterCount were specified, then use them.
  4288. //
  4289. if (ARGUMENT_PRESENT(StartingVcn)) {
  4290. ASSERT(ARGUMENT_PRESENT(ClusterCount));
  4291. NewHighestVcn = (*StartingVcn + LocalClusterCount) - 1;
  4292. //
  4293. // If there are no entries in the file record then we have no new
  4294. // sizes to report.
  4295. //
  4296. } else if (NtfsLookupLastNtfsMcbEntry(Mcb, &NewHighestVcn, &TempLcn)) {
  4297. //
  4298. // For compressed files, make sure we are not shrinking allocation
  4299. // size (OldHighestVcn) due to a compression unit that was all zeros
  4300. // and has no allocation. Note, truncates are done in
  4301. // NtfsDeleteAttributeAllocation, so we should not be shrinking the
  4302. // file here.
  4303. //
  4304. // If this is an attribute being written compressed, then always
  4305. // insure that we keep the allocation size on a compression unit
  4306. // boundary, by pushing NewHighestVcn to a boundary - 1.
  4307. //
  4308. if (Scb->CompressionUnit != 0) {
  4309. //
  4310. // Don't shrink the file on this path.
  4311. //
  4312. if (OldHighestVcn > NewHighestVcn) {
  4313. NewHighestVcn = OldHighestVcn;
  4314. }
  4315. ((PLARGE_INTEGER) &NewHighestVcn)->LowPart |= ClustersFromBytes(Vcb, Scb->CompressionUnit) - 1;
  4316. //
  4317. // Make sure we didn't push a hole into the next compression
  4318. // unit. If so then truncate to the current NewHighestVcn. We
  4319. // know this will be on a compression unit boundary.
  4320. //
  4321. if (NewHighestVcn < Scb->Mcb.NtfsMcbArray[Scb->Mcb.NtfsMcbArraySizeInUse - 1].EndingVcn) {
  4322. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  4323. NewHighestVcn + 1,
  4324. MAXLONGLONG,
  4325. TRUE,
  4326. FALSE );
  4327. }
  4328. }
  4329. }
  4330. //
  4331. // Copy the new allocation size into our size structure and
  4332. // update the attribute.
  4333. //
  4334. ASSERT( Scb->Header.AllocationSize.QuadPart != 0 || NewHighestVcn > OldHighestVcn );
  4335. if (NewHighestVcn > OldHighestVcn) {
  4336. Scb->Header.AllocationSize.QuadPart = LlBytesFromClusters(Fcb->Vcb, NewHighestVcn + 1);
  4337. UpdateFileSizes = TRUE;
  4338. }
  4339. //
  4340. // If we moved the allocation size up or the totally allocated does
  4341. // not match the value on the disk (only for compressed files,
  4342. // then update the file sizes.
  4343. //
  4344. if (UpdateFileSizes ||
  4345. (FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ) &&
  4346. (Attribute->Form.Nonresident.TotalAllocated != Scb->TotalAllocated))) {
  4347. NtfsWriteFileSizes( IrpContext,
  4348. Scb,
  4349. &Scb->Header.ValidDataLength.QuadPart,
  4350. FALSE,
  4351. TRUE,
  4352. TRUE );
  4353. }
  4354. }
  4355. //
  4356. // Step 3.
  4357. //
  4358. // Lookup the attribute record at which the change begins, if it is not
  4359. // the first file record that we are looking at.
  4360. //
  4361. if ((Attribute->Form.Nonresident.HighestVcn != OldHighestVcn) &&
  4362. (NewHighestVcn > Attribute->Form.Nonresident.HighestVcn)) {
  4363. NtfsCleanupAttributeContext( IrpContext, Context );
  4364. NtfsInitializeAttributeContext( Context );
  4365. NtfsLookupAttributeForScb( IrpContext, Scb, &NewHighestVcn, Context );
  4366. Attribute = NtfsFoundAttribute(Context);
  4367. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  4368. FileRecord = NtfsContainingFileRecord(Context);
  4369. }
  4370. //
  4371. // Make sure we nuke this range if we get an error, by expanding
  4372. // the error recovery range.
  4373. //
  4374. if (Scb->Mcb.PoolType == PagedPool) {
  4375. if (Scb->ScbSnapshot != NULL) {
  4376. if (Attribute->Form.Nonresident.LowestVcn < Scb->ScbSnapshot->LowestModifiedVcn) {
  4377. Scb->ScbSnapshot->LowestModifiedVcn = Attribute->Form.Nonresident.LowestVcn;
  4378. }
  4379. if (NewHighestVcn > Scb->ScbSnapshot->HighestModifiedVcn) {
  4380. Scb->ScbSnapshot->HighestModifiedVcn = NewHighestVcn;
  4381. }
  4382. if (Attribute->Form.Nonresident.HighestVcn > Scb->ScbSnapshot->HighestModifiedVcn) {
  4383. Scb->ScbSnapshot->HighestModifiedVcn = Attribute->Form.Nonresident.HighestVcn;
  4384. }
  4385. }
  4386. }
  4387. //
  4388. // Remember the last Vcn we will need to create mapping pairs
  4389. // for. We use either NewHighestVcn or the highest Vcn in this
  4390. // file record in the case that we are just inserting a run into
  4391. // an existing record.
  4392. //
  4393. if (ARGUMENT_PRESENT(StartingVcn)) {
  4394. if (Attribute->Form.Nonresident.HighestVcn > NewHighestVcn) {
  4395. NewHighestVcn = Attribute->Form.Nonresident.HighestVcn;
  4396. }
  4397. }
  4398. //
  4399. // Remember the lowest Vcn for this attribute. We will use this to
  4400. // decide whether to loop back and look for an earlier file record.
  4401. //
  4402. LowestVcnRemapped = Attribute->Form.Nonresident.LowestVcn;
  4403. //
  4404. // Remember the header size for this attribute. This will be the
  4405. // mapping pairs offset except for attributes with names.
  4406. //
  4407. AttributeHeaderSize = Attribute->Form.Nonresident.MappingPairsOffset;
  4408. if (Attribute->NameOffset != 0) {
  4409. AttributeHeaderSize = Attribute->NameOffset;
  4410. }
  4411. //
  4412. // If we are making space for a totally allocated field then we
  4413. // want to add space to the non-resident header for these entries.
  4414. // To detect this we know that a starting Vcn was specified and
  4415. // we specified exactly the entire file record. Also the major
  4416. // and minor Irp codes are exactly that for a compression operation.
  4417. //
  4418. if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
  4419. (IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
  4420. (IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp)->Parameters.FileSystemControl.FsControlCode == FSCTL_SET_COMPRESSION) &&
  4421. ARGUMENT_PRESENT( StartingVcn ) &&
  4422. (*StartingVcn == 0) &&
  4423. (LocalClusterCount == Attribute->Form.Nonresident.HighestVcn + 1)) {
  4424. AttributeHeaderSize += sizeof( LONGLONG );
  4425. }
  4426. //
  4427. // Now we must make sure that we never ask for more than can fit in
  4428. // one file record with our attribute and a $END record.
  4429. //
  4430. SizeAvailable = NtfsMaximumAttributeSize(Vcb->BytesPerFileRecordSegment) -
  4431. AttributeHeaderSize -
  4432. QuadAlign( Scb->AttributeName.Length );
  4433. //
  4434. // For the Mft, we will leave a "fudge factor" of 1/8th a file record
  4435. // free to make sure that possible hot fixes do not cause us to
  4436. // break the bootstrap process to finding the mapping for the Mft.
  4437. // Only take this action if we already have an attribute list for
  4438. // the Mft, otherwise we may not detect when we need to move to own
  4439. // record.
  4440. //
  4441. IsHotFixScb = NtfsIsTopLevelHotFixScb( Scb );
  4442. if ((Scb == Vcb->MftScb) &&
  4443. (Context->AttributeList.Bcb != NULL) &&
  4444. !IsHotFixScb &&
  4445. !ARGUMENT_PRESENT( StartingVcn )) {
  4446. SizeAvailable -= Vcb->MftCushion;
  4447. }
  4448. //
  4449. // Calculate how much space is actually needed, independent of whether it will
  4450. // fit.
  4451. //
  4452. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4453. SizeAvailable,
  4454. Attribute->Form.Nonresident.LowestVcn,
  4455. &NewHighestVcn,
  4456. &LastVcn ));
  4457. NewSize = AttributeHeaderSize + QuadAlign( Scb->AttributeName.Length ) + MappingPairsSize;
  4458. SizeChange = (LONG)NewSize - (LONG)Attribute->RecordLength;
  4459. //
  4460. // Step 4.
  4461. //
  4462. // Here we decide if we need to move the attribute to its own record,
  4463. // or whether there is enough room to grow it in place.
  4464. //
  4465. {
  4466. VCN LowestVcn;
  4467. ULONG Pass = 0;
  4468. //
  4469. // It is important to note that at this point, if we will need an
  4470. // attribute list attribute, then we will already have it. This is
  4471. // because we calculated the size needed for the attribute, and moved
  4472. // to a our own record if we were not going to fit and we were not
  4473. // already in a separate record. Later on we assume that the attribute
  4474. // list exists, and just add to it as required. If we didn't move to
  4475. // own record because this is the Mft and this is not file record 0,
  4476. // then we already have an attribute list from a previous split.
  4477. //
  4478. do {
  4479. //
  4480. // If not the first pass, we have to lookup the attribute
  4481. // again. (It looks terrible to have to refind an attribute
  4482. // record other than the first one, but this should never
  4483. // happen, since subsequent attributes should always be in
  4484. // their own record.)
  4485. //
  4486. if (Pass != 0) {
  4487. NtfsCleanupAttributeContext( IrpContext, Context );
  4488. NtfsInitializeAttributeContext( Context );
  4489. if (!NtfsLookupAttributeByName( IrpContext,
  4490. Fcb,
  4491. &Fcb->FileReference,
  4492. Scb->AttributeTypeCode,
  4493. &Scb->AttributeName,
  4494. &LowestVcn,
  4495. FALSE,
  4496. Context )) {
  4497. ASSERT( FALSE );
  4498. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  4499. }
  4500. }
  4501. Pass += 1;
  4502. //
  4503. // Now we have to reload our pointers
  4504. //
  4505. Attribute = NtfsFoundAttribute(Context);
  4506. FileRecord = NtfsContainingFileRecord(Context);
  4507. //
  4508. // If the attribute doesn't fit, and it is not alone in this file
  4509. // record, and the attribute is big enough to move, then we will
  4510. // have to take some special action. Note that if we do not already
  4511. // have an attribute list, then we will only do the move if we are
  4512. // currently big enough to move, otherwise there may not be enough
  4513. // space in MoveAttributeToOwnRecord to create the attribute list,
  4514. // and that could cause us to recursively try to create the attribute
  4515. // list in Create Attribute With Value.
  4516. //
  4517. // We won't make this move if we are dealing with the Mft and it
  4518. // is not file record 0.
  4519. //
  4520. // Also we never move an attribute list to its own record.
  4521. //
  4522. if ((Attribute->TypeCode != $ATTRIBUTE_LIST)
  4523. &&
  4524. (SizeChange > (LONG)(FileRecord->BytesAvailable - FileRecord->FirstFreeByte))
  4525. &&
  4526. ((NtfsFirstAttribute(FileRecord) != Attribute) ||
  4527. (((PATTRIBUTE_RECORD_HEADER)NtfsGetNextRecord(Attribute))->TypeCode != $END))
  4528. &&
  4529. (((NewSize >= Vcb->BigEnoughToMove) && (Context->AttributeList.Bcb != NULL)) ||
  4530. (Attribute->RecordLength >= Vcb->BigEnoughToMove))
  4531. &&
  4532. ((Scb != Vcb->MftScb)
  4533. ||
  4534. (*(PLONGLONG)&FileRecord->BaseFileRecordSegment == 0))) {
  4535. //
  4536. // If we are moving the Mft $DATA out of the base file record, the
  4537. // attribute context will point to the split portion on return.
  4538. // The attribute will only contain previously existing mapping, none
  4539. // of the additional clusters which exist in the Mcb.
  4540. //
  4541. MftReferenceNumber = MoveAttributeToOwnRecord( IrpContext,
  4542. Fcb,
  4543. Attribute,
  4544. Context,
  4545. &NewBcb,
  4546. &FileRecord );
  4547. Attribute = NtfsFirstAttribute(FileRecord);
  4548. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  4549. FileRecord = NtfsContainingFileRecord(Context);
  4550. //
  4551. // If this is the MftScb then we need to recheck the size needed for the
  4552. // mapping pairs. The test for the Mft above guarantees that we
  4553. // were dealing with the base file record.
  4554. //
  4555. if (Scb == Vcb->MftScb) {
  4556. LastVcn = LastVcn - 1;
  4557. //
  4558. // Calculate how much space is now needed given our new LastVcn.
  4559. //
  4560. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4561. SizeAvailable,
  4562. Attribute->Form.Nonresident.LowestVcn,
  4563. &LastVcn,
  4564. &LastVcn ));
  4565. }
  4566. }
  4567. //
  4568. // Remember the lowest Vcn so that we can find this record again
  4569. // if we have to. We capture the value now, after the move attribute
  4570. // in case this is the Mft doing a split and the entire attribute
  4571. // didn't move. We depend on MoveAttributeToOwnRecord to return
  4572. // the new file record for the Mft split.
  4573. //
  4574. LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  4575. //
  4576. // If FALSE is returned, then the space was not allocated and
  4577. // we have to loop back and try again. Second time must work.
  4578. //
  4579. } while (!NtfsChangeAttributeSize( IrpContext,
  4580. Fcb,
  4581. NewSize,
  4582. Context ));
  4583. //
  4584. // Now we have to reload our pointers
  4585. //
  4586. Attribute = NtfsFoundAttribute(Context);
  4587. FileRecord = NtfsContainingFileRecord(Context);
  4588. }
  4589. //
  4590. // Step 5.
  4591. //
  4592. // Get pointer to mapping pairs
  4593. //
  4594. {
  4595. ULONG AttributeOffset;
  4596. ULONG MappingPairsOffset;
  4597. CHAR MappingPairsBuffer[64];
  4598. ULONG RecordOffset = PtrOffset(FileRecord, Attribute);
  4599. //
  4600. // See if it is the case that all mapping pairs will not fit into
  4601. // the current file record, as we may wish to split in the middle
  4602. // rather than at the end as we are currently set up to do.
  4603. // We don't want to take this path if we are splitting the file record
  4604. // because of our limit on the range size due to maximum clusters per
  4605. // range.
  4606. //
  4607. if (LastVcn < NewHighestVcn) {
  4608. if (ARGUMENT_PRESENT( StartingVcn ) &&
  4609. (Scb != Vcb->MftScb)) {
  4610. LONGLONG TempCount;
  4611. //
  4612. // There are two cases to deal with. If the existing file record
  4613. // was a large hole then we may need to limit the size if we
  4614. // are adding allocation. In this case we don't want to simply
  4615. // split at the run being inserted. Otherwise we might end up
  4616. // creating a large number of file records containing only one
  4617. // run (the case where a user fills a large hole by working
  4618. // backwards). Pad the new file record with a portion of the hole.
  4619. //
  4620. if (LastVcn - Attribute->Form.Nonresident.LowestVcn > MAX_CLUSTERS_PER_RANGE) {
  4621. //
  4622. // We don't start within our maximum range from the beginning of the
  4623. // range. If we are within our limit from the end of the range
  4624. // then extend the new range backwards to reach our limit.
  4625. //
  4626. if ((NewHighestVcn - LastVcn + 1) < MAX_CLUSTERS_PER_RANGE) {
  4627. LastVcn = NewHighestVcn - MAX_CLUSTERS_PER_RANGE;
  4628. //
  4629. // Calculate how much space is now needed given our new LastVcn.
  4630. //
  4631. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4632. SizeAvailable,
  4633. Attribute->Form.Nonresident.LowestVcn,
  4634. &LastVcn,
  4635. &LastVcn ));
  4636. }
  4637. //
  4638. //
  4639. // In this case we have run out of room for mapping pairs via
  4640. // an overwrite somewhere in the middle of the file. To avoid
  4641. // shoving a couple mapping pairs off the end over and over, we
  4642. // will arbitrarily split this attribute in the middle. We do
  4643. // so by looking up the lowest and highest Vcns that we are working
  4644. // with and get their indices, then split in the middle.
  4645. //
  4646. } else if (MappingPairsSize > (SizeAvailable >> 1)) {
  4647. LCN TempLcn;
  4648. PVOID RangeLow, RangeHigh;
  4649. ULONG IndexLow, IndexHigh;
  4650. //
  4651. // Get the low and high Mcb indices for these runs.
  4652. //
  4653. if (!NtfsLookupNtfsMcbEntry( Mcb,
  4654. Attribute->Form.Nonresident.LowestVcn,
  4655. NULL,
  4656. NULL,
  4657. NULL,
  4658. NULL,
  4659. &RangeLow,
  4660. &IndexLow )) {
  4661. ASSERT( FALSE );
  4662. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  4663. }
  4664. //
  4665. // Point to the last Vcn we know is actually in the Mcb...
  4666. //
  4667. LastVcn = LastVcn - 1;
  4668. if (!NtfsLookupNtfsMcbEntry( Mcb,
  4669. LastVcn,
  4670. NULL,
  4671. NULL,
  4672. NULL,
  4673. NULL,
  4674. &RangeHigh,
  4675. &IndexHigh )) {
  4676. ASSERT( FALSE );
  4677. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4678. }
  4679. ASSERT(RangeLow == RangeHigh);
  4680. //
  4681. // Calculate the index in the middle.
  4682. //
  4683. IndexLow += (IndexHigh - IndexLow) /2;
  4684. //
  4685. // If we are inserting past the ValidDataToDisk (SplitMcb case),
  4686. // then the allocation behind us may be relatively static, so
  4687. // let's just move with our preallocated space to the new buffer.
  4688. //
  4689. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
  4690. (*StartingVcn >= LlClustersFromBytes(Vcb, Scb->ValidDataToDisk))) {
  4691. //
  4692. // Calculate the index at about 7/8 the way. Hopefully this will
  4693. // move over all of the unallocated piece, while still leaving
  4694. // some small amount of expansion space behind for overwrites.
  4695. //
  4696. IndexLow += (IndexHigh - IndexLow) /2;
  4697. IndexLow += (IndexHigh - IndexLow) /2;
  4698. }
  4699. //
  4700. // Lookup the middle run and use the Last Vcn in that run.
  4701. //
  4702. if (!NtfsGetNextNtfsMcbEntry( Mcb,
  4703. &RangeLow,
  4704. IndexLow,
  4705. &LastVcn,
  4706. &TempLcn,
  4707. &TempCount )) {
  4708. ASSERT( FALSE );
  4709. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4710. }
  4711. LastVcn = (LastVcn + TempCount) - 1;
  4712. //
  4713. // Calculate how much space is now needed given our new LastVcn.
  4714. //
  4715. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4716. SizeAvailable,
  4717. Attribute->Form.Nonresident.LowestVcn,
  4718. &LastVcn,
  4719. &LastVcn ));
  4720. }
  4721. }
  4722. }
  4723. //
  4724. // If we are growing this range, then we need to make sure we fix
  4725. // its definition.
  4726. //
  4727. if ((LastVcn - 1) != Attribute->Form.Nonresident.HighestVcn) {
  4728. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  4729. Attribute->Form.Nonresident.LowestVcn,
  4730. LastVcn - 1,
  4731. FALSE );
  4732. }
  4733. //
  4734. // Point to our local mapping pairs buffer, or allocate one if it is not
  4735. // big enough.
  4736. //
  4737. MappingPairs = MappingPairsBuffer;
  4738. if (MappingPairsSize > 64) {
  4739. MappingPairs = NtfsAllocatePool( NonPagedPool, MappingPairsSize + 8 );
  4740. }
  4741. //
  4742. // Use try-finally to insure we free any pool on the way out.
  4743. //
  4744. try {
  4745. DebugDoit(
  4746. VCN TempVcn;
  4747. TempVcn = LastVcn - 1;
  4748. ASSERT(MappingPairsSize >=
  4749. QuadAlign( NtfsGetSizeForMappingPairs( Mcb, SizeAvailable,
  4750. Attribute->Form.Nonresident.LowestVcn,
  4751. &TempVcn, &LastVcn )));
  4752. );
  4753. //
  4754. // Now add the space in the file record.
  4755. //
  4756. *MappingPairs = 0;
  4757. SingleHole = NtfsBuildMappingPairs( Mcb,
  4758. Attribute->Form.Nonresident.LowestVcn,
  4759. &LastVcn,
  4760. MappingPairs );
  4761. //
  4762. // Now find the first different byte. (Most of the time the
  4763. // cost to do this is probably more than paid for by less
  4764. // logging.)
  4765. //
  4766. AttributeOffset = Attribute->Form.Nonresident.MappingPairsOffset;
  4767. MappingPairsOffset = (ULONG)
  4768. RtlCompareMemory( MappingPairs,
  4769. Add2Ptr(Attribute, AttributeOffset),
  4770. ((Attribute->RecordLength - AttributeOffset) > MappingPairsSize ?
  4771. MappingPairsSize :
  4772. (Attribute->RecordLength - AttributeOffset)));
  4773. AttributeOffset += MappingPairsOffset;
  4774. //
  4775. // Log the change.
  4776. //
  4777. {
  4778. LONGLONG LogOffset;
  4779. if (NewBcb != NULL) {
  4780. //
  4781. // We know the file record number of the new file
  4782. // record. Convert it to a file offset.
  4783. //
  4784. LogOffset = LlBytesFromFileRecords( Vcb, MftReferenceNumber );
  4785. } else {
  4786. LogOffset = NtfsMftOffset( Context );
  4787. }
  4788. FileRecord->Lsn =
  4789. NtfsWriteLog( IrpContext,
  4790. Vcb->MftScb,
  4791. NewBcb != NULL ? NewBcb : NtfsFoundBcb(Context),
  4792. UpdateMappingPairs,
  4793. Add2Ptr(MappingPairs, MappingPairsOffset),
  4794. MappingPairsSize - MappingPairsOffset,
  4795. UpdateMappingPairs,
  4796. Add2Ptr(Attribute, AttributeOffset),
  4797. Attribute->RecordLength - AttributeOffset,
  4798. LogOffset,
  4799. RecordOffset,
  4800. AttributeOffset,
  4801. Vcb->BytesPerFileRecordSegment );
  4802. }
  4803. //
  4804. // Now do the mapping pairs update by calling the same
  4805. // routine called at restart.
  4806. //
  4807. NtfsRestartChangeMapping( IrpContext,
  4808. Vcb,
  4809. FileRecord,
  4810. RecordOffset,
  4811. AttributeOffset,
  4812. Add2Ptr(MappingPairs, MappingPairsOffset),
  4813. MappingPairsSize - MappingPairsOffset );
  4814. } finally {
  4815. if (MappingPairs != MappingPairsBuffer) {
  4816. NtfsFreePool(MappingPairs);
  4817. }
  4818. }
  4819. }
  4820. ASSERT( Attribute->Form.Nonresident.HighestVcn == LastVcn );
  4821. //
  4822. // Check if have spilled into the reserved area of an Mft file record.
  4823. //
  4824. if ((Scb == Vcb->MftScb) &&
  4825. (Context->AttributeList.Bcb != NULL)) {
  4826. if (FileRecord->BytesAvailable - FileRecord->FirstFreeByte < Vcb->MftReserved
  4827. && (*(PLONGLONG)&FileRecord->BaseFileRecordSegment != 0)) {
  4828. NtfsAcquireCheckpoint( IrpContext, Vcb );
  4829. SetFlag( Vcb->MftDefragState,
  4830. VCB_MFT_DEFRAG_EXCESS_MAP | VCB_MFT_DEFRAG_ENABLED );
  4831. NtfsReleaseCheckpoint( IrpContext, Vcb );
  4832. }
  4833. }
  4834. //
  4835. // It is possible that we have a file record which contains nothing but a
  4836. // hole, if that is the case see if we can merge this with either the
  4837. // preceding or following attribute (merge the holes). We will then
  4838. // need to rewrite the mapping for the merged record.
  4839. //
  4840. if (SingleHole &&
  4841. ARGUMENT_PRESENT( StartingVcn ) &&
  4842. (Context->AttributeList.Bcb != NULL) &&
  4843. (Scb != Vcb->MftScb) &&
  4844. ((Attribute->Form.Nonresident.LowestVcn != 0) ||
  4845. (LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart ) !=
  4846. (Attribute->Form.Nonresident.HighestVcn + 1)))) {
  4847. //
  4848. // Call our worker routine to perform the actual work if necessary.
  4849. //
  4850. NtfsMergeFileRecords( IrpContext,
  4851. Scb,
  4852. (BOOLEAN) (LastVcn < NewHighestVcn),
  4853. Context );
  4854. }
  4855. //
  4856. // Step 6.
  4857. //
  4858. // Now loop to create new file records if we have more allocation to
  4859. // describe. We use the highest Vcn of the file record we began with
  4860. // as our stopping point or the last Vcn we are adding.
  4861. //
  4862. // NOTE - The record merge code above uses the same test to see if there is more
  4863. // work to do. If this test changes then the body of the IF statement above also
  4864. // needs to be updated.
  4865. //
  4866. while (LastVcn < NewHighestVcn) {
  4867. MFT_SEGMENT_REFERENCE Reference;
  4868. LONGLONG FileRecordNumber;
  4869. PATTRIBUTE_TYPE_CODE NewEnd;
  4870. //
  4871. // If we get here as the result of a hot fix in the Mft, bail
  4872. // out. We could cause a disconnect in the Mft.
  4873. //
  4874. if (IsHotFixScb && (Scb == Vcb->MftScb)) {
  4875. ExRaiseStatus( STATUS_INTERNAL_ERROR );
  4876. }
  4877. //
  4878. // If we have a large sparse range then we may find that the limit
  4879. // in the base file record is range of clusters in the
  4880. // attribute not the number of runs. In that case the base
  4881. // file record may not have been moved to its own file record
  4882. // and there is no attribute list. We need to create the attribute
  4883. // list before cloning the file record.
  4884. //
  4885. if (Context->AttributeList.Bcb == NULL) {
  4886. NtfsCleanupAttributeContext( IrpContext, Context );
  4887. NtfsInitializeAttributeContext( Context );
  4888. //
  4889. // We don't use the second file reference in this case so
  4890. // it is safe to pass the value in the Fcb.
  4891. //
  4892. CreateAttributeList( IrpContext,
  4893. Fcb,
  4894. FileRecord,
  4895. NULL,
  4896. Fcb->FileReference,
  4897. NULL,
  4898. GetSizeForAttributeList( FileRecord ),
  4899. Context );
  4900. //
  4901. // Now look up the previous attribute again.
  4902. //
  4903. NtfsCleanupAttributeContext( IrpContext, Context );
  4904. NtfsInitializeAttributeContext( Context );
  4905. NtfsLookupAttributeForScb( IrpContext, Scb, &LastVcn, Context );
  4906. }
  4907. //
  4908. // Clone our current file record, and point to our new attribute.
  4909. //
  4910. NtfsUnpinBcb( IrpContext, &NewBcb );
  4911. FileRecord = NtfsCloneFileRecord( IrpContext,
  4912. Fcb,
  4913. (BOOLEAN)(Scb == Vcb->MftScb),
  4914. &NewBcb,
  4915. &Reference );
  4916. Attribute = Add2Ptr( FileRecord, FileRecord->FirstAttributeOffset );
  4917. //
  4918. // Next LowestVcn is the LastVcn + 1
  4919. //
  4920. LastVcn = LastVcn + 1;
  4921. Attribute->Form.Nonresident.LowestVcn = LastVcn;
  4922. //
  4923. // Consistency check for MFT defragging. An mft segment can never
  4924. // describe itself or any piece of the mft before it
  4925. //
  4926. if (Scb == Vcb->MftScb) {
  4927. VCN NewFileVcn;
  4928. if (Vcb->FileRecordsPerCluster == 0) {
  4929. NewFileVcn = NtfsFullSegmentNumber( &Reference ) << Vcb->MftToClusterShift;
  4930. } else {
  4931. NewFileVcn = NtfsFullSegmentNumber( &Reference ) >> Vcb->MftToClusterShift;
  4932. }
  4933. if (LastVcn <= NewFileVcn) {
  4934. #ifdef BENL_DBG
  4935. KdPrint(( "NTFS: selfdescribing mft segment vcn: 0x%I64x, Ref: 0x%I64x\n", LastVcn, NtfsFullSegmentNumber( &Reference ) ));
  4936. #endif
  4937. NtfsRaiseStatus( IrpContext, STATUS_MFT_TOO_FRAGMENTED, NULL, NULL );
  4938. }
  4939. }
  4940. //
  4941. // Calculate the size of the attribute record we will need.
  4942. //
  4943. NewSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER
  4944. + QuadAlign( Scb->AttributeName.Length )
  4945. + QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4946. SizeAvailable,
  4947. LastVcn,
  4948. &NewHighestVcn,
  4949. &LastVcn ));
  4950. //
  4951. // Define the new range.
  4952. //
  4953. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  4954. Attribute->Form.Nonresident.LowestVcn,
  4955. LastVcn - 1,
  4956. FALSE );
  4957. //
  4958. // Initialize the new attribute from the old one.
  4959. //
  4960. Attribute->TypeCode = Scb->AttributeTypeCode;
  4961. Attribute->RecordLength = NewSize;
  4962. Attribute->FormCode = NONRESIDENT_FORM;
  4963. //
  4964. // Assume no attribute name, and calculate where the Mapping Pairs
  4965. // will go. (Update below if we are wrong.)
  4966. //
  4967. MappingPairs = (PCHAR)Attribute + SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  4968. //
  4969. // If the attribute has a name, take care of that now.
  4970. //
  4971. if (Scb->AttributeName.Length != 0) {
  4972. Attribute->NameLength = (UCHAR)(Scb->AttributeName.Length / sizeof(WCHAR));
  4973. Attribute->NameOffset = (USHORT)PtrOffset(Attribute, MappingPairs);
  4974. RtlCopyMemory( MappingPairs,
  4975. Scb->AttributeName.Buffer,
  4976. Scb->AttributeName.Length );
  4977. MappingPairs += QuadAlign( Scb->AttributeName.Length );
  4978. }
  4979. Attribute->Flags = Scb->AttributeFlags;
  4980. Attribute->Instance = FileRecord->NextAttributeInstance++;
  4981. //
  4982. // We always need the mapping pairs offset.
  4983. //
  4984. Attribute->Form.Nonresident.MappingPairsOffset = (USHORT)(MappingPairs -
  4985. (PCHAR)Attribute);
  4986. NewEnd = Add2Ptr( Attribute, Attribute->RecordLength );
  4987. *NewEnd = $END;
  4988. FileRecord->FirstFreeByte = PtrOffset( FileRecord, NewEnd )
  4989. + QuadAlign( sizeof(ATTRIBUTE_TYPE_CODE ));
  4990. //
  4991. // Now add the space in the file record.
  4992. //
  4993. *MappingPairs = 0;
  4994. NtfsBuildMappingPairs( Mcb,
  4995. Attribute->Form.Nonresident.LowestVcn,
  4996. &LastVcn,
  4997. MappingPairs );
  4998. Attribute->Form.Nonresident.HighestVcn = LastVcn;
  4999. //
  5000. // Now log these changes and fix up the first file record.
  5001. //
  5002. FileRecordNumber = NtfsFullSegmentNumber(&Reference);
  5003. //
  5004. // Now log these changes and fix up the first file record.
  5005. //
  5006. FileRecord->Lsn =
  5007. NtfsWriteLog( IrpContext,
  5008. Vcb->MftScb,
  5009. NewBcb,
  5010. InitializeFileRecordSegment,
  5011. FileRecord,
  5012. FileRecord->FirstFreeByte,
  5013. Noop,
  5014. NULL,
  5015. 0,
  5016. LlBytesFromFileRecords( Vcb, FileRecordNumber ),
  5017. 0,
  5018. 0,
  5019. Vcb->BytesPerFileRecordSegment );
  5020. //
  5021. // Finally, we have to add the entry to the attribute list.
  5022. // The routine we have to do this gets most of its inputs
  5023. // out of an attribute context. Our context at this point
  5024. // does not have quite the right information, so we have to
  5025. // update it here before calling AddToAttributeList. (OK
  5026. // this interface ain't pretty, but any normal person would
  5027. // have fallen asleep before getting to this comment!)
  5028. //
  5029. Context->FoundAttribute.FileRecord = FileRecord;
  5030. Context->FoundAttribute.Attribute = Attribute;
  5031. Context->AttributeList.Entry =
  5032. NtfsGetNextRecord(Context->AttributeList.Entry);
  5033. NtfsAddToAttributeList( IrpContext, Fcb, Reference, Context );
  5034. }
  5035. } finally {
  5036. NtfsUnpinBcb( IrpContext, &NewBcb );
  5037. }
  5038. if (!ARGUMENT_PRESENT( StartingVcn) ||
  5039. (LowestVcnRemapped <= *StartingVcn)) {
  5040. break;
  5041. }
  5042. //
  5043. // Move the range to be remapped down.
  5044. //
  5045. LocalClusterCount = LowestVcnRemapped - *StartingVcn;
  5046. NtfsCleanupAttributeContext( IrpContext, Context );
  5047. NtfsInitializeAttributeContext( Context );
  5048. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, Context );
  5049. }
  5050. DebugTrace( -1, Dbg, ("NtfsAddAttributeAllocation -> VOID\n") );
  5051. }
  5052. //
  5053. // This routine is intended for use by allocsup.c. Other callers should use
  5054. // the routines in allocsup.
  5055. //
  5056. VOID
  5057. NtfsDeleteAttributeAllocation (
  5058. IN PIRP_CONTEXT IrpContext,
  5059. IN PSCB Scb,
  5060. IN BOOLEAN LogIt,
  5061. IN PVCN StopOnVcn,
  5062. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context,
  5063. IN BOOLEAN TruncateToVcn
  5064. )
  5065. /*++
  5066. Routine Description:
  5067. This routine deletes an existing nonresident attribute, removing the
  5068. deleted clusters only from the allocation description in the file
  5069. record.
  5070. The caller specifies the attribute to be changed via the attribute context,
  5071. and must be prepared to clean up this context no matter how this routine
  5072. returns. The Scb must already have deleted the clusters in question.
  5073. Arguments:
  5074. Scb - Current attribute, with the clusters in question already deleted from
  5075. the Mcb.
  5076. LogIt - Most callers should specify TRUE, to have the change logged. However,
  5077. we can specify FALSE if we are deleting an entire file record, and
  5078. will be logging that.
  5079. StopOnVcn - Vcn to stop on for regerating mapping
  5080. Context - Attribute Context positioned at the attribute to change.
  5081. TruncateToVcn - Truncate file sizes as appropriate to the Vcn
  5082. Return Value:
  5083. None.
  5084. --*/
  5085. {
  5086. ULONG AttributeOffset;
  5087. ULONG MappingPairsOffset, MappingPairsSize;
  5088. CHAR MappingPairsBuffer[64];
  5089. ULONG RecordOffset;
  5090. PATTRIBUTE_RECORD_HEADER Attribute;
  5091. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  5092. PCHAR MappingPairs;
  5093. VCN LastVcn;
  5094. ULONG NewSize;
  5095. PVCB Vcb;
  5096. ASSERT_IRP_CONTEXT( IrpContext );
  5097. ASSERT_SCB( Scb );
  5098. PAGED_CODE();
  5099. Vcb = Scb->Vcb;
  5100. //
  5101. // For now we only support truncation.
  5102. //
  5103. DebugTrace( +1, Dbg, ("NtfsDeleteAttributeAllocation\n") );
  5104. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  5105. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  5106. //
  5107. // Make sure the buffer is pinned.
  5108. //
  5109. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  5110. Attribute = NtfsFoundAttribute(Context);
  5111. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  5112. //
  5113. // Get the file record pointer.
  5114. //
  5115. FileRecord = NtfsContainingFileRecord(Context);
  5116. RecordOffset = PtrOffset(FileRecord, Attribute);
  5117. //
  5118. // Calculate how much space is actually needed.
  5119. //
  5120. MappingPairsSize = QuadAlign(NtfsGetSizeForMappingPairs( &Scb->Mcb,
  5121. MAXULONG,
  5122. Attribute->Form.Nonresident.LowestVcn,
  5123. StopOnVcn,
  5124. &LastVcn ));
  5125. //
  5126. // Don't assume we understand everything about the size of the current header.
  5127. // Find the offset of the name or the mapping pairs to use as the size
  5128. // of the header.
  5129. //
  5130. NewSize = Attribute->Form.Nonresident.MappingPairsOffset;
  5131. if (Attribute->NameLength != 0) {
  5132. NewSize = Attribute->NameOffset + QuadAlign( Attribute->NameLength << 1 );
  5133. }
  5134. NewSize += MappingPairsSize;
  5135. //
  5136. // If the record could somehow grow by deleting allocation, then
  5137. // NtfsChangeAttributeSize could fail and we would have to copy the
  5138. // loop from NtfsAddAttributeAllocation.
  5139. //
  5140. ASSERT( NewSize <= Attribute->RecordLength );
  5141. MappingPairs = MappingPairsBuffer;
  5142. if (MappingPairsSize > 64) {
  5143. MappingPairs = NtfsAllocatePool( NonPagedPool, MappingPairsSize + 8 );
  5144. }
  5145. //
  5146. // Use try-finally to insure we free any pool on the way out.
  5147. //
  5148. try {
  5149. //
  5150. // Now build up the mapping pairs in the buffer.
  5151. //
  5152. *MappingPairs = 0;
  5153. NtfsBuildMappingPairs( &Scb->Mcb,
  5154. Attribute->Form.Nonresident.LowestVcn,
  5155. &LastVcn,
  5156. MappingPairs );
  5157. //
  5158. // Now find the first different byte. (Most of the time the
  5159. // cost to do this is probably more than paid for by less
  5160. // logging.)
  5161. //
  5162. AttributeOffset = Attribute->Form.Nonresident.MappingPairsOffset;
  5163. MappingPairsOffset = (ULONG)
  5164. RtlCompareMemory( MappingPairs,
  5165. Add2Ptr(Attribute, AttributeOffset),
  5166. MappingPairsSize );
  5167. AttributeOffset += MappingPairsOffset;
  5168. //
  5169. // Log the change.
  5170. //
  5171. if (LogIt) {
  5172. FileRecord->Lsn =
  5173. NtfsWriteLog( IrpContext,
  5174. Vcb->MftScb,
  5175. NtfsFoundBcb(Context),
  5176. UpdateMappingPairs,
  5177. Add2Ptr(MappingPairs, MappingPairsOffset),
  5178. MappingPairsSize - MappingPairsOffset,
  5179. UpdateMappingPairs,
  5180. Add2Ptr(Attribute, AttributeOffset),
  5181. Attribute->RecordLength - AttributeOffset,
  5182. NtfsMftOffset( Context ),
  5183. RecordOffset,
  5184. AttributeOffset,
  5185. Vcb->BytesPerFileRecordSegment );
  5186. }
  5187. //
  5188. // Now do the mapping pairs update by calling the same
  5189. // routine called at restart.
  5190. //
  5191. NtfsRestartChangeMapping( IrpContext,
  5192. Vcb,
  5193. FileRecord,
  5194. RecordOffset,
  5195. AttributeOffset,
  5196. Add2Ptr(MappingPairs, MappingPairsOffset),
  5197. MappingPairsSize - MappingPairsOffset );
  5198. //
  5199. // If we were asked to stop on a Vcn, then the caller does not wish
  5200. // us to modify the Scb. (Currently this is only done one time when
  5201. // the Mft Data attribute no longer fits in the first file record.)
  5202. //
  5203. if (TruncateToVcn) {
  5204. LONGLONG Size;
  5205. //
  5206. // We add one cluster to calculate the allocation size.
  5207. //
  5208. LastVcn = LastVcn + 1;
  5209. Size = LlBytesFromClusters( Vcb, LastVcn );
  5210. Scb->Header.AllocationSize.QuadPart = Size;
  5211. if (Scb->Header.ValidDataLength.QuadPart > Size) {
  5212. Scb->Header.ValidDataLength.QuadPart = Size;
  5213. }
  5214. if (Scb->Header.FileSize.QuadPart > Size) {
  5215. Scb->Header.FileSize.QuadPart = Size;
  5216. }
  5217. //
  5218. // Possibly update ValidDataToDisk which is only nonzero for compressed file
  5219. //
  5220. if (Size < Scb->ValidDataToDisk) {
  5221. Scb->ValidDataToDisk = Size;
  5222. }
  5223. }
  5224. } finally {
  5225. if (MappingPairs != MappingPairsBuffer) {
  5226. NtfsFreePool(MappingPairs);
  5227. }
  5228. }
  5229. DebugTrace( -1, Dbg, ("NtfsDeleteAttributeAllocation -> VOID\n") );
  5230. }
  5231. BOOLEAN
  5232. NtfsIsFileDeleteable (
  5233. IN PIRP_CONTEXT IrpContext,
  5234. IN PFCB Fcb,
  5235. OUT PBOOLEAN NonEmptyIndex
  5236. )
  5237. /*++
  5238. Routine Description:
  5239. This look checks if a file may be deleted by examing all of the index
  5240. attributes to check that they have no children.
  5241. Note that once a file is marked for delete, we must insure
  5242. that none of the conditions checked by this routine are allowed to
  5243. change. For example, once the file is marked for delete, no links
  5244. may be added, and no files may be created in any indices of this
  5245. file.
  5246. Arguments:
  5247. Fcb - Fcb for the file.
  5248. NonEmptyIndex - Address to store TRUE if the file is not deleteable because
  5249. it contains an non-empty indexed attribute.
  5250. Return Value:
  5251. FALSE - If it is not ok to delete the specified file.
  5252. TRUE - If it is ok to delete the specified file.
  5253. --*/
  5254. {
  5255. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  5256. PATTRIBUTE_RECORD_HEADER Attribute;
  5257. BOOLEAN MoreToGo;
  5258. PAGED_CODE();
  5259. NtfsInitializeAttributeContext( &Context );
  5260. try {
  5261. //
  5262. // Enumerate all of the attributes to check whether they may be deleted.
  5263. //
  5264. MoreToGo = NtfsLookupAttributeByCode( IrpContext,
  5265. Fcb,
  5266. &Fcb->FileReference,
  5267. $INDEX_ROOT,
  5268. &Context );
  5269. while (MoreToGo) {
  5270. //
  5271. // Point to the current attribute.
  5272. //
  5273. Attribute = NtfsFoundAttribute( &Context );
  5274. //
  5275. // If the attribute is an index, then it must be empty.
  5276. //
  5277. if (!NtfsIsIndexEmpty( IrpContext, Attribute )) {
  5278. *NonEmptyIndex = TRUE;
  5279. break;
  5280. }
  5281. //
  5282. // Go to the next attribute.
  5283. //
  5284. MoreToGo = NtfsLookupNextAttributeByCode( IrpContext,
  5285. Fcb,
  5286. $INDEX_ROOT,
  5287. &Context );
  5288. }
  5289. } finally {
  5290. DebugUnwind( NtfsIsFileDeleteable );
  5291. NtfsCleanupAttributeContext( IrpContext, &Context );
  5292. }
  5293. //
  5294. // The File is deleteable if scanned the entire file record
  5295. // and found no reasons we could not delete the file.
  5296. //
  5297. return (BOOLEAN)(!MoreToGo);
  5298. }
  5299. VOID
  5300. NtfsDeleteFile (
  5301. IN PIRP_CONTEXT IrpContext,
  5302. IN PFCB Fcb,
  5303. IN PSCB ParentScb,
  5304. IN OUT PBOOLEAN AcquiredParentScb,
  5305. IN OUT PNAME_PAIR NamePair OPTIONAL,
  5306. IN OUT PNTFS_TUNNELED_DATA TunneledData OPTIONAL
  5307. )
  5308. /*++
  5309. Routine Description:
  5310. This routine may be called to see if it is the specified file may
  5311. be deleted from the specified parent (i.e., if the specified parent
  5312. were to be acquired exclusive). This routine should be called from
  5313. fileinfo, to see whether it is ok to mark an open file for delete.
  5314. NamePair will capture the names of the file being deleted if supplied.
  5315. Note that once a file is marked for delete, none of we must insure
  5316. that none of the conditions checked by this routine are allowed to
  5317. change. For example, once the file is marked for delete, no links
  5318. may be added, and no files may be created in any indices of this
  5319. file.
  5320. NOTE: The caller must have the Fcb and ParentScb exclusive to call
  5321. this routine,
  5322. Arguments:
  5323. Fcb - Fcb for the file.
  5324. ParentScb - Parent Scb via which the file was opened, and which would
  5325. be acquired exclusive to perform the delete.
  5326. AcquiredParentScb - On input indicates whether the ParentScb has
  5327. already been acquired. Set to TRUE here if this routine
  5328. acquires the parent.
  5329. TunneledData - Optionally provided to capture the name pair and
  5330. object id of a file so they can be tunneled.
  5331. Return Value:
  5332. None
  5333. --*/
  5334. {
  5335. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  5336. LONGLONG Delta;
  5337. PATTRIBUTE_RECORD_HEADER Attribute;
  5338. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  5339. PVCB Vcb;
  5340. BOOLEAN MoreToGo;
  5341. ULONG RecordNumber;
  5342. NUKEM LocalNuke;
  5343. ULONG Pass;
  5344. ULONG i;
  5345. PNUKEM TempNukem;
  5346. PNUKEM Nukem = &LocalNuke;
  5347. ULONG NukemIndex = 0;
  5348. UCHAR *ObjectId;
  5349. MAP_HANDLE MapHandle;
  5350. BOOLEAN NonresidentAttributeList = FALSE;
  5351. BOOLEAN InitializedMapHandle = FALSE;
  5352. BOOLEAN ReparsePointIsPresent = FALSE;
  5353. BOOLEAN ObjectIdIsPresent = FALSE;
  5354. BOOLEAN LogIt;
  5355. BOOLEAN AcquiredReparseIndex = FALSE;
  5356. BOOLEAN AcquiredObjectIdIndex = FALSE;
  5357. ULONG ForceCheckpointCount;
  5358. ULONG IncomingFileAttributes = 0; // invalid value
  5359. ULONG IncomingReparsePointTag = IO_REPARSE_TAG_RESERVED_ZERO; // invalid value
  5360. PAGED_CODE();
  5361. ASSERT_EXCLUSIVE_FCB( Fcb );
  5362. RtlZeroMemory( &LocalNuke, sizeof(NUKEM) );
  5363. Vcb = Fcb->Vcb;
  5364. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_QUOTA_DISABLE );
  5365. //
  5366. // Remember the values of the file attribute flags and of the reparse tag
  5367. // for abnormal termination recovery.
  5368. //
  5369. IncomingFileAttributes = Fcb->Info.FileAttributes;
  5370. IncomingReparsePointTag = Fcb->Info.ReparsePointTag;
  5371. try {
  5372. //
  5373. // We perform the delete in multiple passes. We need to break this up carefully so that
  5374. // if the delete aborts for any reason that the file is left in a consistent state. The
  5375. // operations are broken up into the following stages.
  5376. //
  5377. //
  5378. // First stage - free any allocation possible
  5379. //
  5380. // - Truncate all user streams to length zero
  5381. //
  5382. // Middle stage - this is required for files with a large number of attributes. Otherwise
  5383. // we can't delete the file records and stay within the log file. Skip this pass
  5384. // for smaller files.
  5385. //
  5386. // - Remove data attributes except unnamed
  5387. //
  5388. // Final stage - no checkpoints allowed until the end of this stage.
  5389. //
  5390. // - Acquire the quota resource if needed.
  5391. // - Remove filenames from Index (for any filename attributes still present)
  5392. // - Remove entry from ObjectId Index
  5393. // - Delete allocation for reparse point and 4.0 style security descriptor
  5394. // - Remove entry from Reparse Index
  5395. // - Delete AttributeList
  5396. // - Log deallocate of file records
  5397. //
  5398. for (Pass = 1; Pass <= 3; Pass += 1) {
  5399. ForceCheckpointCount = 0;
  5400. NtfsInitializeAttributeContext( &Context );
  5401. //
  5402. // Enumerate all of the attributes to check whether they may be deleted.
  5403. //
  5404. MoreToGo = NtfsLookupAttribute( IrpContext,
  5405. Fcb,
  5406. &Fcb->FileReference,
  5407. &Context );
  5408. //
  5409. // Log the change to the mapping pairs if there is an attribute list.
  5410. //
  5411. LogIt = FALSE;
  5412. if (Context.AttributeList.Bcb != NULL) {
  5413. LogIt = TRUE;
  5414. }
  5415. //
  5416. // Remember if we want to log the changes to non-resident attributes.
  5417. //
  5418. while (MoreToGo) {
  5419. //
  5420. // Point to the current attribute.
  5421. //
  5422. Attribute = NtfsFoundAttribute( &Context );
  5423. //
  5424. // All indices must be empty.
  5425. //
  5426. ASSERT( (Attribute->TypeCode != $INDEX_ROOT) ||
  5427. NtfsIsIndexEmpty( IrpContext, Attribute ));
  5428. //
  5429. // Remember when the $REPARSE_POINT attribute is present.
  5430. // When it is non-resident we delete it in pass 3.
  5431. // The entry in the $Reparse index always gets deleted in pass 3.
  5432. // We have to delete the index entry before deleting the allocation.
  5433. //
  5434. if (Attribute->TypeCode == $REPARSE_POINT) {
  5435. ReparsePointIsPresent = TRUE;
  5436. if (Pass == 3) {
  5437. //
  5438. // If this is the $REPARSE_POINT attribute, delete now the appropriate
  5439. // entry from the $Reparse index.
  5440. //
  5441. NTSTATUS Status = STATUS_SUCCESS;
  5442. INDEX_KEY IndexKey;
  5443. INDEX_ROW IndexRow;
  5444. REPARSE_INDEX_KEY KeyValue;
  5445. PREPARSE_DATA_BUFFER ReparseBuffer = NULL;
  5446. PVOID AttributeData = NULL;
  5447. PBCB Bcb = NULL;
  5448. ULONG Length = 0;
  5449. //
  5450. // Point to the attribute data.
  5451. //
  5452. if (NtfsIsAttributeResident( Attribute )) {
  5453. //
  5454. // Point to the value of the arribute.
  5455. //
  5456. AttributeData = NtfsAttributeValue( Attribute );
  5457. DebugTrace( 0, Dbg, ("Existing attribute is resident.\n") );
  5458. } else {
  5459. //
  5460. // Map the attribute list if the attribute is non-resident.
  5461. // Otherwise the attribute is already mapped and we have a Bcb
  5462. // in the attribute context.
  5463. //
  5464. DebugTrace( 0, Dbg, ("Existing attribute is non-resident.\n") );
  5465. if (Attribute->Form.Nonresident.FileSize > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) {
  5466. NtfsRaiseStatus( IrpContext,STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  5467. }
  5468. NtfsMapAttributeValue( IrpContext,
  5469. Fcb,
  5470. &AttributeData, // point to the value
  5471. &Length,
  5472. &Bcb,
  5473. &Context );
  5474. //
  5475. // Unpin the Bcb. The unpin routine checks for NULL.
  5476. //
  5477. NtfsUnpinBcb( IrpContext, &Bcb );
  5478. }
  5479. //
  5480. // Set the pointer to extract the reparse point tag.
  5481. //
  5482. ReparseBuffer = (PREPARSE_DATA_BUFFER)AttributeData;
  5483. //
  5484. // Verify that this file is in the reparse point index and delete it.
  5485. //
  5486. KeyValue.FileReparseTag = ReparseBuffer->ReparseTag;
  5487. KeyValue.FileId = *(PLARGE_INTEGER)&Fcb->FileReference;
  5488. IndexKey.Key = (PVOID)&KeyValue;
  5489. IndexKey.KeyLength = sizeof(KeyValue);
  5490. NtOfsInitializeMapHandle( &MapHandle );
  5491. InitializedMapHandle = TRUE;
  5492. //
  5493. // All of the resources should have been acquired.
  5494. //
  5495. ASSERT( *AcquiredParentScb );
  5496. ASSERT( AcquiredReparseIndex );
  5497. //
  5498. // NtOfsFindRecord will return an error status if the key is not found.
  5499. //
  5500. Status = NtOfsFindRecord( IrpContext,
  5501. Vcb->ReparsePointTableScb,
  5502. &IndexKey,
  5503. &IndexRow,
  5504. &MapHandle,
  5505. NULL );
  5506. if (!NT_SUCCESS(Status)) {
  5507. //
  5508. // Should not happen. The reparse point should be in the index.
  5509. //
  5510. DebugTrace( 0, Dbg, ("Record not found in the reparse point index.\n") );
  5511. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  5512. }
  5513. //
  5514. // Remove the entry from the reparse point index.
  5515. //
  5516. NtOfsDeleteRecords( IrpContext,
  5517. Vcb->ReparsePointTableScb,
  5518. 1, // deleting one record from the index
  5519. &IndexKey );
  5520. }
  5521. }
  5522. //
  5523. // If the attribute is nonresident, then delete its allocation.
  5524. // We only need to make the NtfsDeleteAllocation from record for
  5525. // the attribute with lowest Vcn of zero. This will deallocate
  5526. // all of the clusters for the file.
  5527. //
  5528. if (Attribute->FormCode == NONRESIDENT_FORM) {
  5529. if ((Attribute->Form.Nonresident.LowestVcn == 0) &&
  5530. (Attribute->Form.Nonresident.AllocatedLength != 0)) {
  5531. if (Pass == 1) {
  5532. //
  5533. // Postpone till pass 3 the deletion of the non-resident attribute in
  5534. // the case of a security descriptor and a reparse point.
  5535. //
  5536. if ((Attribute->TypeCode != $SECURITY_DESCRIPTOR) &&
  5537. (Attribute->TypeCode != $REPARSE_POINT)) {
  5538. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, &Context, TRUE, LogIt );
  5539. //
  5540. // Make sure we count the number of these calls we make. Force a
  5541. // periodic checkpoint on a file with a lot of streams. We might have
  5542. // thousands of streams whose allocations won't force a checkpoint. we
  5543. // could spin indefinitely trying to delete this file. Lets force
  5544. // a checkpoint on a regular basis.
  5545. //
  5546. ForceCheckpointCount += 1;
  5547. if (ForceCheckpointCount > 10) {
  5548. NtfsCheckpointCurrentTransaction( IrpContext );
  5549. ForceCheckpointCount = 0;
  5550. }
  5551. //
  5552. // Reload the attribute pointer in the event it
  5553. // was remapped.
  5554. //
  5555. Attribute = NtfsFoundAttribute( &Context );
  5556. }
  5557. } else if (Pass == 3) {
  5558. //
  5559. // Now, in pass 3, delete the security descriptor and the reparse
  5560. // point attributes when they are non-residents.
  5561. //
  5562. if ((Attribute->TypeCode == $SECURITY_DESCRIPTOR) ||
  5563. (Attribute->TypeCode == $REPARSE_POINT)) {
  5564. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, &Context, FALSE, LogIt );
  5565. //
  5566. // Reload the attribute pointer in the event it
  5567. // was remapped.
  5568. //
  5569. Attribute = NtfsFoundAttribute( &Context );
  5570. }
  5571. }
  5572. }
  5573. } else {
  5574. //
  5575. // If we are at the start of Pass 3 then make sure we have the parent
  5576. // acquired and can perform any necessary quota operations.
  5577. //
  5578. if ((Attribute->TypeCode == $STANDARD_INFORMATION) &&
  5579. (Pass == 3)) {
  5580. if (!*AcquiredParentScb ||
  5581. NtfsPerformQuotaOperation( Fcb ) ||
  5582. ReparsePointIsPresent ||
  5583. ObjectIdIsPresent) {
  5584. //
  5585. // See if we need to acquire any resources, and if so, get
  5586. // them in the right order. We need to do this carefully.
  5587. // If the Mft is acquired by this thread then checkpoint
  5588. // the transaction and release the Mft before we go any
  5589. // further.
  5590. //
  5591. if (Vcb->MftScb->Fcb->ExclusiveFcbLinks.Flink != NULL &&
  5592. NtfsIsExclusiveScb( Vcb->MftScb )) {
  5593. NtfsCheckpointCurrentTransaction( IrpContext );
  5594. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  5595. }
  5596. ASSERT(!NtfsIsExclusiveScb( Vcb->MftScb ));
  5597. //
  5598. // Now acquire the parent if not already acquired.
  5599. //
  5600. if (!*AcquiredParentScb) {
  5601. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  5602. *AcquiredParentScb = TRUE;
  5603. }
  5604. if (ObjectIdIsPresent) {
  5605. NtfsAcquireExclusiveScb( IrpContext, Vcb->ObjectIdTableScb );
  5606. AcquiredObjectIdIndex = TRUE;
  5607. }
  5608. //
  5609. // Also acquire reparse & object id if necessary in
  5610. // the correct bottom-up Vcb order.
  5611. //
  5612. if (ReparsePointIsPresent && !AcquiredReparseIndex) {
  5613. NtfsAcquireExclusiveScb( IrpContext, Vcb->ReparsePointTableScb );
  5614. AcquiredReparseIndex = TRUE;
  5615. }
  5616. //
  5617. // We may acquire the quota index in here.
  5618. //
  5619. if (Attribute->Form.Resident.ValueLength == sizeof( STANDARD_INFORMATION )) {
  5620. //
  5621. // Capture all of the user's quota for this file.
  5622. //
  5623. //
  5624. // The quota resource cannot be acquired before the streams
  5625. // are deleted because we can deadlock with the mapped page
  5626. // writer when CcSetFileSizes is called.
  5627. //
  5628. if (NtfsPerformQuotaOperation( Fcb )) {
  5629. ASSERT(!NtfsIsExclusiveScb( Vcb->MftScb ));
  5630. Delta = -(LONGLONG) ((PSTANDARD_INFORMATION)
  5631. NtfsAttributeValue( Attribute ))->QuotaCharged;
  5632. NtfsUpdateFileQuota( IrpContext,
  5633. Fcb,
  5634. &Delta,
  5635. TRUE,
  5636. FALSE );
  5637. }
  5638. }
  5639. }
  5640. }
  5641. }
  5642. //
  5643. // If we are deleting the object id attribute, we need to
  5644. // update the object id index as well.
  5645. //
  5646. if (Attribute->TypeCode == $OBJECT_ID) {
  5647. if (Pass == 1) {
  5648. //
  5649. // On pass 1, it is only necessary to remember we have
  5650. // an object id so we remember to acquire the oid index
  5651. // on pass 3.
  5652. //
  5653. ObjectIdIsPresent = TRUE;
  5654. } else if (Pass == 3) {
  5655. //
  5656. // We'd better be holding the object id index and parent
  5657. // directory already, or else there is a potential for
  5658. // deadlock.
  5659. //
  5660. ASSERT(NtfsIsExclusiveScb( Vcb->ObjectIdTableScb ));
  5661. ASSERT(*AcquiredParentScb);
  5662. if (ARGUMENT_PRESENT(TunneledData)) {
  5663. //
  5664. // We need to lookup the object id so we can tunnel it.
  5665. //
  5666. TunneledData->HasObjectId = TRUE;
  5667. ObjectId = (UCHAR *) NtfsAttributeValue( Attribute );
  5668. RtlCopyMemory( TunneledData->ObjectIdBuffer.ObjectId,
  5669. ObjectId,
  5670. sizeof(TunneledData->ObjectIdBuffer.ObjectId) );
  5671. NtfsGetObjectIdExtendedInfo( IrpContext,
  5672. Fcb->Vcb,
  5673. ObjectId,
  5674. TunneledData->ObjectIdBuffer.ExtendedInfo );
  5675. }
  5676. //
  5677. // We need to delete the object id from the index
  5678. // to keep everything consistent. The FALSE means
  5679. // don't delete the attribute itself, that would
  5680. // lead to some ugly recursion.
  5681. //
  5682. NtfsDeleteObjectIdInternal( IrpContext,
  5683. Fcb,
  5684. Vcb,
  5685. FALSE );
  5686. }
  5687. }
  5688. //
  5689. // If we are in the second pass then remove extra named data streams.
  5690. //
  5691. if (Pass == 2) {
  5692. //
  5693. // The record is large enough to consider. Only do this for named
  5694. // data streams
  5695. //
  5696. if ((Attribute->TypeCode == $DATA) &&
  5697. (Attribute->NameLength != 0)) {
  5698. PSCB DeleteScb;
  5699. UNICODE_STRING AttributeName;
  5700. //
  5701. // Get the Scb so we can mark it as deleted.
  5702. //
  5703. AttributeName.Buffer = Add2Ptr( Attribute, Attribute->NameOffset );
  5704. AttributeName.Length = Attribute->NameLength * sizeof( WCHAR );
  5705. DeleteScb = NtfsCreateScb( IrpContext,
  5706. Fcb,
  5707. Attribute->TypeCode,
  5708. &AttributeName,
  5709. TRUE,
  5710. NULL );
  5711. NtfsDeleteAttributeRecord( IrpContext,
  5712. Fcb,
  5713. (DELETE_LOG_OPERATION |
  5714. DELETE_RELEASE_FILE_RECORD),
  5715. &Context );
  5716. if (DeleteScb != NULL) {
  5717. SetFlag( DeleteScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  5718. DeleteScb->AttributeTypeCode = $UNUSED;
  5719. }
  5720. //
  5721. // Let's checkpoint periodically after each attribute. Since
  5722. // the attribute list is large and we are removing and
  5723. // entry from the beginning of the list the log records
  5724. // can be very large.
  5725. //
  5726. NtfsCheckpointCurrentTransaction( IrpContext );
  5727. }
  5728. } else if (Pass == 3) {
  5729. //
  5730. // If the attribute is a file name, then it must be from our
  5731. // caller's parent directory, or else we cannot delete.
  5732. //
  5733. if (Attribute->TypeCode == $FILE_NAME) {
  5734. PFILE_NAME FileName;
  5735. FileName = (PFILE_NAME)NtfsAttributeValue( Attribute );
  5736. ASSERT( ARGUMENT_PRESENT( ParentScb ));
  5737. ASSERT(NtfsEqualMftRef(&FileName->ParentDirectory,
  5738. &ParentScb->Fcb->FileReference));
  5739. if (ARGUMENT_PRESENT(NamePair)) {
  5740. //
  5741. // Squirrel away names
  5742. //
  5743. NtfsCopyNameToNamePair( NamePair,
  5744. FileName->FileName,
  5745. FileName->FileNameLength,
  5746. FileName->Flags );
  5747. }
  5748. NtfsDeleteIndexEntry( IrpContext,
  5749. ParentScb,
  5750. (PVOID)FileName,
  5751. &Fcb->FileReference );
  5752. }
  5753. //
  5754. // If this file record is not already deleted, then do it now.
  5755. // Note, we are counting on its contents not to change.
  5756. //
  5757. FileRecord = NtfsContainingFileRecord( &Context );
  5758. //
  5759. // See if this is the same as the last one we remembered, else remember it.
  5760. //
  5761. if (Context.AttributeList.Bcb != NULL) {
  5762. RecordNumber = NtfsUnsafeSegmentNumber( &Context.AttributeList.Entry->SegmentReference );
  5763. } else {
  5764. RecordNumber = NtfsUnsafeSegmentNumber( &Fcb->FileReference );
  5765. }
  5766. //
  5767. // Now loop to see if we already remembered this record.
  5768. // This reduces our pool allocation and also prevents us
  5769. // from deleting file records twice.
  5770. //
  5771. TempNukem = Nukem;
  5772. while (TempNukem != NULL) {
  5773. for (i = 0; i < 4; i++) {
  5774. if (TempNukem->RecordNumbers[i] == RecordNumber) {
  5775. RecordNumber = 0;
  5776. break;
  5777. }
  5778. }
  5779. TempNukem = TempNukem->Next;
  5780. }
  5781. if (RecordNumber != 0) {
  5782. //
  5783. // Is the list full? If so allocate and initialize a new one.
  5784. //
  5785. if (NukemIndex > 3) {
  5786. TempNukem = (PNUKEM)ExAllocateFromPagedLookasideList( &NtfsNukemLookasideList );
  5787. RtlZeroMemory( TempNukem, sizeof(NUKEM) );
  5788. TempNukem->Next = Nukem;
  5789. Nukem = TempNukem;
  5790. NukemIndex = 0;
  5791. }
  5792. //
  5793. // Remember to delete this guy. (Note we can possibly list someone
  5794. // more than once, but NtfsDeleteFileRecord handles that.)
  5795. //
  5796. Nukem->RecordNumbers[NukemIndex] = RecordNumber;
  5797. NukemIndex += 1;
  5798. }
  5799. //
  5800. // When we have the first attribute, check for the existance of
  5801. // a non-resident attribute list.
  5802. //
  5803. } else if ((Attribute->TypeCode == $STANDARD_INFORMATION) &&
  5804. (Context.AttributeList.Bcb != NULL) &&
  5805. (!NtfsIsAttributeResident( Context.AttributeList.AttributeList ))) {
  5806. NonresidentAttributeList = TRUE;
  5807. }
  5808. //
  5809. // Go to the next attribute.
  5810. //
  5811. MoreToGo = NtfsLookupNextAttribute( IrpContext,
  5812. Fcb,
  5813. &Context );
  5814. }
  5815. NtfsCleanupAttributeContext( IrpContext, &Context );
  5816. //
  5817. // Skip pass 2 unless there is a large attribute list.
  5818. //
  5819. if (Pass == 1) {
  5820. if (RtlPointerToOffset( Context.AttributeList.FirstEntry,
  5821. Context.AttributeList.BeyondFinalEntry ) > 0x1000) {
  5822. //
  5823. // Go ahead and checkpoint now so we will make progress in Pass 2.
  5824. //
  5825. NtfsCheckpointCurrentTransaction( IrpContext );
  5826. } else {
  5827. //
  5828. // Skip pass 2.
  5829. //
  5830. Pass += 1;
  5831. }
  5832. }
  5833. }
  5834. //
  5835. // Handle the unusual nonresident attribute list case
  5836. //
  5837. if (NonresidentAttributeList) {
  5838. NtfsInitializeAttributeContext( &Context );
  5839. NtfsLookupAttributeByCode( IrpContext,
  5840. Fcb,
  5841. &Fcb->FileReference,
  5842. $ATTRIBUTE_LIST,
  5843. &Context );
  5844. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, &Context, FALSE, FALSE );
  5845. NtfsCleanupAttributeContext( IrpContext, &Context );
  5846. }
  5847. //
  5848. // Post the delete to the Usn Journal.
  5849. //
  5850. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_FILE_DELETE | USN_REASON_CLOSE );
  5851. //
  5852. // Now loop to delete the file records.
  5853. //
  5854. while (Nukem != NULL) {
  5855. for (i = 0; i < 4; i++) {
  5856. if (Nukem->RecordNumbers[i] != 0) {
  5857. NtfsDeallocateMftRecord( IrpContext,
  5858. Vcb,
  5859. Nukem->RecordNumbers[i] );
  5860. }
  5861. }
  5862. TempNukem = Nukem->Next;
  5863. if (Nukem != &LocalNuke) {
  5864. ExFreeToPagedLookasideList( &NtfsNukemLookasideList, Nukem );
  5865. }
  5866. Nukem = TempNukem;
  5867. }
  5868. } finally {
  5869. DebugUnwind( NtfsDeleteFile );
  5870. NtfsCleanupAttributeContext( IrpContext, &Context );
  5871. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_QUOTA_DISABLE );
  5872. //
  5873. // Release the reparse point index Scb and the map handle.
  5874. //
  5875. if (AcquiredReparseIndex) {
  5876. NtfsReleaseScb( IrpContext, Vcb->ReparsePointTableScb );
  5877. }
  5878. if (InitializedMapHandle) {
  5879. NtOfsReleaseMap( IrpContext, &MapHandle );
  5880. }
  5881. //
  5882. // Drop the object id index if necessary.
  5883. //
  5884. if (AcquiredObjectIdIndex) {
  5885. NtfsReleaseScb( IrpContext, Vcb->ObjectIdTableScb );
  5886. }
  5887. //
  5888. // Need to roll-back the value of the file attributes and the reparse point
  5889. // flag in case of problems.
  5890. //
  5891. if (AbnormalTermination()) {
  5892. Fcb->Info.FileAttributes = IncomingFileAttributes;
  5893. Fcb->Info.ReparsePointTag = IncomingReparsePointTag;
  5894. }
  5895. }
  5896. return;
  5897. }
  5898. VOID
  5899. NtfsPrepareForUpdateDuplicate (
  5900. IN PIRP_CONTEXT IrpContext,
  5901. IN PFCB Fcb,
  5902. IN OUT PLCB *Lcb,
  5903. IN OUT PSCB *ParentScb,
  5904. IN BOOLEAN AcquireShared
  5905. )
  5906. /*++
  5907. Routine Description:
  5908. This routine is called to prepare for updating the duplicate information.
  5909. At the conclusion of this routine we will have the Lcb and Scb for the
  5910. update along with the Scb acquired. This routine will look at
  5911. the existing values for the input parameters in deciding what actions
  5912. need to be done.
  5913. Arguments:
  5914. Fcb - Fcb for the file. The file must already be acquired exclusively.
  5915. Lcb - This is the address to store the link to update. This may already
  5916. have a value.
  5917. ParentScb - This is the address to store the parent Scb for the update.
  5918. This may already point to a valid Scb.
  5919. AcquireShared - Indicates how to acquire the parent Scb.
  5920. Return Value:
  5921. None
  5922. --*/
  5923. {
  5924. PLIST_ENTRY Links;
  5925. PLCB ThisLcb;
  5926. PAGED_CODE();
  5927. //
  5928. // Start by trying to guarantee we have an Lcb for the update.
  5929. //
  5930. if (*Lcb == NULL) {
  5931. Links = Fcb->LcbQueue.Flink;
  5932. while (Links != &Fcb->LcbQueue) {
  5933. ThisLcb = CONTAINING_RECORD( Links,
  5934. LCB,
  5935. FcbLinks );
  5936. //
  5937. // We can use this link if it is still present on the
  5938. // disk and if we were passed a parent Scb, it matches
  5939. // the one for this Lcb.
  5940. //
  5941. if (!FlagOn( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE ) &&
  5942. ((*ParentScb == NULL) ||
  5943. (*ParentScb == ThisLcb->Scb) ||
  5944. ((ThisLcb == Fcb->Vcb->RootLcb) &&
  5945. (*ParentScb == Fcb->Vcb->RootIndexScb)))) {
  5946. *Lcb = ThisLcb;
  5947. break;
  5948. }
  5949. Links = Links->Flink;
  5950. }
  5951. }
  5952. //
  5953. // If we have an Lcb, try to find the correct Scb.
  5954. //
  5955. if ((*Lcb != NULL) && (*ParentScb == NULL)) {
  5956. if (*Lcb == Fcb->Vcb->RootLcb) {
  5957. *ParentScb = Fcb->Vcb->RootIndexScb;
  5958. } else {
  5959. *ParentScb = (*Lcb)->Scb;
  5960. }
  5961. }
  5962. //
  5963. // Acquire the parent Scb and put it in the transaction queue in the
  5964. // IrpContext.
  5965. //
  5966. if (*ParentScb != NULL) {
  5967. if (AcquireShared) {
  5968. NtfsAcquireSharedScbForTransaction( IrpContext, *ParentScb );
  5969. } else {
  5970. NtfsAcquireExclusiveScb( IrpContext, *ParentScb );
  5971. }
  5972. }
  5973. return;
  5974. }
  5975. VOID
  5976. NtfsUpdateDuplicateInfo (
  5977. IN PIRP_CONTEXT IrpContext,
  5978. IN PFCB Fcb,
  5979. IN PLCB Lcb OPTIONAL,
  5980. IN PSCB ParentScb OPTIONAL
  5981. )
  5982. /*++
  5983. Routine Description:
  5984. This routine is called to update the duplicate information for a file
  5985. in the duplicated information of its parent. If the Lcb is specified
  5986. then this parent is the parent to update. If the link is either an
  5987. NTFS or DOS only link then we must update the complementary link as
  5988. well. If no Lcb is specified then this open was by file id or the
  5989. original link has been deleted. In that case we will try to find a different
  5990. link to update.
  5991. Arguments:
  5992. Fcb - Fcb for the file.
  5993. Lcb - This is the link to update. Specified only if this is not
  5994. an open by Id operation.
  5995. ParentScb - This is the parent directory for the Lcb link if specified.
  5996. Return Value:
  5997. None
  5998. --*/
  5999. {
  6000. PQUICK_INDEX QuickIndex = NULL;
  6001. UCHAR Buffer[sizeof( FILE_NAME ) + 11 * sizeof( WCHAR )];
  6002. PFILE_NAME FileNameAttr;
  6003. BOOLEAN AcquiredFcbTable = FALSE;
  6004. BOOLEAN ReturnedExistingFcb = TRUE;
  6005. BOOLEAN Found;
  6006. UCHAR FileNameFlags;
  6007. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  6008. PVCB Vcb = Fcb->Vcb;
  6009. PFCB ParentFcb = NULL;
  6010. PAGED_CODE();
  6011. ASSERT_EXCLUSIVE_FCB( Fcb );
  6012. //
  6013. // Return immediately if the volume is locked or
  6014. // is mounted readonly.
  6015. //
  6016. if (FlagOn( Vcb->VcbState, (VCB_STATE_LOCKED |
  6017. VCB_STATE_MOUNT_READ_ONLY ))) {
  6018. return;
  6019. }
  6020. NtfsInitializeAttributeContext( &Context );
  6021. try {
  6022. //
  6023. // If we are updating the entry for the root then we know the
  6024. // file name attribute to build.
  6025. //
  6026. if (Fcb == Fcb->Vcb->RootIndexScb->Fcb) {
  6027. Lcb = Fcb->Vcb->RootLcb;
  6028. ParentScb = Fcb->Vcb->RootIndexScb;
  6029. QuickIndex = &Fcb->Vcb->RootLcb->QuickIndex;
  6030. FileNameAttr = (PFILE_NAME) &Buffer;
  6031. RtlZeroMemory( FileNameAttr,
  6032. sizeof( FILE_NAME ));
  6033. NtfsBuildFileNameAttribute( IrpContext,
  6034. &Fcb->FileReference,
  6035. NtfsRootIndexString,
  6036. FILE_NAME_DOS | FILE_NAME_NTFS,
  6037. FileNameAttr );
  6038. //
  6039. // If we have and Lcb then it is either present or we noop this update.
  6040. //
  6041. } else if (ARGUMENT_PRESENT( Lcb )) {
  6042. if (!FlagOn( Lcb->LcbState, LCB_STATE_LINK_IS_GONE )) {
  6043. QuickIndex = &Lcb->QuickIndex;
  6044. FileNameAttr = Lcb->FileNameAttr;
  6045. } else {
  6046. leave;
  6047. }
  6048. //
  6049. // If there is no Lcb then lookup the first filename attribute
  6050. // and update its index entry. If there is a parent Scb then we
  6051. // must find a file name attribute for the same parent or we could
  6052. // get into a deadlock situation.
  6053. //
  6054. } else {
  6055. //
  6056. // We now have a name link to update. We will now need
  6057. // an Scb for the parent index. Remember that we may
  6058. // have to teardown the Scb. If we already have a ParentScb
  6059. // then we must find a link to the same parent or to the root.
  6060. // Otherwise we could hit a deadlock.
  6061. //
  6062. Found = NtfsLookupAttributeByCode( IrpContext,
  6063. Fcb,
  6064. &Fcb->FileReference,
  6065. $FILE_NAME,
  6066. &Context );
  6067. if (!Found) {
  6068. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6069. }
  6070. //
  6071. // Loop until we find a suitable link or there are no more on the file.
  6072. //
  6073. do {
  6074. FileNameAttr = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &Context ));
  6075. //
  6076. // If there is a parent and this attribute has the same parent we are
  6077. // done. Our caller will always have acquired the ParentScb.
  6078. //
  6079. if (ARGUMENT_PRESENT( ParentScb )) {
  6080. if (NtfsEqualMftRef( &FileNameAttr->ParentDirectory,
  6081. &ParentScb->Fcb->FileReference )) {
  6082. ASSERT_SHARED_SCB( ParentScb );
  6083. break;
  6084. }
  6085. //
  6086. // If this is the parent of this link is the root then
  6087. // acquire the root directory.
  6088. //
  6089. } else if (NtfsEqualMftRef( &FileNameAttr->ParentDirectory,
  6090. &Vcb->RootIndexScb->Fcb->FileReference )) {
  6091. ParentScb = Vcb->RootIndexScb;
  6092. NtfsAcquireSharedScbForTransaction( IrpContext, ParentScb );
  6093. break;
  6094. //
  6095. // We have a link for this file. If we weren't given a parent
  6096. // Scb then create one here.
  6097. //
  6098. } else if (!ARGUMENT_PRESENT( ParentScb )) {
  6099. NtfsAcquireFcbTable( IrpContext, Vcb );
  6100. AcquiredFcbTable = TRUE;
  6101. ParentFcb = NtfsCreateFcb( IrpContext,
  6102. Vcb,
  6103. FileNameAttr->ParentDirectory,
  6104. FALSE,
  6105. TRUE,
  6106. &ReturnedExistingFcb );
  6107. ParentFcb->ReferenceCount += 1;
  6108. if (!NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK | ACQUIRE_DONT_WAIT )) {
  6109. NtfsReleaseFcbTable( IrpContext, Vcb );
  6110. NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  6111. NtfsAcquireFcbTable( IrpContext, Vcb );
  6112. }
  6113. ParentFcb->ReferenceCount -= 1;
  6114. NtfsReleaseFcbTable( IrpContext, Vcb );
  6115. AcquiredFcbTable = FALSE;
  6116. ParentScb = NtfsCreateScb( IrpContext,
  6117. ParentFcb,
  6118. $INDEX_ALLOCATION,
  6119. &NtfsFileNameIndex,
  6120. FALSE,
  6121. NULL );
  6122. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  6123. break;
  6124. }
  6125. } while (Found = NtfsLookupNextAttributeByCode( IrpContext,
  6126. Fcb,
  6127. $FILE_NAME,
  6128. &Context ));
  6129. //
  6130. // If we didn't find anything then return.
  6131. //
  6132. if (!Found) { leave; }
  6133. }
  6134. //
  6135. // Now update the filename in the parent index.
  6136. //
  6137. NtfsUpdateFileNameInIndex( IrpContext,
  6138. ParentScb,
  6139. FileNameAttr,
  6140. &Fcb->Info,
  6141. QuickIndex );
  6142. //
  6143. // If this filename is either NTFS-ONLY or DOS-ONLY then
  6144. // we need to find the other link.
  6145. //
  6146. if ((FileNameAttr->Flags == FILE_NAME_NTFS) ||
  6147. (FileNameAttr->Flags == FILE_NAME_DOS)) {
  6148. //
  6149. // Find out which flag we should be looking for.
  6150. //
  6151. if (FlagOn( FileNameAttr->Flags, FILE_NAME_NTFS )) {
  6152. FileNameFlags = FILE_NAME_DOS;
  6153. } else {
  6154. FileNameFlags = FILE_NAME_NTFS;
  6155. }
  6156. if (!ARGUMENT_PRESENT( Lcb )) {
  6157. NtfsCleanupAttributeContext( IrpContext, &Context );
  6158. NtfsInitializeAttributeContext( &Context );
  6159. }
  6160. //
  6161. // Now scan for the filename attribute we need.
  6162. //
  6163. Found = NtfsLookupAttributeByCode( IrpContext,
  6164. Fcb,
  6165. &Fcb->FileReference,
  6166. $FILE_NAME,
  6167. &Context );
  6168. while (Found) {
  6169. FileNameAttr = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &Context ));
  6170. if (FileNameAttr->Flags == FileNameFlags) {
  6171. break;
  6172. }
  6173. Found = NtfsLookupNextAttributeByCode( IrpContext,
  6174. Fcb,
  6175. $FILE_NAME,
  6176. &Context );
  6177. }
  6178. //
  6179. // We should have found the entry.
  6180. //
  6181. if (!Found) {
  6182. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6183. }
  6184. NtfsUpdateFileNameInIndex( IrpContext,
  6185. ParentScb,
  6186. FileNameAttr,
  6187. &Fcb->Info,
  6188. NULL );
  6189. }
  6190. } finally {
  6191. DebugUnwind( NtfsUpdateDuplicateInfo );
  6192. if (AcquiredFcbTable) {
  6193. NtfsReleaseFcbTable( IrpContext, Vcb );
  6194. }
  6195. //
  6196. // Cleanup the attribute context for this attribute search.
  6197. //
  6198. NtfsCleanupAttributeContext( IrpContext, &Context );
  6199. //
  6200. // If we created the ParentFcb here then release it and
  6201. // call teardown on it.
  6202. //
  6203. if (!ReturnedExistingFcb && (ParentFcb != NULL)) {
  6204. NtfsTeardownStructures( IrpContext,
  6205. ParentFcb,
  6206. NULL,
  6207. FALSE,
  6208. 0,
  6209. NULL );
  6210. }
  6211. }
  6212. return;
  6213. }
  6214. VOID
  6215. NtfsUpdateLcbDuplicateInfo (
  6216. IN PFCB Fcb,
  6217. IN PLCB Lcb
  6218. )
  6219. /*++
  6220. Routine Description:
  6221. This routine is called after updating duplicate information via an Lcb.
  6222. We want to clear the info flags for this Lcb and any complementary Lcb
  6223. it may be part of. We also want to OR in the Info flags in the Fcb with
  6224. any other Lcb's attached to the Fcb so we will update those in a timely
  6225. fashion as well.
  6226. Arguments:
  6227. Fcb - Fcb for the file.
  6228. Lcb - Lcb used to update duplicate information. It may not be present but
  6229. that would be a rare case and we will perform that test here.
  6230. Return Value:
  6231. None
  6232. --*/
  6233. {
  6234. UCHAR FileNameFlags;
  6235. PLCB NextLcb;
  6236. PLIST_ENTRY Links;
  6237. PAGED_CODE();
  6238. //
  6239. // No work to do unless we were passed an Lcb.
  6240. //
  6241. if (Lcb != NULL) {
  6242. //
  6243. // Check if this is an NTFS only or DOS only link.
  6244. //
  6245. if (Lcb->FileNameAttr->Flags == FILE_NAME_NTFS) {
  6246. FileNameFlags = FILE_NAME_DOS;
  6247. } else if (Lcb->FileNameAttr->Flags == FILE_NAME_DOS) {
  6248. FileNameFlags = FILE_NAME_NTFS;
  6249. } else {
  6250. FileNameFlags = (UCHAR) -1;
  6251. }
  6252. Lcb->InfoFlags = 0;
  6253. Links = Fcb->LcbQueue.Flink;
  6254. do {
  6255. NextLcb = CONTAINING_RECORD( Links,
  6256. LCB,
  6257. FcbLinks );
  6258. if (NextLcb != Lcb) {
  6259. if (NextLcb->FileNameAttr->Flags == FileNameFlags) {
  6260. NextLcb->InfoFlags = 0;
  6261. } else {
  6262. SetFlag( NextLcb->InfoFlags, Fcb->InfoFlags );
  6263. }
  6264. }
  6265. Links = Links->Flink;
  6266. } while (Links != &Fcb->LcbQueue);
  6267. }
  6268. return;
  6269. }
  6270. VOID
  6271. NtfsUpdateFcb (
  6272. IN PFCB Fcb,
  6273. IN ULONG ChangeFlags
  6274. )
  6275. /*++
  6276. Routine Description:
  6277. This routine is called when a timestamp may be updated on an Fcb which
  6278. may have no open handles. We update the time stamps for the flags passed
  6279. in.
  6280. Arguments:
  6281. Fcb - Fcb for the file.
  6282. ChangeFlags - Flags indicating which times to update.
  6283. Return Value:
  6284. None
  6285. --*/
  6286. {
  6287. PAGED_CODE();
  6288. //
  6289. // We need to update the parent directory's time stamps
  6290. // to reflect this change.
  6291. //
  6292. //
  6293. // The change flag should always be set.
  6294. //
  6295. ASSERT( FlagOn( ChangeFlags, FCB_INFO_CHANGED_LAST_CHANGE ));
  6296. KeQuerySystemTime( (PLARGE_INTEGER)&Fcb->Info.LastChangeTime );
  6297. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  6298. //
  6299. // Test for the other flags which may be set.
  6300. //
  6301. if (FlagOn( ChangeFlags, FCB_INFO_CHANGED_LAST_MOD )) {
  6302. Fcb->Info.LastModificationTime = Fcb->Info.LastChangeTime;
  6303. }
  6304. if (FlagOn( ChangeFlags, FCB_INFO_UPDATE_LAST_ACCESS )) {
  6305. Fcb->CurrentLastAccess = Fcb->Info.LastChangeTime;
  6306. }
  6307. SetFlag( Fcb->InfoFlags, ChangeFlags );
  6308. return;
  6309. }
  6310. VOID
  6311. NtfsAddLink (
  6312. IN PIRP_CONTEXT IrpContext,
  6313. IN BOOLEAN CreatePrimaryLink,
  6314. IN PSCB ParentScb,
  6315. IN PFCB Fcb,
  6316. IN PFILE_NAME FileNameAttr,
  6317. IN PBOOLEAN LogIt OPTIONAL,
  6318. OUT PUCHAR FileNameFlags,
  6319. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  6320. IN PNAME_PAIR NamePair OPTIONAL,
  6321. IN PINDEX_CONTEXT IndexContext OPTIONAL
  6322. )
  6323. /*++
  6324. Routine Description:
  6325. This routine adds a link to a file by adding the filename attribute
  6326. for the filename to the file and inserting the name in the parent Scb
  6327. index. If we are creating the primary link for the file and need
  6328. to generate an auxilary name, we will do that here. Use the optional
  6329. NamePair to suggest auxilary names if provided.
  6330. Arguments:
  6331. CreatePrimaryLink - Indicates if we are creating the main Ntfs name
  6332. for the file.
  6333. ParentScb - This is the Scb to add the index entry for this link to.
  6334. Fcb - This is the file to add the hard link to.
  6335. FileNameAttr - File name attribute which is guaranteed only to have the
  6336. name in it.
  6337. LogIt - Indicates whether we should log the creation of this name. If not
  6338. specified then we always log the name creation. On exit we will
  6339. update this to TRUE if we logged the name creation because it
  6340. might cause a split.
  6341. FileNameFlags - We return the file name flags we use to create the link.
  6342. QuickIndex - If specified, supplies a pointer to a quik lookup structure
  6343. to be updated by this routine.
  6344. NamePair - If specified, supplies names that will be checked first as
  6345. possible auxilary names
  6346. IndexContext - Previous result of doing the lookup for the name in the index.
  6347. Return Value:
  6348. None
  6349. --*/
  6350. {
  6351. BOOLEAN LocalLogIt = TRUE;
  6352. PAGED_CODE();
  6353. DebugTrace( +1, Dbg, ("NtfsAddLink: Entered\n") );
  6354. if (!ARGUMENT_PRESENT( LogIt )) {
  6355. LogIt = &LocalLogIt;
  6356. }
  6357. *FileNameFlags = 0;
  6358. //
  6359. // Next add this entry to parent. It is possible that this is a link,
  6360. // an Ntfs name, a DOS name or Ntfs/Dos name. We use the filename
  6361. // attribute structure from earlier, but need to add more information.
  6362. //
  6363. FileNameAttr->ParentDirectory = ParentScb->Fcb->FileReference;
  6364. RtlCopyMemory( &FileNameAttr->Info,
  6365. &Fcb->Info,
  6366. sizeof( DUPLICATED_INFORMATION ));
  6367. FileNameAttr->Flags = 0;
  6368. //
  6369. // We will override the CreatePrimaryLink with the value in the
  6370. // registry.
  6371. //
  6372. NtfsAddNameToParent( IrpContext,
  6373. ParentScb,
  6374. Fcb,
  6375. (BOOLEAN) (FlagOn( NtfsData.Flags,
  6376. NTFS_FLAGS_CREATE_8DOT3_NAMES ) &&
  6377. CreatePrimaryLink),
  6378. LogIt,
  6379. FileNameAttr,
  6380. FileNameFlags,
  6381. QuickIndex,
  6382. NamePair,
  6383. IndexContext );
  6384. //
  6385. // If the name is Ntfs only, we need to generate the DOS name.
  6386. //
  6387. if (*FileNameFlags == FILE_NAME_NTFS) {
  6388. UNICODE_STRING NtfsName;
  6389. NtfsName.Length = (USHORT)(FileNameAttr->FileNameLength * sizeof(WCHAR));
  6390. NtfsName.Buffer = FileNameAttr->FileName;
  6391. NtfsAddDosOnlyName( IrpContext,
  6392. ParentScb,
  6393. Fcb,
  6394. NtfsName,
  6395. *LogIt,
  6396. (NamePair ? &NamePair->Short : NULL) );
  6397. }
  6398. DebugTrace( -1, Dbg, ("NtfsAddLink: Exit\n") );
  6399. return;
  6400. }
  6401. VOID
  6402. NtfsRemoveLink (
  6403. IN PIRP_CONTEXT IrpContext,
  6404. IN PFCB Fcb,
  6405. IN PSCB ParentScb,
  6406. IN UNICODE_STRING LinkName,
  6407. IN OUT PNAME_PAIR NamePair OPTIONAL,
  6408. IN OUT PNTFS_TUNNELED_DATA TunneledData OPTIONAL
  6409. )
  6410. /*++
  6411. Routine Description:
  6412. This routine removes a hard link to a file by removing the filename attribute
  6413. for the filename from the file and removing the name from the parent Scb
  6414. index. It will also remove the other half of a primary link pair.
  6415. A name pair may be used to capture the names.
  6416. Arguments:
  6417. Fcb - This is the file to remove the hard link from
  6418. ParentScb - This is the Scb to remove the index entry for this link from
  6419. LinkName - This is the file name to remove. It will be exact case.
  6420. NamePair - optional name pair for capture
  6421. Return Value:
  6422. None
  6423. --*/
  6424. {
  6425. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6426. ATTRIBUTE_ENUMERATION_CONTEXT OidAttrContext;
  6427. PFILE_NAME FoundFileName;
  6428. UCHAR FileNameFlags;
  6429. UCHAR *ObjectId;
  6430. PAGED_CODE();
  6431. DebugTrace( +1, Dbg, ("NtfsRemoveLink: Entered\n") );
  6432. NtfsInitializeAttributeContext( &AttrContext );
  6433. NtfsInitializeAttributeContext( &OidAttrContext );
  6434. //
  6435. // Use a try-finally to facilitate cleanup.
  6436. //
  6437. try {
  6438. //
  6439. // Now loop through the filenames and find a match.
  6440. // We better find at least one.
  6441. //
  6442. if (!NtfsLookupAttributeByCode( IrpContext,
  6443. Fcb,
  6444. &Fcb->FileReference,
  6445. $FILE_NAME,
  6446. &AttrContext )) {
  6447. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6448. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6449. }
  6450. //
  6451. // Now keep looking until we find a match.
  6452. //
  6453. while (TRUE) {
  6454. FoundFileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  6455. //
  6456. // Do an exact memory comparison.
  6457. //
  6458. if ((*(PLONGLONG)&FoundFileName->ParentDirectory ==
  6459. *(PLONGLONG)&ParentScb->Fcb->FileReference ) &&
  6460. ((FoundFileName->FileNameLength * sizeof( WCHAR )) == (ULONG)LinkName.Length) &&
  6461. (RtlEqualMemory( LinkName.Buffer,
  6462. FoundFileName->FileName,
  6463. LinkName.Length ))) {
  6464. break;
  6465. }
  6466. //
  6467. // Get the next filename attribute.
  6468. //
  6469. if (!NtfsLookupNextAttributeByCode( IrpContext,
  6470. Fcb,
  6471. $FILE_NAME,
  6472. &AttrContext )) {
  6473. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6474. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6475. }
  6476. }
  6477. //
  6478. // Capture the name into caller's area
  6479. //
  6480. if (ARGUMENT_PRESENT(NamePair)) {
  6481. NtfsCopyNameToNamePair( NamePair,
  6482. FoundFileName->FileName,
  6483. FoundFileName->FileNameLength,
  6484. FoundFileName->Flags );
  6485. }
  6486. //
  6487. // It's important to do any object id operations now, before we
  6488. // acquire the Mft. Otherwise we risk a deadlock.
  6489. //
  6490. if (ARGUMENT_PRESENT(TunneledData)) {
  6491. //
  6492. // Find and store the object id, if any, for this file.
  6493. //
  6494. if (NtfsLookupAttributeByCode( IrpContext,
  6495. Fcb,
  6496. &Fcb->FileReference,
  6497. $OBJECT_ID,
  6498. &OidAttrContext )) {
  6499. TunneledData->HasObjectId = TRUE;
  6500. ObjectId = (UCHAR *) NtfsAttributeValue( NtfsFoundAttribute( &OidAttrContext ));
  6501. RtlCopyMemory( TunneledData->ObjectIdBuffer.ObjectId,
  6502. ObjectId,
  6503. sizeof(TunneledData->ObjectIdBuffer.ObjectId) );
  6504. NtfsGetObjectIdExtendedInfo( IrpContext,
  6505. Fcb->Vcb,
  6506. ObjectId,
  6507. TunneledData->ObjectIdBuffer.ExtendedInfo );
  6508. }
  6509. }
  6510. //
  6511. // Now delete the name from the parent Scb.
  6512. //
  6513. NtfsDeleteIndexEntry( IrpContext,
  6514. ParentScb,
  6515. FoundFileName,
  6516. &Fcb->FileReference );
  6517. //
  6518. // Remember the filename flags for this entry.
  6519. //
  6520. FileNameFlags = FoundFileName->Flags;
  6521. //
  6522. // Now delete the entry. Log the operation, discard the file record
  6523. // if empty, and release any and all allocation.
  6524. //
  6525. NtfsDeleteAttributeRecord( IrpContext,
  6526. Fcb,
  6527. (DELETE_LOG_OPERATION |
  6528. DELETE_RELEASE_FILE_RECORD |
  6529. DELETE_RELEASE_ALLOCATION),
  6530. &AttrContext );
  6531. //
  6532. // If the link is a partial link, we need to remove the second
  6533. // half of the link.
  6534. //
  6535. if (FlagOn( FileNameFlags, (FILE_NAME_NTFS | FILE_NAME_DOS) )
  6536. && (FileNameFlags != (FILE_NAME_NTFS | FILE_NAME_DOS))) {
  6537. NtfsRemoveLinkViaFlags( IrpContext,
  6538. Fcb,
  6539. ParentScb,
  6540. (UCHAR)(FlagOn( FileNameFlags, FILE_NAME_NTFS )
  6541. ? FILE_NAME_DOS
  6542. : FILE_NAME_NTFS),
  6543. NamePair,
  6544. NULL );
  6545. }
  6546. } finally {
  6547. DebugUnwind( NtfsRemoveLink );
  6548. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  6549. NtfsCleanupAttributeContext( IrpContext, &OidAttrContext );
  6550. DebugTrace( -1, Dbg, ("NtfsRemoveLink: Exit\n") );
  6551. }
  6552. return;
  6553. }
  6554. VOID
  6555. NtfsRemoveLinkViaFlags (
  6556. IN PIRP_CONTEXT IrpContext,
  6557. IN PFCB Fcb,
  6558. IN PSCB Scb,
  6559. IN UCHAR FileNameFlags,
  6560. IN OUT PNAME_PAIR NamePair OPTIONAL,
  6561. OUT PUNICODE_STRING FileName OPTIONAL
  6562. )
  6563. /*++
  6564. Routine Description:
  6565. This routine is called to remove only a Dos name or only an Ntfs name. We
  6566. already must know that these will be described by separate filename attributes.
  6567. A name pair may be used to capture the name.
  6568. Arguments:
  6569. Fcb - This is the file to remove the hard link from
  6570. ParentScb - This is the Scb to remove the index entry for this link from
  6571. FileNameFlags - This is the single name flag that we must match exactly.
  6572. NamePair - Optional name pair for capture
  6573. FileName - Optional pointer to unicode string. If specified we allocate a buffer and
  6574. return the name deleted.
  6575. Return Value:
  6576. None
  6577. --*/
  6578. {
  6579. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6580. PFILE_NAME FileNameAttr;
  6581. PFILE_NAME FoundFileName;
  6582. PAGED_CODE();
  6583. DebugTrace( +1, Dbg, ("NtfsRemoveLinkViaFlags: Entered\n") );
  6584. NtfsInitializeAttributeContext( &AttrContext );
  6585. FileNameAttr = NULL;
  6586. //
  6587. // Use a try-finally to facilitate cleanup.
  6588. //
  6589. try {
  6590. //
  6591. // Now loop through the filenames and find a match.
  6592. // We better find at least one.
  6593. //
  6594. if (!NtfsLookupAttributeByCode( IrpContext,
  6595. Fcb,
  6596. &Fcb->FileReference,
  6597. $FILE_NAME,
  6598. &AttrContext )) {
  6599. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6600. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6601. }
  6602. //
  6603. // Now keep looking until we find a match.
  6604. //
  6605. while (TRUE) {
  6606. FoundFileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  6607. //
  6608. // Check for an exact flag match.
  6609. //
  6610. if ((*(PLONGLONG)&FoundFileName->ParentDirectory ==
  6611. *(PLONGLONG)&Scb->Fcb->FileReference) &&
  6612. (FoundFileName->Flags == FileNameFlags)) {
  6613. break;
  6614. }
  6615. //
  6616. // Get the next filename attribute.
  6617. //
  6618. if (!NtfsLookupNextAttributeByCode( IrpContext,
  6619. Fcb,
  6620. $FILE_NAME,
  6621. &AttrContext )) {
  6622. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb@ %08lx\n", Fcb) );
  6623. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6624. }
  6625. }
  6626. //
  6627. // Capture the name into caller's area
  6628. //
  6629. if (ARGUMENT_PRESENT(NamePair)) {
  6630. NtfsCopyNameToNamePair( NamePair,
  6631. FoundFileName->FileName,
  6632. FoundFileName->FileNameLength,
  6633. FoundFileName->Flags );
  6634. }
  6635. FileNameAttr = NtfsAllocatePool( PagedPool,
  6636. sizeof( FILE_NAME ) + (FoundFileName->FileNameLength << 1) );
  6637. //
  6638. // We build the file name attribute for the search.
  6639. //
  6640. RtlCopyMemory( FileNameAttr,
  6641. FoundFileName,
  6642. NtfsFileNameSize( FoundFileName ));
  6643. //
  6644. // Now delete the entry.
  6645. //
  6646. NtfsDeleteAttributeRecord( IrpContext,
  6647. Fcb,
  6648. (DELETE_LOG_OPERATION |
  6649. DELETE_RELEASE_FILE_RECORD |
  6650. DELETE_RELEASE_ALLOCATION),
  6651. &AttrContext );
  6652. //
  6653. // Now delete the name from the parent Scb.
  6654. //
  6655. NtfsDeleteIndexEntry( IrpContext,
  6656. Scb,
  6657. FileNameAttr,
  6658. &Fcb->FileReference );
  6659. } finally {
  6660. DebugUnwind( NtfsRemoveLinkViaFlags );
  6661. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  6662. if (FileNameAttr != NULL) {
  6663. //
  6664. // If the user passed in a unicode string then make this look like the name
  6665. // and store the buffer into the input pointer.
  6666. //
  6667. if (ARGUMENT_PRESENT( FileName )) {
  6668. ASSERT( FileName->Buffer == NULL );
  6669. FileName->MaximumLength = FileName->Length = FileNameAttr->FileNameLength * sizeof( WCHAR );
  6670. RtlMoveMemory( FileNameAttr,
  6671. FileNameAttr->FileName,
  6672. FileName->Length );
  6673. FileName->Buffer = (PVOID) FileNameAttr;
  6674. } else {
  6675. NtfsFreePool( FileNameAttr );
  6676. }
  6677. }
  6678. DebugTrace( -1, Dbg, ("NtfsRemoveLinkViaFlags: Exit\n") );
  6679. }
  6680. return;
  6681. }
  6682. VOID
  6683. NtfsUpdateFileNameFlags (
  6684. IN PIRP_CONTEXT IrpContext,
  6685. IN PFCB Fcb,
  6686. IN PSCB ParentScb,
  6687. IN UCHAR FileNameFlags,
  6688. IN PFILE_NAME FileNameLink
  6689. )
  6690. /*++
  6691. Routine Description:
  6692. This routine is called to perform the file name flag update on a name
  6693. link. Nothing else about the name is changing except for the flag
  6694. changes.
  6695. Arguments:
  6696. Fcb - This is the file to change the link flags on.
  6697. ParentScb - This is the Scb which contains the link.
  6698. FileNameFlags - This is the single name flag that we want to change to.
  6699. FileNameLink - Pointer to a copy of the link to change.
  6700. Return Value:
  6701. None
  6702. --*/
  6703. {
  6704. PFILE_NAME FoundFileName;
  6705. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6706. BOOLEAN CleanupContext = FALSE;
  6707. PAGED_CODE();
  6708. //
  6709. // Use a try-finally to facilitate cleanup.
  6710. //
  6711. try {
  6712. //
  6713. // Look up the correct attribute in the file record.
  6714. //
  6715. NtfsInitializeAttributeContext( &AttrContext );
  6716. CleanupContext = TRUE;
  6717. if (!NtfsLookupAttributeByCode( IrpContext,
  6718. Fcb,
  6719. &Fcb->FileReference,
  6720. $FILE_NAME,
  6721. &AttrContext )) {
  6722. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6723. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6724. }
  6725. //
  6726. // Now keep looking till we find the one we want.
  6727. //
  6728. while (TRUE) {
  6729. FoundFileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  6730. //
  6731. // If the names match exactly and the parent directories match then
  6732. // we have a match.
  6733. //
  6734. if (NtfsEqualMftRef( &FileNameLink->ParentDirectory,
  6735. &FoundFileName->ParentDirectory ) &&
  6736. (FileNameLink->FileNameLength == FoundFileName->FileNameLength) &&
  6737. RtlEqualMemory( FileNameLink->FileName,
  6738. FoundFileName->FileName,
  6739. FileNameLink->FileNameLength * sizeof( WCHAR ))) {
  6740. break;
  6741. }
  6742. //
  6743. // Get the next filename attribute.
  6744. //
  6745. if (!NtfsLookupNextAttributeByCode( IrpContext,
  6746. Fcb,
  6747. $FILE_NAME,
  6748. &AttrContext )) {
  6749. //
  6750. // This is bad. We should have found a match.
  6751. //
  6752. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6753. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6754. }
  6755. }
  6756. //
  6757. // Unfortunately we can't log the change to the file name flags only so we will have to remove
  6758. // and reinsert the index entry.
  6759. //
  6760. NtfsDeleteIndexEntry( IrpContext,
  6761. ParentScb,
  6762. FoundFileName,
  6763. &Fcb->FileReference );
  6764. //
  6765. // Update just the flags field.
  6766. //
  6767. NtfsChangeAttributeValue( IrpContext,
  6768. Fcb,
  6769. FIELD_OFFSET( FILE_NAME, Flags ),
  6770. &FileNameFlags,
  6771. sizeof( UCHAR ),
  6772. FALSE,
  6773. TRUE,
  6774. FALSE,
  6775. TRUE,
  6776. &AttrContext );
  6777. //
  6778. // Now reinsert the name in the index.
  6779. //
  6780. NtfsAddIndexEntry( IrpContext,
  6781. ParentScb,
  6782. FoundFileName,
  6783. NtfsFileNameSize( FoundFileName ),
  6784. &Fcb->FileReference,
  6785. NULL,
  6786. NULL );
  6787. } finally {
  6788. DebugUnwind( NtfsUpdateFileNameFlags );
  6789. if (CleanupContext) {
  6790. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  6791. }
  6792. }
  6793. return;
  6794. }
  6795. //
  6796. // This routine is intended only for RESTART.
  6797. //
  6798. VOID
  6799. NtfsRestartInsertAttribute (
  6800. IN PIRP_CONTEXT IrpContext,
  6801. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  6802. IN ULONG RecordOffset,
  6803. IN PATTRIBUTE_RECORD_HEADER Attribute,
  6804. IN PUNICODE_STRING AttributeName OPTIONAL,
  6805. IN PVOID ValueOrMappingPairs OPTIONAL,
  6806. IN ULONG Length
  6807. )
  6808. /*++
  6809. Routine Description:
  6810. This routine performs a simple insert of an attribute record into a
  6811. file record, without worrying about Bcbs or logging.
  6812. Arguments:
  6813. FileRecord - File record into which the attribute is to be inserted.
  6814. RecordOffset - ByteOffset within the file record at which insert is to occur.
  6815. Attribute - The attribute record to be inserted.
  6816. AttributeName - May pass an optional attribute name in the running system
  6817. only.
  6818. ValueOrMappingPairs - May pass a value or mapping pairs pointer in the
  6819. running system only.
  6820. Length - Length of the value or mapping pairs array in bytes - nonzero in
  6821. the running system only. If nonzero and the above pointer is NULL,
  6822. then a value is to be zeroed.
  6823. Return Value:
  6824. None
  6825. --*/
  6826. {
  6827. PVOID From, To;
  6828. ULONG MoveLength;
  6829. ULONG AttributeHeaderSize;
  6830. ASSERT_IRP_CONTEXT( IrpContext );
  6831. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  6832. PAGED_CODE();
  6833. DebugTrace( +1, Dbg, ("NtfsRestartInsertAttribute\n") );
  6834. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  6835. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  6836. DebugTrace( 0, Dbg, ("Attribute = %08lx\n", Attribute) );
  6837. //
  6838. // First make room for the attribute
  6839. //
  6840. From = (PCHAR)FileRecord + RecordOffset;
  6841. To = (PCHAR)From + Attribute->RecordLength;
  6842. MoveLength = FileRecord->FirstFreeByte - RecordOffset;
  6843. RtlMoveMemory( To, From, MoveLength );
  6844. //
  6845. // If there is either an attribute name or Length is nonzero, then
  6846. // we are in the running system, and we are to assemble the attribute
  6847. // in place.
  6848. //
  6849. if ((Length != 0) || ARGUMENT_PRESENT(AttributeName)) {
  6850. //
  6851. // First move the attribute header in.
  6852. //
  6853. if (Attribute->FormCode == RESIDENT_FORM) {
  6854. AttributeHeaderSize = SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  6855. } else if (Attribute->NameOffset != 0) {
  6856. AttributeHeaderSize = Attribute->NameOffset;
  6857. } else {
  6858. AttributeHeaderSize = Attribute->Form.Nonresident.MappingPairsOffset;
  6859. }
  6860. RtlCopyMemory( From,
  6861. Attribute,
  6862. AttributeHeaderSize );
  6863. if (ARGUMENT_PRESENT(AttributeName)) {
  6864. RtlCopyMemory( (PCHAR)From + Attribute->NameOffset,
  6865. AttributeName->Buffer,
  6866. AttributeName->Length );
  6867. }
  6868. //
  6869. // If a value was specified, move it in. Else the caller just wants us
  6870. // to clear for that much.
  6871. //
  6872. if (ARGUMENT_PRESENT(ValueOrMappingPairs)) {
  6873. RtlCopyMemory( (PCHAR)From +
  6874. ((Attribute->FormCode == RESIDENT_FORM) ?
  6875. Attribute->Form.Resident.ValueOffset :
  6876. Attribute->Form.Nonresident.MappingPairsOffset),
  6877. ValueOrMappingPairs,
  6878. Length );
  6879. //
  6880. // Only the resident form will pass a NULL pointer.
  6881. //
  6882. } else {
  6883. RtlZeroMemory( (PCHAR)From + Attribute->Form.Resident.ValueOffset,
  6884. Length );
  6885. }
  6886. //
  6887. // For the restart case, we really only have to insert the attribute.
  6888. // (Note we can also hit this case in the running system when a resident
  6889. // attribute is being created with no name and a null value.)
  6890. //
  6891. } else {
  6892. //
  6893. // Now move the attribute in.
  6894. //
  6895. RtlCopyMemory( From, Attribute, Attribute->RecordLength );
  6896. }
  6897. //
  6898. // Update the file record.
  6899. //
  6900. FileRecord->FirstFreeByte += Attribute->RecordLength;
  6901. //
  6902. // We only need to do this if we would be incrementing the instance
  6903. // number. In the abort or restart case, we don't need to do this.
  6904. //
  6905. if (FileRecord->NextAttributeInstance <= Attribute->Instance) {
  6906. FileRecord->NextAttributeInstance = Attribute->Instance + 1;
  6907. }
  6908. //
  6909. // Remember to increment the reference count if this attribute is indexed.
  6910. //
  6911. if (FlagOn(Attribute->Form.Resident.ResidentFlags, RESIDENT_FORM_INDEXED)) {
  6912. FileRecord->ReferenceCount += 1;
  6913. }
  6914. DebugTrace( -1, Dbg, ("NtfsRestartInsertAttribute -> VOID\n") );
  6915. return;
  6916. }
  6917. //
  6918. // This routine is intended only for RESTART.
  6919. //
  6920. VOID
  6921. NtfsRestartRemoveAttribute (
  6922. IN PIRP_CONTEXT IrpContext,
  6923. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  6924. IN ULONG RecordOffset
  6925. )
  6926. /*++
  6927. Routine Description:
  6928. This routine performs a simple remove of an attribute record from a
  6929. file record, without worrying about Bcbs or logging.
  6930. Arguments:
  6931. FileRecord - File record from which the attribute is to be removed.
  6932. RecordOffset - ByteOffset within the file record at which remove is to occur.
  6933. Return Value:
  6934. None
  6935. --*/
  6936. {
  6937. PATTRIBUTE_RECORD_HEADER Attribute;
  6938. ASSERT_IRP_CONTEXT( IrpContext );
  6939. PAGED_CODE();
  6940. DebugTrace( +1, Dbg, ("NtfsRestartRemoveAttribute\n") );
  6941. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  6942. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  6943. //
  6944. // Calculate the address of the attribute we are removing.
  6945. //
  6946. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord + RecordOffset);
  6947. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  6948. //
  6949. // Reduce first free byte by the amount we removed.
  6950. //
  6951. FileRecord->FirstFreeByte -= Attribute->RecordLength;
  6952. //
  6953. // Remember to decrement the reference count if this attribute is indexed.
  6954. //
  6955. if (FlagOn(Attribute->Form.Resident.ResidentFlags, RESIDENT_FORM_INDEXED)) {
  6956. FileRecord->ReferenceCount -= 1;
  6957. }
  6958. //
  6959. // Remove the attribute by moving the rest of the record down.
  6960. //
  6961. RtlMoveMemory( Attribute,
  6962. (PCHAR)Attribute + Attribute->RecordLength,
  6963. FileRecord->FirstFreeByte - RecordOffset );
  6964. DebugTrace( -1, Dbg, ("NtfsRestartRemoveAttribute -> VOID\n") );
  6965. return;
  6966. }
  6967. //
  6968. // This routine is intended only for RESTART.
  6969. //
  6970. VOID
  6971. NtfsRestartChangeAttributeSize (
  6972. IN PIRP_CONTEXT IrpContext,
  6973. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  6974. IN PATTRIBUTE_RECORD_HEADER Attribute,
  6975. IN ULONG NewRecordLength
  6976. )
  6977. /*++
  6978. Routine Description:
  6979. This routine changes the size of an attribute, and makes the related
  6980. changes in the attribute record.
  6981. Arguments:
  6982. FileRecord - Pointer to the file record in which the attribute resides.
  6983. Attribute - Pointer to the attribute whose size is changing.
  6984. NewRecordLength - New attribute record length.
  6985. Return Value:
  6986. None.
  6987. --*/
  6988. {
  6989. LONG SizeChange = NewRecordLength - Attribute->RecordLength;
  6990. PVOID AttributeEnd = Add2Ptr(Attribute, Attribute->RecordLength);
  6991. UNREFERENCED_PARAMETER( IrpContext );
  6992. PAGED_CODE();
  6993. DebugTrace( +1, Dbg, ("NtfsRestartChangeAttributeSize\n") );
  6994. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  6995. DebugTrace( 0, Dbg, ("Attribute = %08lx\n", Attribute) );
  6996. DebugTrace( 0, Dbg, ("NewRecordLength = %08lx\n", NewRecordLength) );
  6997. //
  6998. // First move the end of the file record after the attribute we are changing.
  6999. //
  7000. RtlMoveMemory( Add2Ptr(Attribute, NewRecordLength),
  7001. AttributeEnd,
  7002. FileRecord->FirstFreeByte - PtrOffset(FileRecord, AttributeEnd) );
  7003. //
  7004. // Now update the file and attribute records.
  7005. //
  7006. FileRecord->FirstFreeByte += SizeChange;
  7007. Attribute->RecordLength = NewRecordLength;
  7008. DebugTrace( -1, Dbg, ("NtfsRestartChangeAttributeSize -> VOID\n") );
  7009. }
  7010. //
  7011. // This routine is intended only for RESTART.
  7012. //
  7013. VOID
  7014. NtfsRestartChangeValue (
  7015. IN PIRP_CONTEXT IrpContext,
  7016. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  7017. IN ULONG RecordOffset,
  7018. IN ULONG AttributeOffset,
  7019. IN PVOID Data,
  7020. IN ULONG Length,
  7021. IN BOOLEAN SetNewLength
  7022. )
  7023. /*++
  7024. Routine Description:
  7025. This routine performs a simple change of an attribute value in a
  7026. file record, without worrying about Bcbs or logging.
  7027. Arguments:
  7028. FileRecord - File record in which the attribute is to be changed.
  7029. RecordOffset - ByteOffset within the file record at which the attribute starts.
  7030. AttributeOffset - Offset within the attribute record at which data is to
  7031. be changed.
  7032. Data - Pointer to the new data.
  7033. Length - Length of the new data.
  7034. SetNewLength - TRUE if the attribute length should be changed.
  7035. Return Value:
  7036. None
  7037. --*/
  7038. {
  7039. PATTRIBUTE_RECORD_HEADER Attribute;
  7040. BOOLEAN AlreadyMoved = FALSE;
  7041. BOOLEAN DataInFileRecord = FALSE;
  7042. ASSERT_IRP_CONTEXT( IrpContext );
  7043. PAGED_CODE();
  7044. DebugTrace( +1, Dbg, ("NtfsRestartChangeValue\n") );
  7045. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  7046. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  7047. DebugTrace( 0, Dbg, ("AttributeOffset = %08lx\n", AttributeOffset) );
  7048. DebugTrace( 0, Dbg, ("Data = %08lx\n", Data) );
  7049. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  7050. DebugTrace( 0, Dbg, ("SetNewLength = %02lx\n", SetNewLength) );
  7051. //
  7052. // Calculate the address of the attribute being changed.
  7053. //
  7054. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord + RecordOffset);
  7055. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  7056. ASSERT( IsQuadAligned( RecordOffset ) );
  7057. //
  7058. // First, if we are setting a new length, then move the data after the
  7059. // attribute record and change FirstFreeByte accordingly.
  7060. //
  7061. if (SetNewLength) {
  7062. ULONG NewLength = QuadAlign( AttributeOffset + Length );
  7063. //
  7064. // If we are shrinking the attribute, we need to move the data
  7065. // first to support caller's who are shifting data down in the
  7066. // attribute value, like DeleteFromAttributeList. If we were
  7067. // to shrink the record first in this case, we would clobber some
  7068. // of the data to be moved down.
  7069. //
  7070. if (NewLength < Attribute->RecordLength) {
  7071. //
  7072. // Now move the new data in and remember we moved it.
  7073. //
  7074. AlreadyMoved = TRUE;
  7075. //
  7076. // If there is data to modify do so now.
  7077. //
  7078. if (Length != 0) {
  7079. if (ARGUMENT_PRESENT(Data)) {
  7080. RtlMoveMemory( (PCHAR)Attribute + AttributeOffset, Data, Length );
  7081. } else {
  7082. RtlZeroMemory( (PCHAR)Attribute + AttributeOffset, Length );
  7083. }
  7084. }
  7085. }
  7086. //
  7087. // First move the tail of the file record to make/eliminate room.
  7088. //
  7089. RtlMoveMemory( Add2Ptr( Attribute, NewLength ),
  7090. Add2Ptr( Attribute, Attribute->RecordLength ),
  7091. FileRecord->FirstFreeByte - RecordOffset - Attribute->RecordLength );
  7092. //
  7093. // Now update fields to reflect the change.
  7094. //
  7095. FileRecord->FirstFreeByte += (NewLength - Attribute->RecordLength);
  7096. Attribute->RecordLength = NewLength;
  7097. Attribute->Form.Resident.ValueLength =
  7098. (USHORT)(AttributeOffset + Length -
  7099. (ULONG)Attribute->Form.Resident.ValueOffset);
  7100. }
  7101. //
  7102. // Now move the new data in.
  7103. //
  7104. if (!AlreadyMoved) {
  7105. if (ARGUMENT_PRESENT(Data)) {
  7106. RtlMoveMemory( Add2Ptr( Attribute, AttributeOffset ),
  7107. Data,
  7108. Length );
  7109. } else {
  7110. RtlZeroMemory( Add2Ptr( Attribute, AttributeOffset ),
  7111. Length );
  7112. }
  7113. }
  7114. DebugTrace( -1, Dbg, ("NtfsRestartChangeValue -> VOID\n") );
  7115. return;
  7116. }
  7117. //
  7118. // This routine is intended only for RESTART.
  7119. //
  7120. VOID
  7121. NtfsRestartChangeMapping (
  7122. IN PIRP_CONTEXT IrpContext,
  7123. IN PVCB Vcb,
  7124. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  7125. IN ULONG RecordOffset,
  7126. IN ULONG AttributeOffset,
  7127. IN PVOID Data,
  7128. IN ULONG Length
  7129. )
  7130. /*++
  7131. Routine Description:
  7132. This routine performs a simple change of an attribute's mapping pairs in a
  7133. file record, without worrying about Bcbs or logging.
  7134. Arguments:
  7135. Vcb - Vcb for volume
  7136. FileRecord - File record in which the attribute is to be changed.
  7137. RecordOffset - ByteOffset within the file record at which the attribute starts.
  7138. AttributeOffset - Offset within the attribute record at which mapping is to
  7139. be changed.
  7140. Data - Pointer to the new mapping.
  7141. Length - Length of the new mapping.
  7142. Return Value:
  7143. None
  7144. --*/
  7145. {
  7146. PATTRIBUTE_RECORD_HEADER Attribute;
  7147. VCN HighestVcn;
  7148. PCHAR MappingPairs;
  7149. ULONG NewLength = QuadAlign( AttributeOffset + Length );
  7150. ASSERT_IRP_CONTEXT( IrpContext );
  7151. UNREFERENCED_PARAMETER( Vcb );
  7152. PAGED_CODE();
  7153. DebugTrace( +1, Dbg, ("NtfsRestartChangeMapping\n") );
  7154. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  7155. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  7156. DebugTrace( 0, Dbg, ("AttributeOffset = %08lx\n", AttributeOffset) );
  7157. DebugTrace( 0, Dbg, ("Data = %08lx\n", Data) );
  7158. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  7159. //
  7160. // Calculate the address of the attribute being changed.
  7161. //
  7162. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord + RecordOffset);
  7163. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  7164. ASSERT( IsQuadAligned( RecordOffset ) );
  7165. //
  7166. // First, if we are setting a new length, then move the data after the
  7167. // attribute record and change FirstFreeByte accordingly.
  7168. //
  7169. //
  7170. // First move the tail of the file record to make/eliminate room.
  7171. //
  7172. RtlMoveMemory( (PCHAR)Attribute + NewLength,
  7173. (PCHAR)Attribute + Attribute->RecordLength,
  7174. FileRecord->FirstFreeByte - RecordOffset -
  7175. Attribute->RecordLength );
  7176. //
  7177. // Now update fields to reflect the change.
  7178. //
  7179. FileRecord->FirstFreeByte += NewLength -
  7180. Attribute->RecordLength;
  7181. Attribute->RecordLength = NewLength;
  7182. //
  7183. // Now move the new data in.
  7184. //
  7185. RtlCopyMemory( (PCHAR)Attribute + AttributeOffset, Data, Length );
  7186. //
  7187. // Finally update HighestVcn and (optionally) AllocatedLength fields.
  7188. //
  7189. MappingPairs = (PCHAR)Attribute + (ULONG)Attribute->Form.Nonresident.MappingPairsOffset;
  7190. HighestVcn = NtfsGetHighestVcn( IrpContext,
  7191. Attribute->Form.Nonresident.LowestVcn,
  7192. MappingPairs );
  7193. ASSERT( IsCharZero( *MappingPairs ) || HighestVcn != -1 );
  7194. Attribute->Form.Nonresident.HighestVcn = HighestVcn;
  7195. DebugTrace( -1, Dbg, ("NtfsRestartChangeMapping -> VOID\n") );
  7196. return;
  7197. }
  7198. VOID
  7199. NtfsAddToAttributeList (
  7200. IN PIRP_CONTEXT IrpContext,
  7201. IN PFCB Fcb,
  7202. IN MFT_SEGMENT_REFERENCE SegmentReference,
  7203. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  7204. )
  7205. /*++
  7206. Routine Description:
  7207. This routine adds an attribute list entry for a newly inserted attribute.
  7208. It is assumed that the context variable is pointing to the attribute
  7209. record in the file record where it has been inserted, and also to the place
  7210. in the attribute list where the new attribute list entry is to be inserted.
  7211. Arguments:
  7212. Fcb - Requested file.
  7213. SegmentReference - Segment reference of the file record the new attribute
  7214. is in.
  7215. Context - Describes the current attribute.
  7216. Return Value:
  7217. None
  7218. --*/
  7219. {
  7220. //
  7221. // Allocate an attribute list entry which hopefully has enough space
  7222. // for the name.
  7223. //
  7224. struct {
  7225. ATTRIBUTE_LIST_ENTRY EntryBuffer;
  7226. WCHAR Name[10];
  7227. } NewEntry;
  7228. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  7229. ULONG EntrySize;
  7230. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  7231. PATTRIBUTE_RECORD_HEADER Attribute;
  7232. PATTRIBUTE_LIST_ENTRY ListEntry = &NewEntry.EntryBuffer;
  7233. BOOLEAN SetNewLength = TRUE;
  7234. ULONG EntryOffset;
  7235. ULONG BeyondEntryOffset;
  7236. PAGED_CODE();
  7237. //
  7238. // First construct the attribute list entry.
  7239. //
  7240. FileRecord = NtfsContainingFileRecord( Context );
  7241. Attribute = NtfsFoundAttribute( Context );
  7242. EntrySize = QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  7243. + ((ULONG) Attribute->NameLength << 1));
  7244. //
  7245. // Allocate the list entry if the one we have is not big enough.
  7246. //
  7247. if (EntrySize > sizeof(NewEntry)) {
  7248. ListEntry = (PATTRIBUTE_LIST_ENTRY)NtfsAllocatePool( NonPagedPool,
  7249. EntrySize );
  7250. }
  7251. RtlZeroMemory( ListEntry, EntrySize );
  7252. NtfsInitializeAttributeContext( &ListContext );
  7253. //
  7254. // Use try-finally to insure cleanup.
  7255. //
  7256. try {
  7257. ULONG OldQuadAttrListSize;
  7258. PATTRIBUTE_RECORD_HEADER ListAttribute;
  7259. PFILE_RECORD_SEGMENT_HEADER ListFileRecord;
  7260. //
  7261. // Now fill in the list entry.
  7262. //
  7263. ListEntry->AttributeTypeCode = Attribute->TypeCode;
  7264. ListEntry->RecordLength = (USHORT)EntrySize;
  7265. ListEntry->AttributeNameLength = Attribute->NameLength;
  7266. ListEntry->Instance = Attribute->Instance;
  7267. ListEntry->AttributeNameOffset =
  7268. (UCHAR)PtrOffset( ListEntry, &ListEntry->AttributeName[0] );
  7269. if (Attribute->FormCode == NONRESIDENT_FORM) {
  7270. ListEntry->LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  7271. }
  7272. ASSERT( (Fcb != Fcb->Vcb->MftScb->Fcb) ||
  7273. (Attribute->TypeCode != $DATA) ||
  7274. ((ULONGLONG)(ListEntry->LowestVcn) > (NtfsFullSegmentNumber( &SegmentReference ) >> Fcb->Vcb->MftToClusterShift)) );
  7275. ListEntry->SegmentReference = SegmentReference;
  7276. if (Attribute->NameLength != 0) {
  7277. RtlCopyMemory( &ListEntry->AttributeName[0],
  7278. Add2Ptr(Attribute, Attribute->NameOffset),
  7279. Attribute->NameLength << 1 );
  7280. }
  7281. //
  7282. // Lookup the list context so that we can modify the attribute list.
  7283. //
  7284. if (!NtfsLookupAttributeByCode( IrpContext,
  7285. Fcb,
  7286. &Fcb->FileReference,
  7287. $ATTRIBUTE_LIST,
  7288. &ListContext )) {
  7289. ASSERT( FALSE );
  7290. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  7291. }
  7292. ListAttribute = NtfsFoundAttribute( &ListContext );
  7293. ListFileRecord = NtfsContainingFileRecord( &ListContext );
  7294. OldQuadAttrListSize = ListAttribute->RecordLength;
  7295. //
  7296. // Remember the relative offsets of list entries.
  7297. //
  7298. EntryOffset = (ULONG) PtrOffset( Context->AttributeList.FirstEntry,
  7299. Context->AttributeList.Entry );
  7300. BeyondEntryOffset = (ULONG) PtrOffset( Context->AttributeList.FirstEntry,
  7301. Context->AttributeList.BeyondFinalEntry );
  7302. //
  7303. // If this operation is possibly going to make the attribute list go
  7304. // non-resident, or else move other attributes around, then we will
  7305. // reserve the space first in the attribute list and then map the
  7306. // value. Note that some of the entries we need to shift up may
  7307. // be modified as a side effect of making space!
  7308. //
  7309. if (NtfsIsAttributeResident( ListAttribute ) &&
  7310. (ListFileRecord->BytesAvailable - ListFileRecord->FirstFreeByte) < EntrySize) {
  7311. ULONG Length;
  7312. //
  7313. // Add enough zeros to the end of the attribute to accommodate
  7314. // the new attribute list entry.
  7315. //
  7316. NtfsChangeAttributeValue( IrpContext,
  7317. Fcb,
  7318. BeyondEntryOffset,
  7319. NULL,
  7320. EntrySize,
  7321. TRUE,
  7322. TRUE,
  7323. FALSE,
  7324. TRUE,
  7325. &ListContext );
  7326. //
  7327. // We now don't have to set the new length.
  7328. //
  7329. SetNewLength = FALSE;
  7330. //
  7331. // In case the attribute list went non-resident on this call, then we
  7332. // need to update both list entry pointers in the found attribute.
  7333. // (We do this "just in case" all the time to avoid a rare code path.)
  7334. //
  7335. //
  7336. // Map the non-resident attribute list.
  7337. //
  7338. NtfsMapAttributeValue( IrpContext,
  7339. Fcb,
  7340. (PVOID *) &Context->AttributeList.FirstEntry,
  7341. &Length,
  7342. &Context->AttributeList.NonresidentListBcb,
  7343. &ListContext );
  7344. //
  7345. // If the list is still resident then unpin the current Bcb in
  7346. // the original context to keep our pin counts in sync.
  7347. //
  7348. if (Context->AttributeList.Bcb == Context->AttributeList.NonresidentListBcb) {
  7349. NtfsUnpinBcb( IrpContext, &Context->AttributeList.NonresidentListBcb );
  7350. }
  7351. Context->AttributeList.Entry = Add2Ptr( Context->AttributeList.FirstEntry,
  7352. EntryOffset );
  7353. Context->AttributeList.BeyondFinalEntry = Add2Ptr( Context->AttributeList.FirstEntry,
  7354. BeyondEntryOffset );
  7355. }
  7356. //
  7357. // Check for adding duplicate entries...
  7358. //
  7359. ASSERT(
  7360. // Not enough room for previous entry to = inserted entry
  7361. ((EntryOffset < EntrySize) ||
  7362. // Previous entry doesn't equal inserted entry
  7363. (!RtlEqualMemory((PVOID)((PCHAR)Context->AttributeList.Entry - EntrySize),
  7364. ListEntry,
  7365. EntrySize)))
  7366. &&
  7367. // At end of attribute list
  7368. ((BeyondEntryOffset == EntryOffset) ||
  7369. // This entry doesn't equal inserted entry
  7370. (!RtlEqualMemory(Context->AttributeList.Entry,
  7371. ListEntry,
  7372. EntrySize))) );
  7373. //
  7374. // Now shift the old contents up to make room for our new entry. We don't let
  7375. // the attribute list grow larger than a cache view however.
  7376. //
  7377. if (EntrySize + BeyondEntryOffset > VACB_MAPPING_GRANULARITY) {
  7378. NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
  7379. }
  7380. NtfsChangeAttributeValue( IrpContext,
  7381. Fcb,
  7382. EntryOffset + EntrySize,
  7383. Context->AttributeList.Entry,
  7384. BeyondEntryOffset - EntryOffset,
  7385. SetNewLength,
  7386. TRUE,
  7387. FALSE,
  7388. TRUE,
  7389. &ListContext );
  7390. //
  7391. // Now write in the new entry.
  7392. //
  7393. NtfsChangeAttributeValue( IrpContext,
  7394. Fcb,
  7395. EntryOffset,
  7396. (PVOID)ListEntry,
  7397. EntrySize,
  7398. FALSE,
  7399. TRUE,
  7400. FALSE,
  7401. FALSE,
  7402. &ListContext );
  7403. //
  7404. // Reload the attribute list values from the list context.
  7405. //
  7406. ListAttribute = NtfsFoundAttribute( &ListContext );
  7407. //
  7408. // Now fix up the context for return
  7409. //
  7410. if (*(PLONGLONG)&FileRecord->BaseFileRecordSegment == 0) {
  7411. //
  7412. // We need to update the attribute pointer for the target attribute
  7413. // by the amount of the change in the attribute list attribute.
  7414. //
  7415. Context->FoundAttribute.Attribute =
  7416. Add2Ptr( Context->FoundAttribute.Attribute,
  7417. ListAttribute->RecordLength - OldQuadAttrListSize );
  7418. }
  7419. Context->AttributeList.BeyondFinalEntry =
  7420. Add2Ptr( Context->AttributeList.BeyondFinalEntry, EntrySize );
  7421. #if DBG
  7422. {
  7423. PATTRIBUTE_LIST_ENTRY LastEntry, Entry;
  7424. for (LastEntry = Context->AttributeList.FirstEntry, Entry = NtfsGetNextRecord(LastEntry);
  7425. Entry < Context->AttributeList.BeyondFinalEntry;
  7426. LastEntry = Entry, Entry = NtfsGetNextRecord(LastEntry)) {
  7427. ASSERT( (LastEntry->RecordLength != Entry->RecordLength) ||
  7428. (!RtlEqualMemory(LastEntry, Entry, Entry->RecordLength)) );
  7429. }
  7430. }
  7431. #endif
  7432. } finally {
  7433. //
  7434. // If we had to allocate a list entry buffer, deallocate it.
  7435. //
  7436. if (ListEntry != &NewEntry.EntryBuffer) {
  7437. NtfsFreePool(ListEntry);
  7438. }
  7439. //
  7440. // Cleanup the enumeration context for the list entry.
  7441. //
  7442. NtfsCleanupAttributeContext( IrpContext, &ListContext);
  7443. }
  7444. }
  7445. VOID
  7446. NtfsDeleteFromAttributeList (
  7447. IN PIRP_CONTEXT IrpContext,
  7448. IN PFCB Fcb,
  7449. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  7450. )
  7451. /*++
  7452. Routine Description:
  7453. This routine deletes an attribute list entry for a recently deleted attribute.
  7454. It is assumed that the context variable is pointing to the place in
  7455. the attribute list where the attribute list entry is to be deleted.
  7456. Arguments:
  7457. Fcb - Requested file.
  7458. Context - Describes the current attribute.
  7459. Return Value:
  7460. None
  7461. --*/
  7462. {
  7463. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  7464. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  7465. PATTRIBUTE_LIST_ENTRY ListEntry, NextListEntry;
  7466. ULONG EntrySize;
  7467. ULONG SavedListSize;
  7468. PAGED_CODE();
  7469. FileRecord = NtfsContainingFileRecord( Context );
  7470. //
  7471. // Lookup the list context so that we can modify the attribute list.
  7472. //
  7473. NtfsInitializeAttributeContext( &ListContext );
  7474. if (!NtfsLookupAttributeByCode( IrpContext,
  7475. Fcb,
  7476. &Fcb->FileReference,
  7477. $ATTRIBUTE_LIST,
  7478. &ListContext )) {
  7479. ASSERT( FALSE );
  7480. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  7481. }
  7482. //
  7483. // Use try-finally to insure cleanup.
  7484. //
  7485. try {
  7486. SavedListSize = NtfsFoundAttribute(&ListContext)->RecordLength;
  7487. //
  7488. // Now shift the old contents down to make room for our new entry.
  7489. //
  7490. ListEntry = Context->AttributeList.Entry;
  7491. EntrySize = ListEntry->RecordLength;
  7492. NextListEntry = Add2Ptr(ListEntry, EntrySize);
  7493. NtfsChangeAttributeValue( IrpContext,
  7494. Fcb,
  7495. PtrOffset( Context->AttributeList.FirstEntry,
  7496. Context->AttributeList.Entry ),
  7497. NextListEntry,
  7498. PtrOffset( NextListEntry,
  7499. Context->AttributeList.BeyondFinalEntry ),
  7500. TRUE,
  7501. TRUE,
  7502. FALSE,
  7503. TRUE,
  7504. &ListContext );
  7505. //
  7506. // Now fix up the context for return
  7507. //
  7508. if (*(PLONGLONG)&FileRecord->BaseFileRecordSegment == 0) {
  7509. SavedListSize -= NtfsFoundAttribute(&ListContext)->RecordLength;
  7510. Context->FoundAttribute.Attribute =
  7511. Add2Ptr( Context->FoundAttribute.Attribute, -(LONG)SavedListSize );
  7512. }
  7513. Context->AttributeList.BeyondFinalEntry =
  7514. Add2Ptr( Context->AttributeList.BeyondFinalEntry, -(LONG)EntrySize );
  7515. } finally {
  7516. //
  7517. // Cleanup the enumeration context for the list entry.
  7518. //
  7519. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  7520. }
  7521. }
  7522. BOOLEAN
  7523. NtfsRewriteMftMapping (
  7524. IN PIRP_CONTEXT IrpContext,
  7525. IN PVCB Vcb
  7526. )
  7527. /*++
  7528. Routine Description:
  7529. This routine is called to rewrite the mapping for the Mft file. This is done
  7530. in the case where either hot-fixing or Mft defragging has caused us to spill
  7531. into the reserved area of a file record. This routine will rewrite the
  7532. mapping from the beginning, using the reserved record if necessary. On return
  7533. it will indicate whether any work was done and if there is more work to do.
  7534. Arguments:
  7535. Vcb - This is the Vcb for the volume to defrag.
  7536. ExcessMapping - Address to store whether there is still excess mapping in
  7537. the file.
  7538. Return Value:
  7539. BOOLEAN - TRUE if we made any changes to the file. FALSE if we found no
  7540. work to do.
  7541. --*/
  7542. {
  7543. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  7544. PUCHAR MappingPairs = NULL;
  7545. PBCB FileRecordBcb = NULL;
  7546. BOOLEAN MadeChanges = FALSE;
  7547. BOOLEAN ExcessMapping = FALSE;
  7548. BOOLEAN LastFileRecord = FALSE;
  7549. BOOLEAN SkipLookup = FALSE;
  7550. PAGED_CODE();
  7551. NtfsInitializeAttributeContext( &AttrContext );
  7552. //
  7553. // Use a try-finally to facilitate cleanup.
  7554. //
  7555. try {
  7556. VCN CurrentVcn; // Starting Vcn for the next file record
  7557. VCN MinimumVcn; // This Vcn must be in the current mapping
  7558. VCN LastVcn; // Last Vcn in the current mapping
  7559. VCN LastMftVcn; // Last Vcn in the file
  7560. VCN NextVcn; // First Vcn past the end of the mapping
  7561. ULONG ReservedIndex; // Reserved index in Mft
  7562. ULONG NextIndex; // Next file record available for Mft mapping
  7563. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  7564. MFT_SEGMENT_REFERENCE FileRecordReference;
  7565. ULONG RecordOffset;
  7566. PATTRIBUTE_RECORD_HEADER Attribute;
  7567. ULONG AttributeOffset;
  7568. ULONG MappingSizeAvailable;
  7569. ULONG MappingPairsSize;
  7570. //
  7571. // Find the initial file record for the Mft.
  7572. //
  7573. NtfsLookupAttributeForScb( IrpContext, Vcb->MftScb, NULL, &AttrContext );
  7574. //
  7575. // Compute some initial values. If this is the only file record
  7576. // for the file then we are done.
  7577. //
  7578. ReservedIndex = Vcb->MftScb->ScbType.Mft.ReservedIndex;
  7579. Attribute = NtfsFoundAttribute( &AttrContext );
  7580. LastMftVcn = Int64ShraMod32(Vcb->MftScb->Header.AllocationSize.QuadPart, Vcb->ClusterShift) - 1;
  7581. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  7582. if (CurrentVcn >= LastMftVcn) {
  7583. try_return( NOTHING );
  7584. }
  7585. //
  7586. // Loop while there are more file records. We will insert any
  7587. // additional file records needed within the loop so that this
  7588. // call should succeed until the remapping is done.
  7589. //
  7590. while (SkipLookup ||
  7591. NtfsLookupNextAttributeForScb( IrpContext,
  7592. Vcb->MftScb,
  7593. &AttrContext )) {
  7594. BOOLEAN ReplaceFileRecord;
  7595. BOOLEAN ReplaceAttributeListEntry;
  7596. ReplaceAttributeListEntry = FALSE;
  7597. //
  7598. // If we just looked up this entry then pin the current
  7599. // attribute.
  7600. //
  7601. if (!SkipLookup) {
  7602. //
  7603. // Always pin the current attribute.
  7604. //
  7605. NtfsPinMappedAttribute( IrpContext,
  7606. Vcb,
  7607. &AttrContext );
  7608. }
  7609. //
  7610. // Extract some pointers from the current file record.
  7611. // Remember if this was the last record.
  7612. //
  7613. ReplaceFileRecord = FALSE;
  7614. FileRecord = NtfsContainingFileRecord( &AttrContext );
  7615. FileRecordReference = AttrContext.AttributeList.Entry->SegmentReference;
  7616. Attribute = NtfsFoundAttribute( &AttrContext );
  7617. AttributeOffset = Attribute->Form.Nonresident.MappingPairsOffset;
  7618. RecordOffset = PtrOffset( FileRecord, Attribute );
  7619. //
  7620. // Remember if we are at the last attribute.
  7621. //
  7622. if (Attribute->Form.Nonresident.HighestVcn == LastMftVcn) {
  7623. LastFileRecord = TRUE;
  7624. }
  7625. //
  7626. // If we have already remapped this entire file record then
  7627. // remove the attribute and it list entry.
  7628. //
  7629. if (!SkipLookup &&
  7630. (CurrentVcn > LastMftVcn)) {
  7631. PATTRIBUTE_LIST_ENTRY ListEntry;
  7632. ULONG Count;
  7633. Count = 0;
  7634. //
  7635. // We want to remove this entry and all subsequent entries.
  7636. //
  7637. ListEntry = AttrContext.AttributeList.Entry;
  7638. while ((ListEntry != AttrContext.AttributeList.BeyondFinalEntry) &&
  7639. (ListEntry->AttributeTypeCode == $DATA) &&
  7640. (ListEntry->AttributeNameLength == 0)) {
  7641. Count += 1;
  7642. NtfsDeallocateMftRecord( IrpContext,
  7643. Vcb,
  7644. NtfsUnsafeSegmentNumber( &ListEntry->SegmentReference ) );
  7645. NtfsDeleteFromAttributeList( IrpContext,
  7646. Vcb->MftScb->Fcb,
  7647. &AttrContext );
  7648. ListEntry = AttrContext.AttributeList.Entry;
  7649. }
  7650. //
  7651. // Clear out the reserved index in case one of these
  7652. // will do.
  7653. //
  7654. NtfsAcquireCheckpoint( IrpContext, Vcb );
  7655. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MFT_REC_RESERVED );
  7656. ClearFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED );
  7657. NtfsReleaseCheckpoint( IrpContext, Vcb );
  7658. Vcb->MftScb->ScbType.Mft.ReservedIndex = 0;
  7659. try_return( NOTHING );
  7660. }
  7661. //
  7662. // Check if we are going to replace this file record with
  7663. // the reserved record.
  7664. //
  7665. if (ReservedIndex < NtfsSegmentNumber( &FileRecordReference )) {
  7666. PATTRIBUTE_RECORD_HEADER NewAttribute;
  7667. PATTRIBUTE_TYPE_CODE NewEnd;
  7668. //
  7669. // Remember this index for our computation for the Minimum mapped
  7670. // Vcn.
  7671. //
  7672. NextIndex = NtfsUnsafeSegmentNumber( &FileRecordReference );
  7673. FileRecord = NtfsCloneFileRecord( IrpContext,
  7674. Vcb->MftScb->Fcb,
  7675. TRUE,
  7676. &FileRecordBcb,
  7677. &FileRecordReference );
  7678. ReservedIndex = MAXULONG;
  7679. //
  7680. // Now lets create an attribute in the new file record.
  7681. //
  7682. NewAttribute = Add2Ptr( FileRecord,
  7683. FileRecord->FirstFreeByte
  7684. - QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE )));
  7685. NewAttribute->TypeCode = Attribute->TypeCode;
  7686. NewAttribute->RecordLength = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  7687. NewAttribute->FormCode = NONRESIDENT_FORM;
  7688. NewAttribute->Flags = Attribute->Flags;
  7689. NewAttribute->Instance = FileRecord->NextAttributeInstance++;
  7690. NewAttribute->Form.Nonresident.LowestVcn = CurrentVcn;
  7691. NewAttribute->Form.Nonresident.HighestVcn = 0;
  7692. NewAttribute->Form.Nonresident.MappingPairsOffset = (USHORT) NewAttribute->RecordLength;
  7693. NewEnd = Add2Ptr( NewAttribute, NewAttribute->RecordLength );
  7694. *NewEnd = $END;
  7695. //
  7696. // Now fix up the file record with this new data.
  7697. //
  7698. FileRecord->FirstFreeByte = PtrOffset( FileRecord, NewEnd )
  7699. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  7700. FileRecord->SequenceNumber += 1;
  7701. if (FileRecord->SequenceNumber == 0) {
  7702. FileRecord->SequenceNumber = 1;
  7703. }
  7704. FileRecordReference.SequenceNumber = FileRecord->SequenceNumber;
  7705. //
  7706. // Now switch this new file record into the attribute context.
  7707. //
  7708. NtfsUnpinBcb( IrpContext, &NtfsFoundBcb( &AttrContext ));
  7709. NtfsFoundBcb( &AttrContext ) = FileRecordBcb;
  7710. AttrContext.FoundAttribute.MftFileOffset = LlBytesFromFileRecords( Vcb, NextIndex );
  7711. AttrContext.FoundAttribute.Attribute = NewAttribute;
  7712. AttrContext.FoundAttribute.FileRecord = FileRecord;
  7713. FileRecordBcb = NULL;
  7714. //
  7715. // Now add an attribute list entry for this entry.
  7716. //
  7717. NtfsAddToAttributeList( IrpContext,
  7718. Vcb->MftScb->Fcb,
  7719. FileRecordReference,
  7720. &AttrContext );
  7721. //
  7722. // Reload our pointers for this file record.
  7723. //
  7724. Attribute = NewAttribute;
  7725. AttributeOffset = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  7726. RecordOffset = PtrOffset( FileRecord, Attribute );
  7727. //
  7728. // We must include either the last Vcn of the file or
  7729. // the Vcn for the next file record to use for the Mft.
  7730. // At this point MinimumVcn is the first Vcn that doesn't
  7731. // have to be in the current mapping.
  7732. //
  7733. if (Vcb->FileRecordsPerCluster == 0) {
  7734. MinimumVcn = (NextIndex + 1) << Vcb->MftToClusterShift;
  7735. } else {
  7736. MinimumVcn = (NextIndex + Vcb->FileRecordsPerCluster - 1) << Vcb->MftToClusterShift;
  7737. }
  7738. ReplaceFileRecord = TRUE;
  7739. //
  7740. // We will be using the current attribute.
  7741. //
  7742. } else {
  7743. //
  7744. // The mapping we write into this page must go
  7745. // to the current end of the page or to the reserved
  7746. // or spare file record, whichever is earlier.
  7747. // If we are adding the reserved record to the end then
  7748. // we know the final Vcn already.
  7749. //
  7750. if (SkipLookup) {
  7751. NextVcn = LastMftVcn;
  7752. } else {
  7753. NextVcn = Attribute->Form.Nonresident.HighestVcn;
  7754. }
  7755. if (Vcb->FileRecordsPerCluster == 0) {
  7756. NextIndex = (ULONG)Int64ShraMod32((NextVcn + 1), Vcb->MftToClusterShift);
  7757. } else {
  7758. NextIndex = (ULONG)Int64ShllMod32((NextVcn + 1), Vcb->MftToClusterShift);
  7759. }
  7760. if (ReservedIndex < NextIndex) {
  7761. NextIndex = ReservedIndex + 1;
  7762. ReplaceFileRecord = TRUE;
  7763. }
  7764. //
  7765. // If we can use this file record unchanged then continue on.
  7766. // Start by checking that it starts on the same Vcn boundary.
  7767. //
  7768. if (!SkipLookup) {
  7769. //
  7770. // If it starts on the same boundary then we check if we
  7771. // can do any work with this.
  7772. //
  7773. if (CurrentVcn == Attribute->Form.Nonresident.LowestVcn) {
  7774. ULONG RemainingFileRecordBytes;
  7775. RemainingFileRecordBytes = FileRecord->BytesAvailable - FileRecord->FirstFreeByte;
  7776. //
  7777. // Check if we have less than the desired cushion
  7778. // left.
  7779. //
  7780. if (RemainingFileRecordBytes < Vcb->MftCushion) {
  7781. //
  7782. // If we have no more file records there is no
  7783. // remapping we can do.
  7784. //
  7785. if (!ReplaceFileRecord) {
  7786. //
  7787. // Remember if we used part of the reserved
  7788. // portion of the file record.
  7789. //
  7790. if (RemainingFileRecordBytes < Vcb->MftReserved) {
  7791. ExcessMapping = TRUE;
  7792. }
  7793. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  7794. continue;
  7795. }
  7796. //
  7797. // We have more than our cushion left. If this
  7798. // is the last file record we will skip this.
  7799. //
  7800. } else if (Attribute->Form.Nonresident.HighestVcn == LastMftVcn) {
  7801. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  7802. continue;
  7803. }
  7804. //
  7805. // If it doesn't start on the same boundary then we have to
  7806. // delete and reinsert the attribute list entry.
  7807. //
  7808. } else {
  7809. ReplaceAttributeListEntry = TRUE;
  7810. }
  7811. }
  7812. ReplaceFileRecord = FALSE;
  7813. //
  7814. // Log the beginning state of this file record.
  7815. //
  7816. NtfsLogMftFileRecord( IrpContext,
  7817. Vcb,
  7818. FileRecord,
  7819. LlBytesFromFileRecords( Vcb, NtfsSegmentNumber( &FileRecordReference ) ),
  7820. NtfsFoundBcb( &AttrContext ),
  7821. FALSE );
  7822. //
  7823. // Compute the Vcn for the file record past the one we will use
  7824. // next. At this point this is the first Vcn that doesn't have
  7825. // to be in the current mapping.
  7826. //
  7827. if (Vcb->FileRecordsPerCluster == 0) {
  7828. MinimumVcn = NextIndex << Vcb->MftToClusterShift;
  7829. } else {
  7830. MinimumVcn = (NextIndex + Vcb->FileRecordsPerCluster - 1) << Vcb->MftToClusterShift;
  7831. }
  7832. }
  7833. //
  7834. // Move back one vcn to adhere to the mapping pairs interface.
  7835. // This is now the last Vcn which MUST appear in the current
  7836. // mapping.
  7837. //
  7838. MinimumVcn = MinimumVcn - 1;
  7839. //
  7840. // Get the available size for the mapping pairs. We won't
  7841. // include the cushion here.
  7842. //
  7843. MappingSizeAvailable = FileRecord->BytesAvailable + Attribute->RecordLength - FileRecord->FirstFreeByte - SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  7844. //
  7845. // We know the range of Vcn's the mapping must cover.
  7846. // Compute the mapping pair size. If they won't fit and
  7847. // leave our desired cushion then use whatever space is
  7848. // needed. The NextVcn value is the first Vcn (or xxMax)
  7849. // for the run after the last run in the current mapping.
  7850. //
  7851. MappingPairsSize = NtfsGetSizeForMappingPairs( &Vcb->MftScb->Mcb,
  7852. MappingSizeAvailable - Vcb->MftCushion,
  7853. CurrentVcn,
  7854. NULL,
  7855. &NextVcn );
  7856. //
  7857. // If this mapping doesn't include the file record we will
  7858. // be using next then extend the mapping to include it.
  7859. //
  7860. if (NextVcn <= MinimumVcn) {
  7861. //
  7862. // Compute the mapping pairs again. This must fit
  7863. // since it already fits.
  7864. //
  7865. MappingPairsSize = NtfsGetSizeForMappingPairs( &Vcb->MftScb->Mcb,
  7866. MappingSizeAvailable,
  7867. CurrentVcn,
  7868. &MinimumVcn,
  7869. &NextVcn );
  7870. //
  7871. // Remember if we still have excess mapping.
  7872. //
  7873. if (MappingSizeAvailable - MappingPairsSize < Vcb->MftReserved) {
  7874. ExcessMapping = TRUE;
  7875. }
  7876. }
  7877. //
  7878. // Remember the last Vcn for the current run. If the NextVcn
  7879. // is xxMax then we are at the end of the file.
  7880. //
  7881. if (NextVcn == MAXLONGLONG) {
  7882. LastVcn = LastMftVcn;
  7883. //
  7884. // Otherwise it is one less than the next vcn value.
  7885. //
  7886. } else {
  7887. LastVcn = NextVcn - 1;
  7888. }
  7889. //
  7890. // Check if we have to rewrite this attribute. We will write the
  7891. // new mapping if any of the following are true.
  7892. //
  7893. // We are replacing a file record
  7894. // The attribute's LowestVcn doesn't match
  7895. // The attributes's HighestVcn doesn't match.
  7896. //
  7897. if (ReplaceFileRecord ||
  7898. (CurrentVcn != Attribute->Form.Nonresident.LowestVcn) ||
  7899. (LastVcn != Attribute->Form.Nonresident.HighestVcn )) {
  7900. Attribute->Form.Nonresident.LowestVcn = CurrentVcn;
  7901. //
  7902. // Replace the attribute list entry at this point if needed.
  7903. //
  7904. if (ReplaceAttributeListEntry) {
  7905. NtfsDeleteFromAttributeList( IrpContext,
  7906. Vcb->MftScb->Fcb,
  7907. &AttrContext );
  7908. NtfsAddToAttributeList( IrpContext,
  7909. Vcb->MftScb->Fcb,
  7910. FileRecordReference,
  7911. &AttrContext );
  7912. }
  7913. //
  7914. // Allocate a buffer for the mapping pairs if we haven't
  7915. // done so.
  7916. //
  7917. if (MappingPairs == NULL) {
  7918. MappingPairs = NtfsAllocatePool(PagedPool, NtfsMaximumAttributeSize( Vcb->BytesPerFileRecordSegment ));
  7919. }
  7920. NtfsBuildMappingPairs( &Vcb->MftScb->Mcb,
  7921. CurrentVcn,
  7922. &NextVcn,
  7923. MappingPairs );
  7924. Attribute->Form.Nonresident.HighestVcn = NextVcn;
  7925. NtfsRestartChangeMapping( IrpContext,
  7926. Vcb,
  7927. FileRecord,
  7928. RecordOffset,
  7929. AttributeOffset,
  7930. MappingPairs,
  7931. MappingPairsSize );
  7932. //
  7933. // Log the changes to this page.
  7934. //
  7935. NtfsLogMftFileRecord( IrpContext,
  7936. Vcb,
  7937. FileRecord,
  7938. LlBytesFromFileRecords( Vcb, NtfsSegmentNumber( &FileRecordReference ) ),
  7939. NtfsFoundBcb( &AttrContext ),
  7940. TRUE );
  7941. MadeChanges = TRUE;
  7942. }
  7943. //
  7944. // Move to the first Vcn of the following record.
  7945. //
  7946. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  7947. //
  7948. // If we reached the last file record and have more mapping to do
  7949. // then use the reserved record. It must be available or we would
  7950. // have written out the entire mapping.
  7951. //
  7952. if (LastFileRecord && (CurrentVcn < LastMftVcn)) {
  7953. PATTRIBUTE_RECORD_HEADER NewAttribute;
  7954. PATTRIBUTE_TYPE_CODE NewEnd;
  7955. //
  7956. // Start by moving to the next file record. It better not be
  7957. // there or the file is corrupt. This will position us to
  7958. // insert the new record.
  7959. //
  7960. if (NtfsLookupNextAttributeForScb( IrpContext,
  7961. Vcb->MftScb,
  7962. &AttrContext )) {
  7963. NtfsAcquireCheckpoint( IrpContext, Vcb );
  7964. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
  7965. NtfsReleaseCheckpoint( IrpContext, Vcb );
  7966. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Vcb->MftScb->Fcb );
  7967. }
  7968. FileRecord = NtfsCloneFileRecord( IrpContext,
  7969. Vcb->MftScb->Fcb,
  7970. TRUE,
  7971. &FileRecordBcb,
  7972. &FileRecordReference );
  7973. ReservedIndex = MAXULONG;
  7974. //
  7975. // Now lets create an attribute in the new file record.
  7976. //
  7977. NewAttribute = Add2Ptr( FileRecord,
  7978. FileRecord->FirstFreeByte
  7979. - QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE )));
  7980. NewAttribute->TypeCode = Attribute->TypeCode;
  7981. NewAttribute->RecordLength = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  7982. NewAttribute->FormCode = NONRESIDENT_FORM;
  7983. NewAttribute->Flags = Attribute->Flags;
  7984. NewAttribute->Instance = FileRecord->NextAttributeInstance++;
  7985. NewAttribute->Form.Nonresident.LowestVcn = CurrentVcn;
  7986. NewAttribute->Form.Nonresident.HighestVcn = 0;
  7987. NewAttribute->Form.Nonresident.MappingPairsOffset = (USHORT) NewAttribute->RecordLength;
  7988. NewEnd = Add2Ptr( NewAttribute, NewAttribute->RecordLength );
  7989. *NewEnd = $END;
  7990. //
  7991. // Now fix up the file record with this new data.
  7992. //
  7993. FileRecord->FirstFreeByte = PtrOffset( FileRecord, NewEnd )
  7994. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  7995. FileRecord->SequenceNumber += 1;
  7996. if (FileRecord->SequenceNumber == 0) {
  7997. FileRecord->SequenceNumber = 1;
  7998. }
  7999. FileRecordReference.SequenceNumber = FileRecord->SequenceNumber;
  8000. //
  8001. // Now switch this new file record into the attribute context.
  8002. //
  8003. NtfsUnpinBcb( IrpContext, &NtfsFoundBcb( &AttrContext ));
  8004. NtfsFoundBcb( &AttrContext ) = FileRecordBcb;
  8005. AttrContext.FoundAttribute.MftFileOffset =
  8006. LlBytesFromFileRecords( Vcb, NtfsSegmentNumber( &FileRecordReference ) );
  8007. AttrContext.FoundAttribute.Attribute = NewAttribute;
  8008. AttrContext.FoundAttribute.FileRecord = FileRecord;
  8009. FileRecordBcb = NULL;
  8010. //
  8011. // Now add an attribute list entry for this entry.
  8012. //
  8013. NtfsAddToAttributeList( IrpContext,
  8014. Vcb->MftScb->Fcb,
  8015. FileRecordReference,
  8016. &AttrContext );
  8017. SkipLookup = TRUE;
  8018. LastFileRecord = FALSE;
  8019. } else {
  8020. SkipLookup = FALSE;
  8021. }
  8022. } // End while more file records
  8023. //
  8024. // If we didn't rewrite all of the mapping then there is some error.
  8025. //
  8026. if (CurrentVcn <= LastMftVcn) {
  8027. NtfsAcquireCheckpoint( IrpContext, Vcb );
  8028. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
  8029. NtfsReleaseCheckpoint( IrpContext, Vcb );
  8030. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Vcb->MftScb->Fcb );
  8031. }
  8032. try_exit: NOTHING;
  8033. //
  8034. // Clear the excess mapping flag if no changes were made.
  8035. //
  8036. if (!ExcessMapping) {
  8037. NtfsAcquireCheckpoint( IrpContext, Vcb );
  8038. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_EXCESS_MAP );
  8039. NtfsReleaseCheckpoint( IrpContext, Vcb );
  8040. }
  8041. } finally {
  8042. DebugUnwind( NtfsRewriteMftMapping );
  8043. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8044. NtfsUnpinBcb( IrpContext, &FileRecordBcb );
  8045. if (MappingPairs != NULL) {
  8046. NtfsFreePool( MappingPairs );
  8047. }
  8048. }
  8049. return MadeChanges;
  8050. }
  8051. VOID
  8052. NtfsSetTotalAllocatedField (
  8053. IN PIRP_CONTEXT IrpContext,
  8054. IN PSCB Scb,
  8055. IN USHORT TotalAllocatedNeeded
  8056. )
  8057. /*++
  8058. Routine Description:
  8059. This routine is called to insure that first attribute of a stream has
  8060. the correct size attribute header based on the compression state of the
  8061. file. Compressed streams will have a field for the total allocated space
  8062. in the file in the nonresident header.
  8063. This routine will see if the header is in a valid state and make space
  8064. if necessary. Then it will rewrite any of the attribute data after
  8065. the header.
  8066. Arguments:
  8067. Scb - Scb for affected stream
  8068. TotalAllocatedPresent - 0 if the TotalAllocated field not needed (this would
  8069. be an uncompressed, non-sparse file), nonzero if the TotalAllocated field
  8070. is needed.
  8071. Return Value:
  8072. None.
  8073. --*/
  8074. {
  8075. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  8076. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  8077. PATTRIBUTE_RECORD_HEADER Attribute;
  8078. PATTRIBUTE_RECORD_HEADER NewAttribute = NULL;
  8079. PUNICODE_STRING NewAttributeName = NULL;
  8080. ULONG OldHeaderSize;
  8081. ULONG NewHeaderSize;
  8082. LONG SizeChange;
  8083. PAGED_CODE();
  8084. //
  8085. // This must be a non-resident user data file.
  8086. //
  8087. if (!NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) ||
  8088. FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  8089. return;
  8090. }
  8091. NtfsInitializeAttributeContext( &AttrContext );
  8092. //
  8093. // Use a try-finally to facilitate cleanup.
  8094. //
  8095. try {
  8096. while (TRUE) {
  8097. //
  8098. // Find the current and the new size for the attribute.
  8099. //
  8100. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  8101. FileRecord = NtfsContainingFileRecord( &AttrContext );
  8102. Attribute = NtfsFoundAttribute( &AttrContext );
  8103. OldHeaderSize = Attribute->Form.Nonresident.MappingPairsOffset;
  8104. if (Attribute->NameOffset != 0) {
  8105. OldHeaderSize = Attribute->NameOffset;
  8106. }
  8107. if (TotalAllocatedNeeded) {
  8108. NewHeaderSize = SIZEOF_FULL_NONRES_ATTR_HEADER;
  8109. } else {
  8110. NewHeaderSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  8111. }
  8112. SizeChange = NewHeaderSize - OldHeaderSize;
  8113. //
  8114. // Make space if we need to do so. Lookup the attribute again
  8115. // if necessary.
  8116. //
  8117. if (SizeChange > 0) {
  8118. VCN StartingVcn;
  8119. VCN ClusterCount;
  8120. //
  8121. // If the attribute is alone in the file record and there isn't
  8122. // enough space available then the call to ChangeAttributeSize
  8123. // can't make any space available. In that case we call
  8124. // NtfsChangeAttributeAllocation and let that routine rewrite
  8125. // the mapping to make space available.
  8126. //
  8127. if ((FileRecord->BytesAvailable - FileRecord->FirstFreeByte < (ULONG) SizeChange) &&
  8128. (NtfsFirstAttribute( FileRecord ) == Attribute) &&
  8129. (((PATTRIBUTE_RECORD_HEADER) NtfsGetNextRecord( Attribute ))->TypeCode == $END)) {
  8130. NtfsLookupAllocation( IrpContext,
  8131. Scb,
  8132. Attribute->Form.Nonresident.HighestVcn,
  8133. &StartingVcn,
  8134. &ClusterCount,
  8135. NULL,
  8136. NULL );
  8137. StartingVcn = 0;
  8138. ClusterCount = Attribute->Form.Nonresident.HighestVcn + 1;
  8139. NtfsAddAttributeAllocation( IrpContext,
  8140. Scb,
  8141. &AttrContext,
  8142. &StartingVcn,
  8143. &ClusterCount );
  8144. } else if (NtfsChangeAttributeSize( IrpContext,
  8145. Scb->Fcb,
  8146. Attribute->RecordLength + SizeChange,
  8147. &AttrContext)) {
  8148. break;
  8149. }
  8150. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8151. NtfsInitializeAttributeContext( &AttrContext );
  8152. continue;
  8153. }
  8154. break;
  8155. }
  8156. NtfsPinMappedAttribute( IrpContext, Scb->Vcb, &AttrContext );
  8157. //
  8158. // Make a copy of the existing attribute and modify the total allocated field
  8159. // if necessary.
  8160. //
  8161. NewAttribute = NtfsAllocatePool( PagedPool, Attribute->RecordLength + SizeChange );
  8162. RtlCopyMemory( NewAttribute,
  8163. Attribute,
  8164. SIZEOF_PARTIAL_NONRES_ATTR_HEADER );
  8165. if (Attribute->NameOffset != 0) {
  8166. NewAttribute->NameOffset += (USHORT) SizeChange;
  8167. NewAttributeName = &Scb->AttributeName;
  8168. RtlCopyMemory( Add2Ptr( NewAttribute, NewAttribute->NameOffset ),
  8169. NewAttributeName->Buffer,
  8170. NewAttributeName->Length );
  8171. }
  8172. NewAttribute->Form.Nonresident.MappingPairsOffset += (USHORT) SizeChange;
  8173. NewAttribute->RecordLength += SizeChange;
  8174. RtlCopyMemory( Add2Ptr( NewAttribute, NewAttribute->Form.Nonresident.MappingPairsOffset ),
  8175. Add2Ptr( Attribute, Attribute->Form.Nonresident.MappingPairsOffset ),
  8176. Attribute->RecordLength - Attribute->Form.Nonresident.MappingPairsOffset );
  8177. if (TotalAllocatedNeeded) {
  8178. NewAttribute->Form.Nonresident.TotalAllocated = Scb->TotalAllocated;
  8179. }
  8180. //
  8181. // We now have the before and after image to log.
  8182. //
  8183. FileRecord->Lsn =
  8184. NtfsWriteLog( IrpContext,
  8185. Scb->Vcb->MftScb,
  8186. NtfsFoundBcb( &AttrContext ),
  8187. DeleteAttribute,
  8188. NULL,
  8189. 0,
  8190. CreateAttribute,
  8191. Attribute,
  8192. Attribute->RecordLength,
  8193. NtfsMftOffset( &AttrContext ),
  8194. PtrOffset( FileRecord, Attribute ),
  8195. 0,
  8196. Scb->Vcb->BytesPerFileRecordSegment );
  8197. NtfsRestartRemoveAttribute( IrpContext, FileRecord, PtrOffset( FileRecord, Attribute ));
  8198. FileRecord->Lsn =
  8199. NtfsWriteLog( IrpContext,
  8200. Scb->Vcb->MftScb,
  8201. NtfsFoundBcb( &AttrContext ),
  8202. CreateAttribute,
  8203. NewAttribute,
  8204. NewAttribute->RecordLength,
  8205. DeleteAttribute,
  8206. NULL,
  8207. 0,
  8208. NtfsMftOffset( &AttrContext ),
  8209. PtrOffset( FileRecord, Attribute ),
  8210. 0,
  8211. Scb->Vcb->BytesPerFileRecordSegment );
  8212. NtfsRestartInsertAttribute( IrpContext,
  8213. FileRecord,
  8214. PtrOffset( FileRecord, Attribute ),
  8215. NewAttribute,
  8216. NewAttributeName,
  8217. Add2Ptr( NewAttribute, NewAttribute->Form.Nonresident.MappingPairsOffset ),
  8218. NewAttribute->RecordLength - NewAttribute->Form.Nonresident.MappingPairsOffset );
  8219. } finally {
  8220. DebugUnwind( NtfsSetTotalAllocatedField );
  8221. if (NewAttribute != NULL) {
  8222. NtfsFreePool( NewAttribute );
  8223. }
  8224. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8225. }
  8226. return;
  8227. }
  8228. VOID
  8229. NtfsSetSparseStream (
  8230. IN PIRP_CONTEXT IrpContext,
  8231. IN PSCB ParentScb OPTIONAL,
  8232. IN PSCB Scb
  8233. )
  8234. /*++
  8235. Routine Description:
  8236. This routine is called change the state of a stream to sparse. It may be
  8237. called on behalf of a user or internally to Ntfs (i.e. for the USN
  8238. journal). Our caller may already have begun a transaction but in any
  8239. case will have acquired the main resource and paging resource for the
  8240. stream exclusively.
  8241. This routine will add the TotalAllocated field to the non-resident attribute
  8242. header and fully allocate (or deallocate) the final compression unit of
  8243. the stream. It will set the SPARSE flag in the attribute header as well as
  8244. in standard information and the directory entry for this stream.
  8245. NOTE - This routine will checkpoint the current transaction in order
  8246. to safely change the compression unit size and shift value in the Scb.
  8247. We also will update the Fcb duplicate information which is not protected
  8248. under transaction control.
  8249. Arguments:
  8250. ParentScb - Scb for the parent. If present we will update the directory
  8251. entry for the parent. Otherwise we simply set the FcbInfo flags and
  8252. let the update happen when the handle is closed.
  8253. Scb - Scb for the stream. Caller should have acquired this already.
  8254. Return Value:
  8255. None.
  8256. --*/
  8257. {
  8258. PFCB Fcb = Scb->Fcb;
  8259. PVCB Vcb = Scb->Vcb;
  8260. PLCB Lcb;
  8261. ULONG OriginalFileAttributes;
  8262. USHORT OriginalStreamAttributes;
  8263. UCHAR OriginalCompressionUnitShift;
  8264. ULONG OriginalCompressionUnit;
  8265. LONGLONG OriginalFileAllocatedLength;
  8266. UCHAR NewCompressionUnitShift;
  8267. ULONG NewCompressionUnit;
  8268. LONGLONG StartVcn;
  8269. LONGLONG FinalVcn;
  8270. ULONG AttributeSizeChange;
  8271. PATTRIBUTE_RECORD_HEADER Attribute;
  8272. ATTRIBUTE_RECORD_HEADER NewAttribute;
  8273. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  8274. ASSERT( (Scb->Header.PagingIoResource == NULL) ||
  8275. ExIsResourceAcquiredExclusiveLite( Scb->Header.PagingIoResource ));
  8276. ASSERT_EXCLUSIVE_SCB( Scb );
  8277. ASSERT( NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
  8278. PAGED_CODE();
  8279. //
  8280. // Return immediately if the stream is already sparse.
  8281. //
  8282. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  8283. return;
  8284. }
  8285. //
  8286. // Remember the current compression unit and flags.
  8287. //
  8288. OriginalFileAttributes = Fcb->Info.FileAttributes;
  8289. OriginalStreamAttributes = Scb->AttributeFlags;
  8290. OriginalCompressionUnitShift = Scb->CompressionUnitShift;
  8291. OriginalCompressionUnit = Scb->CompressionUnit;
  8292. OriginalFileAllocatedLength = Fcb->Info.AllocatedLength;
  8293. //
  8294. // Use a try-finally to facilitate cleanup.
  8295. //
  8296. NtfsInitializeAttributeContext( &AttrContext );
  8297. try {
  8298. //
  8299. // Post the change to the Usn Journal
  8300. //
  8301. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_BASIC_INFO_CHANGE );
  8302. //
  8303. // Acquire the parent now for the update duplicate call.
  8304. //
  8305. if (ARGUMENT_PRESENT( ParentScb )) {
  8306. NtfsPrepareForUpdateDuplicate( IrpContext, Fcb, &Lcb, &ParentScb, TRUE );
  8307. }
  8308. //
  8309. // If the file is not already compressed then we need to add a total allocated
  8310. // field and adjust the allocation length.
  8311. //
  8312. NewCompressionUnitShift = Scb->CompressionUnitShift;
  8313. NewCompressionUnit = Scb->CompressionUnit;
  8314. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  8315. //
  8316. // Compute the new compression unit and shift.
  8317. //
  8318. NewCompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
  8319. NewCompressionUnit = BytesFromClusters( Vcb,
  8320. 1 << NTFS_CLUSTERS_PER_COMPRESSION );
  8321. //
  8322. // If the compression unit is larger than 64K then find the correct
  8323. // compression unit to reach exactly 64k.
  8324. //
  8325. while (NewCompressionUnit > Vcb->SparseFileUnit) {
  8326. NewCompressionUnitShift -= 1;
  8327. NewCompressionUnit /= 2;
  8328. }
  8329. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  8330. //
  8331. // Fully allocate the final compression unit.
  8332. //
  8333. if (Scb->Header.AllocationSize.LowPart & (NewCompressionUnit - 1)) {
  8334. StartVcn = LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart );
  8335. FinalVcn = Scb->Header.AllocationSize.QuadPart + NewCompressionUnit - 1;
  8336. ((PLARGE_INTEGER) &FinalVcn)->LowPart &= ~(NewCompressionUnit - 1);
  8337. FinalVcn = LlClustersFromBytesTruncate( Vcb, FinalVcn );
  8338. NtfsAddAllocation( IrpContext,
  8339. NULL,
  8340. Scb,
  8341. StartVcn,
  8342. FinalVcn - StartVcn,
  8343. FALSE,
  8344. NULL );
  8345. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  8346. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  8347. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  8348. }
  8349. }
  8350. //
  8351. // Add a total allocated field to the attribute record header.
  8352. //
  8353. NtfsSetTotalAllocatedField( IrpContext, Scb, ATTRIBUTE_FLAG_SPARSE );
  8354. }
  8355. }
  8356. //
  8357. // Look up the existing attribute.
  8358. //
  8359. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  8360. NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
  8361. Attribute = NtfsFoundAttribute( &AttrContext );
  8362. //
  8363. // Now we need to set the bits in the attribute flag field.
  8364. //
  8365. if (NtfsIsAttributeResident( Attribute )) {
  8366. RtlCopyMemory( &NewAttribute, Attribute, SIZEOF_RESIDENT_ATTRIBUTE_HEADER );
  8367. AttributeSizeChange = SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  8368. //
  8369. // Else if it is nonresident, copy it here, set the compression parameter,
  8370. // and remember its size.
  8371. //
  8372. } else {
  8373. AttributeSizeChange = Attribute->Form.Nonresident.MappingPairsOffset;
  8374. if (Attribute->NameOffset != 0) {
  8375. AttributeSizeChange = Attribute->NameOffset;
  8376. }
  8377. ASSERT( AttributeSizeChange <= sizeof( NewAttribute ));
  8378. RtlCopyMemory( &NewAttribute, Attribute, AttributeSizeChange );
  8379. NewAttribute.Form.Nonresident.CompressionUnit = NewCompressionUnitShift;
  8380. }
  8381. SetFlag( NewAttribute.Flags, ATTRIBUTE_FLAG_SPARSE );
  8382. //
  8383. // Now, log the changed attribute.
  8384. //
  8385. (VOID)NtfsWriteLog( IrpContext,
  8386. Vcb->MftScb,
  8387. NtfsFoundBcb( &AttrContext ),
  8388. UpdateResidentValue,
  8389. &NewAttribute,
  8390. AttributeSizeChange,
  8391. UpdateResidentValue,
  8392. Attribute,
  8393. AttributeSizeChange,
  8394. NtfsMftOffset( &AttrContext ),
  8395. PtrOffset(NtfsContainingFileRecord( &AttrContext ), Attribute),
  8396. 0,
  8397. Vcb->BytesPerFileRecordSegment );
  8398. //
  8399. // Change the attribute by calling the same routine called at restart.
  8400. //
  8401. NtfsRestartChangeValue( IrpContext,
  8402. NtfsContainingFileRecord( &AttrContext ),
  8403. PtrOffset( NtfsContainingFileRecord( &AttrContext ), Attribute ),
  8404. 0,
  8405. &NewAttribute,
  8406. AttributeSizeChange,
  8407. FALSE );
  8408. //
  8409. // If the file is not already marked sparse then update the standard information
  8410. // and the parent directory (if specified). Also report the change via
  8411. // dirnotify if there is a Ccb with a name.
  8412. //
  8413. ASSERTMSG( "conflict with flush",
  8414. ExIsResourceAcquiredSharedLite( Fcb->Resource ) ||
  8415. (Fcb->PagingIoResource != NULL &&
  8416. ExIsResourceAcquiredSharedLite( Fcb->PagingIoResource )));
  8417. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  8418. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  8419. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  8420. //
  8421. // Update the attributes in standard information.
  8422. //
  8423. NtfsUpdateStandardInformation( IrpContext, Fcb );
  8424. if (ARGUMENT_PRESENT( ParentScb )) {
  8425. //
  8426. // Update the directory entry for this file.
  8427. //
  8428. NtfsUpdateDuplicateInfo( IrpContext, Fcb, NULL, NULL );
  8429. NtfsUpdateLcbDuplicateInfo( Fcb, Lcb );
  8430. Fcb->InfoFlags = 0;
  8431. }
  8432. //
  8433. // Update the compression values and the sparse flag in the Scb.
  8434. //
  8435. Scb->CompressionUnit = NewCompressionUnit;
  8436. Scb->CompressionUnitShift = NewCompressionUnitShift;
  8437. SetFlag( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE );
  8438. //
  8439. // Set the FastIo state.
  8440. //
  8441. NtfsAcquireFsrtlHeader( Scb );
  8442. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  8443. SetFlag( Scb->Header.Flags2, FSRTL_FLAG2_PURGE_WHEN_MAPPED );
  8444. NtfsReleaseFsrtlHeader( Scb );
  8445. //
  8446. // Commit this change.
  8447. //
  8448. NtfsCheckpointCurrentTransaction( IrpContext );
  8449. } finally {
  8450. DebugUnwind( NtfsSetSparseStream );
  8451. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8452. //
  8453. // Backout the changes to the non-logged structures on abort.
  8454. //
  8455. if (AbnormalTermination()) {
  8456. Fcb->Info.FileAttributes = OriginalFileAttributes;
  8457. Scb->AttributeFlags = OriginalStreamAttributes;
  8458. if (!FlagOn( OriginalStreamAttributes, ATTRIBUTE_FLAG_SPARSE )) {
  8459. NtfsAcquireFsrtlHeader( Scb );
  8460. ClearFlag( Scb->Header.Flags2, FSRTL_FLAG2_PURGE_WHEN_MAPPED );
  8461. NtfsReleaseFsrtlHeader( Scb );
  8462. }
  8463. Scb->CompressionUnitShift = OriginalCompressionUnitShift;
  8464. Scb->CompressionUnit = OriginalCompressionUnit;
  8465. Fcb->Info.AllocatedLength = OriginalFileAllocatedLength;
  8466. }
  8467. }
  8468. return;
  8469. }
  8470. NTSTATUS
  8471. NtfsZeroRangeInStream (
  8472. IN PIRP_CONTEXT IrpContext,
  8473. IN PFILE_OBJECT FileObject OPTIONAL,
  8474. IN PSCB Scb,
  8475. IN PLONGLONG StartingOffset,
  8476. IN LONGLONG FinalZero
  8477. )
  8478. /*++
  8479. Routine Description:
  8480. This routine is the worker routine which will zero a range of a stream and
  8481. (if sparse) deallocate any space in the stream that is convenient. We only
  8482. perform this operation on $DATA streams where there are no user maps. We
  8483. will zero, flush and purge any partial pages. We will zero full pages except
  8484. for sparse streams where we will purge the data and deallocate the
  8485. disk backing for this range.
  8486. This routine will fail if the stream has a user map. Note that if the user
  8487. is zeroing the end of the stream we can choose to simply move valid data length
  8488. and purge the existing data instead of performing extensive flush operations.
  8489. Arguments:
  8490. FileObject - A file object for the stream. We can use this to follow the
  8491. caller's preference for write through.
  8492. Scb - This is the Scb for the stream we are to zero. The user may have acquired
  8493. the paging io resource prior to this call but the main resource should
  8494. not be acquired.
  8495. StartingOffset - Offset in the file to start the zero operation. This may
  8496. lie outside of the file size. We update this to reflect the current
  8497. position through the file. That way if this routine should raise log
  8498. file full our caller can resume from the point where we left off.
  8499. FinalZero - Offset of last byte in the file to zero.
  8500. Return Value:
  8501. NTSTATUS - Result of this operation.
  8502. --*/
  8503. {
  8504. NTSTATUS Status = STATUS_SUCCESS;
  8505. BOOLEAN ReleaseScb = FALSE;
  8506. BOOLEAN UnlockHeader = FALSE;
  8507. LONGLONG LastOffset = -1;
  8508. LONGLONG CurrentBytes;
  8509. LONGLONG CurrentOffset;
  8510. LONGLONG CurrentFinalByte;
  8511. LONGLONG ClusterCount;
  8512. ULONG ClustersPerCompressionUnit;
  8513. BOOLEAN ThrottleWrites;
  8514. VCN NextVcn;
  8515. VCN CurrentVcn;
  8516. LCN Lcn;
  8517. PBCB ZeroBufferBcb = NULL;
  8518. PVOID ZeroBuffer;
  8519. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  8520. BOOLEAN CleanupAttrContext = FALSE;
  8521. PAGED_CODE();
  8522. //
  8523. // We better not be holding the main resource without also holding
  8524. // the paging resource, if any.
  8525. //
  8526. ASSERT( !NtfsIsSharedScb( Scb ) ||
  8527. (Scb->Header.PagingIoResource == NULL) ||
  8528. NtfsIsExclusiveScbPagingIo( Scb ) );
  8529. //
  8530. // We will loop through the requested zero range. We will checkpoint
  8531. // periodically and drop all resources so we don't become a bottle neck
  8532. // in the system.
  8533. //
  8534. try {
  8535. while (TRUE) {
  8536. //
  8537. // Acquire either the paging Io resource if present and lock the header
  8538. // or simply acquire the main resource.
  8539. //
  8540. if (Scb->Header.PagingIoResource != NULL) {
  8541. if (IrpContext->CleanupStructure != NULL) {
  8542. ASSERT( (PFCB)IrpContext->CleanupStructure == Scb->Fcb );
  8543. } else {
  8544. ExAcquireResourceExclusiveLite( Scb->Header.PagingIoResource, TRUE );
  8545. FsRtlLockFsRtlHeader( &Scb->Header );
  8546. IrpContext->CleanupStructure = Scb;
  8547. UnlockHeader = TRUE;
  8548. }
  8549. } else {
  8550. NtfsAcquireExclusiveScb( IrpContext, Scb );
  8551. ReleaseScb = TRUE;
  8552. }
  8553. //
  8554. // Verify that the file and volume are still present.
  8555. //
  8556. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED | SCB_STATE_VOLUME_DISMOUNTED)) {
  8557. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  8558. Status = STATUS_FILE_DELETED;
  8559. } else {
  8560. Status = STATUS_VOLUME_DISMOUNTED;
  8561. }
  8562. leave;
  8563. }
  8564. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  8565. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  8566. }
  8567. //
  8568. // If we are past the end of the file or the length is zero we can break out.
  8569. //
  8570. if ((*StartingOffset >= Scb->Header.FileSize.QuadPart) ||
  8571. (*StartingOffset >= FinalZero)) {
  8572. try_return( NOTHING );
  8573. }
  8574. ThrottleWrites = FALSE;
  8575. //
  8576. // Check for the oplock and file state.
  8577. //
  8578. if (ARGUMENT_PRESENT( FileObject )) {
  8579. CurrentBytes = FinalZero - *StartingOffset;
  8580. if (FinalZero > Scb->Header.FileSize.QuadPart) {
  8581. CurrentBytes = Scb->Header.FileSize.QuadPart - *StartingOffset;
  8582. }
  8583. if (CurrentBytes > NTFS_MAX_ZERO_RANGE) {
  8584. CurrentBytes = NTFS_MAX_ZERO_RANGE;
  8585. }
  8586. Status = NtfsCheckLocksInZeroRange( IrpContext,
  8587. IrpContext->OriginatingIrp,
  8588. Scb,
  8589. FileObject,
  8590. StartingOffset,
  8591. (ULONG) CurrentBytes );
  8592. if (Status != STATUS_SUCCESS) {
  8593. leave;
  8594. }
  8595. }
  8596. //
  8597. // Post the change to the Usn Journal
  8598. //
  8599. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_DATA_OVERWRITE );
  8600. //
  8601. // We are going to make the changes. Make sure we set the file object
  8602. // flag to indicate we are making changes.
  8603. //
  8604. if (ARGUMENT_PRESENT( FileObject )) {
  8605. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  8606. }
  8607. //
  8608. // If the file is resident then flush and purge the stream and
  8609. // then change the attribute itself.
  8610. //
  8611. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  8612. //
  8613. // Trim the remaining bytes to file size.
  8614. //
  8615. CurrentBytes = FinalZero - *StartingOffset;
  8616. if (FinalZero > Scb->Header.FileSize.QuadPart) {
  8617. CurrentBytes = Scb->Header.FileSize.QuadPart - *StartingOffset;
  8618. }
  8619. Status = NtfsFlushUserStream( IrpContext, Scb, NULL, 0 );
  8620. NtfsNormalizeAndCleanupTransaction( IrpContext,
  8621. &Status,
  8622. TRUE,
  8623. STATUS_UNEXPECTED_IO_ERROR );
  8624. //
  8625. // Proceed if there is nothing to purge or the purge succeeds.
  8626. //
  8627. if ((Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
  8628. !CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  8629. NULL,
  8630. 0,
  8631. FALSE )) {
  8632. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  8633. leave;
  8634. }
  8635. //
  8636. // Acquire the main resource to change the attribute.
  8637. //
  8638. if (!ReleaseScb) {
  8639. NtfsAcquireExclusiveScb( IrpContext, Scb );
  8640. ReleaseScb = TRUE;
  8641. }
  8642. //
  8643. // Now look up the attribute and zero the requested range.
  8644. //
  8645. NtfsInitializeAttributeContext( &AttrContext );
  8646. CleanupAttrContext = TRUE;
  8647. NtfsLookupAttributeForScb( IrpContext,
  8648. Scb,
  8649. NULL,
  8650. &AttrContext );
  8651. NtfsChangeAttributeValue( IrpContext,
  8652. Scb->Fcb,
  8653. (ULONG) *StartingOffset,
  8654. NULL,
  8655. (ULONG) CurrentBytes,
  8656. FALSE,
  8657. TRUE,
  8658. FALSE,
  8659. FALSE,
  8660. &AttrContext );
  8661. NtfsCheckpointCurrentTransaction( IrpContext );
  8662. *StartingOffset += CurrentBytes;
  8663. try_return( NOTHING );
  8664. }
  8665. //
  8666. // Make sure there are no mapped sections in the range we are trying to
  8667. // zero.
  8668. //
  8669. if (!MmCanFileBeTruncated( &Scb->NonpagedScb->SegmentObject,
  8670. (PLARGE_INTEGER) StartingOffset )) {
  8671. Status = STATUS_USER_MAPPED_FILE;
  8672. try_return( NOTHING );
  8673. }
  8674. //
  8675. // If the file is either sparse or compressed then we look for ranges
  8676. // we need to flush, purge or deallocate.
  8677. //
  8678. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  8679. ClustersPerCompressionUnit = 1 << Scb->CompressionUnitShift;
  8680. //
  8681. // Move our starting point back to a compression unit boundary. If our
  8682. // ending point is past the end of the file then set it to the compression
  8683. // unit past the EOF.
  8684. //
  8685. CurrentOffset = *StartingOffset & ~((LONGLONG) (Scb->CompressionUnit - 1));
  8686. CurrentFinalByte = FinalZero;
  8687. if (CurrentFinalByte > Scb->Header.FileSize.QuadPart) {
  8688. CurrentFinalByte = Scb->Header.FileSize.QuadPart + Scb->CompressionUnit - 1;
  8689. ((PLARGE_INTEGER) &CurrentFinalByte)->LowPart &= ~(Scb->CompressionUnit - 1);
  8690. }
  8691. //
  8692. // Then look forward for either an allocated range or a reserved compression
  8693. // unit. We may have to flush and/or purge data at that offset.
  8694. //
  8695. NextVcn =
  8696. CurrentVcn = LlClustersFromBytesTruncate( Scb->Vcb, CurrentOffset );
  8697. while (!NtfsLookupAllocation( IrpContext,
  8698. Scb,
  8699. NextVcn,
  8700. &Lcn,
  8701. &ClusterCount,
  8702. NULL,
  8703. NULL )) {
  8704. //
  8705. // Move the current Vcn forward by the size of the hole.
  8706. // Break out if we are beyond the final byte.
  8707. //
  8708. NextVcn += ClusterCount;
  8709. if ((LONGLONG) LlBytesFromClusters( Scb->Vcb, NextVcn ) >= CurrentFinalByte) {
  8710. //
  8711. // Trim the final Vcn to the beginning of the last compression unit.
  8712. //
  8713. NextVcn = LlClustersFromBytesTruncate( Scb->Vcb, CurrentFinalByte );
  8714. break;
  8715. }
  8716. }
  8717. //
  8718. // Back up to a compression unit.
  8719. //
  8720. ((PLARGE_INTEGER) &NextVcn)->LowPart &= ~(ClustersPerCompressionUnit - 1);
  8721. //
  8722. // If we found a hole then we need to look for reserved clusters within
  8723. // the range.
  8724. //
  8725. if (NextVcn != CurrentVcn) {
  8726. ClusterCount = NextVcn - CurrentVcn;
  8727. if (Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) {
  8728. NtfsCheckForReservedClusters( Scb, CurrentVcn, &ClusterCount );
  8729. }
  8730. CurrentVcn += ClusterCount;
  8731. }
  8732. //
  8733. // CurrentVcn - points to the first range we might have to zero in memory.
  8734. // NextVcn - points to the first range we might choose to deallocate.
  8735. //
  8736. // Proceed if we aren't beyond the final byte to zero.
  8737. //
  8738. CurrentOffset = LlBytesFromClusters( Scb->Vcb, CurrentVcn );
  8739. if (CurrentOffset >= CurrentFinalByte) {
  8740. ASSERT( IrpContext->TransactionId == 0 );
  8741. *StartingOffset = CurrentFinalByte;
  8742. try_return( NOTHING );
  8743. }
  8744. //
  8745. // If we find a range which is less than our starting offset then we will
  8746. // have to zero this range in the data section.
  8747. //
  8748. ASSERT( ((ULONG) CurrentOffset & (Scb->CompressionUnit - 1)) == 0 );
  8749. if (CurrentOffset < *StartingOffset) {
  8750. //
  8751. // Reserve a cluster to perform the write.
  8752. //
  8753. if (!NtfsReserveClusters( IrpContext, Scb, CurrentOffset, Scb->CompressionUnit )) {
  8754. Status = STATUS_DISK_FULL;
  8755. try_return( NOTHING );
  8756. }
  8757. //
  8758. // Limit the zero range.
  8759. //
  8760. CurrentBytes = Scb->CompressionUnit - (*StartingOffset - CurrentOffset);
  8761. if (CurrentOffset + Scb->CompressionUnit > CurrentFinalByte) {
  8762. CurrentBytes = CurrentFinalByte - *StartingOffset;
  8763. }
  8764. //
  8765. // See if we have to create an internal attribute stream.
  8766. //
  8767. if (Scb->FileObject == NULL) {
  8768. NtfsCreateInternalAttributeStream( IrpContext,
  8769. Scb,
  8770. FALSE,
  8771. &NtfsInternalUseFile[ZERORANGEINSTREAM_FILE_NUMBER] );
  8772. }
  8773. //
  8774. // Zero the data in the cache.
  8775. //
  8776. CcPinRead( Scb->FileObject,
  8777. (PLARGE_INTEGER) &CurrentOffset,
  8778. Scb->CompressionUnit,
  8779. TRUE,
  8780. &ZeroBufferBcb,
  8781. &ZeroBuffer );
  8782. #ifdef MAPCOUNT_DBG
  8783. IrpContext->MapCount++;
  8784. #endif
  8785. RtlZeroMemory( Add2Ptr( ZeroBuffer,
  8786. ((ULONG) *StartingOffset) & (Scb->CompressionUnit - 1)),
  8787. (ULONG) CurrentBytes );
  8788. CcSetDirtyPinnedData( ZeroBufferBcb, NULL );
  8789. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  8790. //
  8791. // Update the current offset to our position within the compression unit.
  8792. //
  8793. CurrentOffset += ((ULONG) *StartingOffset) & (Scb->CompressionUnit - 1);
  8794. //
  8795. // If the current compression unit includes the last byte to zero
  8796. // then we need flush and/or purge this compression unit.
  8797. //
  8798. } else if (CurrentOffset + Scb->CompressionUnit > CurrentFinalByte) {
  8799. //
  8800. // Reserve a cluster to perform the write.
  8801. //
  8802. if (!NtfsReserveClusters( IrpContext, Scb, CurrentOffset, Scb->CompressionUnit )) {
  8803. Status = STATUS_DISK_FULL;
  8804. try_return( NOTHING );
  8805. }
  8806. //
  8807. // Limit the zero range.
  8808. //
  8809. CurrentBytes = (ULONG) CurrentFinalByte & (Scb->CompressionUnit - 1);
  8810. //
  8811. // See if we have to create an internal attribute stream.
  8812. //
  8813. if (Scb->FileObject == NULL) {
  8814. NtfsCreateInternalAttributeStream( IrpContext,
  8815. Scb,
  8816. FALSE,
  8817. &NtfsInternalUseFile[ZERORANGEINSTREAM2_FILE_NUMBER] );
  8818. }
  8819. //
  8820. // Zero the data in the cache.
  8821. //
  8822. CcPinRead( Scb->FileObject,
  8823. (PLARGE_INTEGER) &CurrentOffset,
  8824. (ULONG) CurrentBytes,
  8825. TRUE,
  8826. &ZeroBufferBcb,
  8827. &ZeroBuffer );
  8828. #ifdef MAPCOUNT_DBG
  8829. IrpContext->MapCount++;
  8830. #endif
  8831. RtlZeroMemory( ZeroBuffer, (ULONG) CurrentBytes );
  8832. CcSetDirtyPinnedData( ZeroBufferBcb, NULL );
  8833. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  8834. } else {
  8835. //
  8836. // Compute the range we want to purge. We will process a maximum of 2Gig
  8837. // at a time.
  8838. //
  8839. CurrentBytes = CurrentFinalByte - CurrentOffset;
  8840. if (CurrentBytes > NTFS_MAX_ZERO_RANGE) {
  8841. CurrentBytes = NTFS_MAX_ZERO_RANGE;
  8842. }
  8843. //
  8844. // Round the size to a compression unit.
  8845. //
  8846. ((PLARGE_INTEGER) &CurrentBytes)->LowPart &= ~(Scb->CompressionUnit - 1);
  8847. //
  8848. // If this is the retry case then let's reduce the amount to
  8849. // zero.
  8850. //
  8851. if ((*StartingOffset == LastOffset) &&
  8852. (CurrentBytes > Scb->CompressionUnit)) {
  8853. CurrentBytes = Scb->CompressionUnit;
  8854. CurrentFinalByte = CurrentOffset + CurrentBytes;
  8855. }
  8856. //
  8857. // Purge the data in this range.
  8858. //
  8859. if ((Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
  8860. !CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  8861. (PLARGE_INTEGER) &CurrentOffset,
  8862. (ULONG) CurrentBytes,
  8863. FALSE )) {
  8864. //
  8865. // There may be a section in the cache manager which is being
  8866. // flushed. Go ahead and see if we can force the data out
  8867. // so the purge will succeed.
  8868. //
  8869. Status = NtfsFlushUserStream( IrpContext,
  8870. Scb,
  8871. &CurrentOffset,
  8872. (ULONG) CurrentBytes );
  8873. NtfsNormalizeAndCleanupTransaction( IrpContext,
  8874. &Status,
  8875. TRUE,
  8876. STATUS_UNEXPECTED_IO_ERROR );
  8877. //
  8878. // If this is the retry case then let's reduce the amount to
  8879. // zero.
  8880. //
  8881. if (CurrentBytes > Scb->CompressionUnit) {
  8882. CurrentBytes = Scb->CompressionUnit;
  8883. CurrentFinalByte = CurrentOffset + CurrentBytes;
  8884. }
  8885. //
  8886. // Now try the purge again.
  8887. //
  8888. if (!CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  8889. (PLARGE_INTEGER) &CurrentOffset,
  8890. (ULONG) CurrentBytes,
  8891. FALSE )) {
  8892. //
  8893. // If our retry failed then give up.
  8894. //
  8895. if (*StartingOffset == LastOffset) {
  8896. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  8897. leave;
  8898. }
  8899. //
  8900. // Otherwise show that we haven't advanced, but we
  8901. // will take one more crack at this.
  8902. //
  8903. CurrentBytes = 0;
  8904. }
  8905. }
  8906. //
  8907. // Delete the allocation if we have any bytes to work with.
  8908. //
  8909. if (CurrentBytes != 0) {
  8910. //
  8911. // Acquire the main resource to change the allocation.
  8912. //
  8913. if (!ReleaseScb) {
  8914. NtfsAcquireExclusiveScb( IrpContext, Scb );
  8915. ReleaseScb = TRUE;
  8916. }
  8917. //
  8918. // Now deallocate the clusters in this range if we have some to delete.
  8919. // Use ClusterCount to indicate the last Vcn to deallocate.
  8920. //
  8921. ClusterCount = CurrentVcn + LlClustersFromBytesTruncate( Scb->Vcb, CurrentBytes ) - 1;
  8922. if (NextVcn <= ClusterCount) {
  8923. NtfsDeleteAllocation( IrpContext,
  8924. FileObject,
  8925. Scb,
  8926. NextVcn,
  8927. ClusterCount,
  8928. TRUE,
  8929. TRUE );
  8930. //
  8931. // Move VDD fwd to protect this hole for compressed files
  8932. //
  8933. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  8934. if ((ULONGLONG)Scb->ValidDataToDisk < LlBytesFromClusters( Scb->Vcb, ClusterCount )) {
  8935. Scb->ValidDataToDisk = LlBytesFromClusters( Scb->Vcb, ClusterCount );
  8936. }
  8937. }
  8938. }
  8939. //
  8940. // Free up the reserved bitmap if there are any bits to clear.
  8941. //
  8942. NtfsFreeReservedClusters( Scb, CurrentOffset, (ULONG) CurrentBytes );
  8943. }
  8944. }
  8945. //
  8946. // Otherwise the file is uncompressed/non-sparse, we need to zero partial
  8947. // cluster and then we need to flush and/or purge the existing pages.
  8948. //
  8949. } else {
  8950. //
  8951. // Remember the current offset within the stream and
  8952. // the length to zero now.
  8953. //
  8954. CurrentOffset = *StartingOffset;
  8955. CurrentFinalByte = (CurrentOffset + 0x40000) & ~((LONGLONG) (0x40000 - 1));
  8956. if (CurrentFinalByte > Scb->Header.FileSize.QuadPart) {
  8957. CurrentFinalByte = Scb->Header.FileSize.QuadPart;
  8958. }
  8959. if (CurrentFinalByte > FinalZero) {
  8960. CurrentFinalByte = FinalZero;
  8961. }
  8962. //
  8963. // Determine the number of bytes remaining in the current cache view.
  8964. //
  8965. CurrentBytes = CurrentFinalByte - CurrentOffset;
  8966. //
  8967. // If this is the retry case then let's reduce the amount to
  8968. // zero.
  8969. //
  8970. if ((*StartingOffset == LastOffset) &&
  8971. (CurrentBytes > PAGE_SIZE)) {
  8972. CurrentBytes = PAGE_SIZE;
  8973. CurrentFinalByte = CurrentOffset + CurrentBytes;
  8974. }
  8975. //
  8976. // Purge the data in this range.
  8977. //
  8978. if (Scb->NonpagedScb->SegmentObject.DataSectionObject &&
  8979. !CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  8980. (PLARGE_INTEGER) &CurrentOffset,
  8981. (ULONG) CurrentBytes,
  8982. FALSE )) {
  8983. //
  8984. // There may be a section in the cache manager which is being
  8985. // flushed. Go ahead and see if we can force the data out
  8986. // so the purge will succeed.
  8987. //
  8988. Status = NtfsFlushUserStream( IrpContext,
  8989. Scb,
  8990. &CurrentOffset,
  8991. (ULONG) CurrentBytes );
  8992. NtfsNormalizeAndCleanupTransaction( IrpContext,
  8993. &Status,
  8994. TRUE,
  8995. STATUS_UNEXPECTED_IO_ERROR );
  8996. //
  8997. // Let's trim back the amount of data to purge at once.
  8998. //
  8999. if (CurrentBytes > PAGE_SIZE) {
  9000. CurrentBytes = PAGE_SIZE;
  9001. CurrentFinalByte = CurrentOffset + CurrentBytes;
  9002. }
  9003. //
  9004. // Now try the purge again.
  9005. //
  9006. if (!CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  9007. (PLARGE_INTEGER) &CurrentOffset,
  9008. (ULONG) CurrentBytes,
  9009. FALSE )) {
  9010. //
  9011. // If our retry failed then give up.
  9012. //
  9013. if (*StartingOffset == LastOffset) {
  9014. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  9015. leave;
  9016. }
  9017. //
  9018. // Otherwise show that we haven't advanced, but we
  9019. // will take one more crack at this.
  9020. //
  9021. CurrentBytes = 0;
  9022. }
  9023. }
  9024. //
  9025. // Continue if we have bytes to zero.
  9026. //
  9027. if (CurrentBytes != 0) {
  9028. //
  9029. // If we are within valid data length then zero the data.
  9030. //
  9031. if (CurrentOffset < Scb->Header.ValidDataLength.QuadPart) {
  9032. //
  9033. // See if we have to create an internal attribute stream.
  9034. //
  9035. if (Scb->FileObject == NULL) {
  9036. NtfsCreateInternalAttributeStream( IrpContext,
  9037. Scb,
  9038. FALSE,
  9039. &NtfsInternalUseFile[ZERORANGEINSTREAM3_FILE_NUMBER] );
  9040. }
  9041. //
  9042. // Zero the data in the cache.
  9043. //
  9044. CcPinRead( Scb->FileObject,
  9045. (PLARGE_INTEGER) &CurrentOffset,
  9046. (ULONG) CurrentBytes,
  9047. TRUE,
  9048. &ZeroBufferBcb,
  9049. &ZeroBuffer );
  9050. #ifdef MAPCOUNT_DBG
  9051. IrpContext->MapCount++;
  9052. #endif
  9053. RtlZeroMemory( ZeroBuffer, (ULONG) CurrentBytes );
  9054. CcSetDirtyPinnedData( ZeroBufferBcb, NULL );
  9055. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  9056. }
  9057. //
  9058. // We want to throttle the writes if there is more to do.
  9059. //
  9060. if (CurrentFinalByte < FinalZero) {
  9061. ThrottleWrites = TRUE;
  9062. }
  9063. }
  9064. }
  9065. //
  9066. // Check and see if we can advance valid data length.
  9067. //
  9068. if ((CurrentOffset + CurrentBytes > Scb->Header.ValidDataLength.QuadPart) &&
  9069. (*StartingOffset <= Scb->Header.ValidDataLength.QuadPart)) {
  9070. NtfsAcquireFsrtlHeader( Scb );
  9071. Scb->Header.ValidDataLength.QuadPart = CurrentOffset + CurrentBytes;
  9072. if (Scb->Header.ValidDataLength.QuadPart > Scb->Header.FileSize.QuadPart) {
  9073. Scb->Header.ValidDataLength.QuadPart = Scb->Header.FileSize.QuadPart;
  9074. }
  9075. #ifdef SYSCACHE_DEBUG
  9076. if (ScbIsBeingLogged( Scb )) {
  9077. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_STREAM, SCE_FLAG_SET_VDL, Scb->Header.ValidDataLength.QuadPart, 0, 0 );
  9078. }
  9079. #endif
  9080. NtfsReleaseFsrtlHeader( Scb );
  9081. }
  9082. //
  9083. // Checkpoint and past the current bytes.
  9084. //
  9085. if (NtfsIsExclusiveScb( Scb->Vcb->MftScb )) {
  9086. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_MFT );
  9087. }
  9088. NtfsCheckpointCurrentTransaction( IrpContext );
  9089. LastOffset = *StartingOffset;
  9090. if (CurrentBytes != 0) {
  9091. *StartingOffset = CurrentOffset + CurrentBytes;
  9092. }
  9093. //
  9094. // Release all of the resources so we don't create a bottleneck.
  9095. //
  9096. if (UnlockHeader) {
  9097. FsRtlUnlockFsRtlHeader( &Scb->Header );
  9098. IrpContext->CleanupStructure = NULL;
  9099. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  9100. UnlockHeader = FALSE;
  9101. }
  9102. if (ReleaseScb) {
  9103. NtfsReleaseScb( IrpContext, Scb );
  9104. ReleaseScb = FALSE;
  9105. }
  9106. //
  9107. // Now throttle the writes if we are accessing an uncompressed/non-sparse file.
  9108. //
  9109. if (ARGUMENT_PRESENT( FileObject ) && ThrottleWrites) {
  9110. CcCanIWrite( FileObject, 0x40000, TRUE, FALSE );
  9111. }
  9112. }
  9113. try_exit: NOTHING;
  9114. //
  9115. // If we have a user file object then check if we need to write any
  9116. // data to disk.
  9117. //
  9118. if ((Status == STATUS_SUCCESS) && ARGUMENT_PRESENT( FileObject )) {
  9119. if ((FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) ||
  9120. IsFileWriteThrough( FileObject, Scb->Vcb ))) {
  9121. //
  9122. // We either want to flush the Scb or flush and purge the Scb.
  9123. //
  9124. if ((Scb->CleanupCount == Scb->NonCachedCleanupCount) &&
  9125. !FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9126. //
  9127. // Flush and purge will alter filesizes on disk so preacquire the file exclusive
  9128. //
  9129. if (!ReleaseScb) {
  9130. NtfsAcquireExclusiveScb( IrpContext, Scb );
  9131. ReleaseScb = TRUE;
  9132. }
  9133. NtfsFlushAndPurgeScb( IrpContext, Scb, NULL );
  9134. } else {
  9135. Status = NtfsFlushUserStream( IrpContext, Scb, NULL, 0 );
  9136. NtfsNormalizeAndCleanupTransaction( IrpContext,
  9137. &Status,
  9138. TRUE,
  9139. STATUS_UNEXPECTED_IO_ERROR );
  9140. }
  9141. }
  9142. //
  9143. // If this is write through or non-cached then flush the log file as well.
  9144. //
  9145. if (IsFileWriteThrough( FileObject, Scb->Vcb ) ||
  9146. FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {
  9147. LfsFlushToLsn( Scb->Vcb->LogHandle, LiMax );
  9148. }
  9149. }
  9150. } finally {
  9151. DebugUnwind( NtfsZeroRangeInStream );
  9152. if (Status != STATUS_PENDING) {
  9153. //
  9154. // Release any held resources.
  9155. //
  9156. if (UnlockHeader) {
  9157. FsRtlUnlockFsRtlHeader( &Scb->Header );
  9158. IrpContext->CleanupStructure = NULL;
  9159. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  9160. }
  9161. if (ReleaseScb) {
  9162. NtfsReleaseScb( IrpContext, Scb );
  9163. }
  9164. //
  9165. // Even if STATUS_PENDING is returned we need to release the paging io
  9166. // resource. PrePostIrp will clear the IoAtEOF bit.
  9167. //
  9168. } else if (UnlockHeader) {
  9169. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  9170. }
  9171. //
  9172. // Cleanup the attribute context if used.
  9173. //
  9174. if (CleanupAttrContext) {
  9175. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  9176. }
  9177. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  9178. }
  9179. return Status;
  9180. }
  9181. BOOLEAN
  9182. NtfsModifyAttributeFlags (
  9183. IN PIRP_CONTEXT IrpContext,
  9184. IN PSCB Scb,
  9185. IN USHORT NewAttributeFlags
  9186. )
  9187. /*++
  9188. Routine Description:
  9189. This routine is called to change the attribute for an Scb. It changes the values
  9190. associated with the AttributeFlags (Encryption, Sparse, Compressed).
  9191. This routine does not commit so our caller must know how to unwind changes to the Scb and
  9192. Fcb (compression fields and Fcb Info).
  9193. NOTE - This routine will update the Fcb duplicate info and flags as well as the compression unit
  9194. fields in the Scb. The caller is responsible for cleaning these up on error.
  9195. Arguments:
  9196. Scb - Scb for the stream being modified.
  9197. NewAttributeFlags - New flags to associate with the stream.
  9198. FcbInfoFlags - Pointer to store changes to apply to the Fcb Info flags.
  9199. Return Value:
  9200. BOOLEAN - TRUE if our caller needs to update duplicate info. FALSE otherwise.
  9201. --*/
  9202. {
  9203. PFCB Fcb = Scb->Fcb;
  9204. PVCB Vcb = Scb->Vcb;
  9205. ATTRIBUTE_RECORD_HEADER NewAttribute;
  9206. PATTRIBUTE_RECORD_HEADER Attribute;
  9207. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  9208. ULONG AttributeSizeChange;
  9209. BOOLEAN ChangeTotalAllocated = FALSE;
  9210. BOOLEAN ChangeCompression = FALSE;
  9211. BOOLEAN ChangeSparse = FALSE;
  9212. BOOLEAN ChangeEncryption = FALSE;
  9213. ULONG NewCompressionUnit;
  9214. UCHAR NewCompressionUnitShift;
  9215. BOOLEAN UpdateDuplicate = FALSE;
  9216. PAGED_CODE();
  9217. ASSERT( Scb->AttributeFlags != NewAttributeFlags );
  9218. NtfsInitializeAttributeContext( &AttrContext );
  9219. //
  9220. // Use a try-finally to facilitate cleanup.
  9221. //
  9222. try {
  9223. //
  9224. // Lookup the attribute and pin it so that we can modify it.
  9225. //
  9226. if ((Scb->Header.NodeTypeCode == NTFS_NTC_SCB_INDEX) ||
  9227. (Scb->Header.NodeTypeCode == NTFS_NTC_SCB_ROOT_INDEX)) {
  9228. //
  9229. // Lookup the attribute record from the Scb.
  9230. //
  9231. if (!NtfsLookupAttributeByName( IrpContext,
  9232. Fcb,
  9233. &Fcb->FileReference,
  9234. $INDEX_ROOT,
  9235. &Scb->AttributeName,
  9236. NULL,
  9237. FALSE,
  9238. &AttrContext )) {
  9239. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
  9240. }
  9241. Attribute = NtfsFoundAttribute( &AttrContext );
  9242. } else {
  9243. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  9244. Attribute = NtfsFoundAttribute( &AttrContext );
  9245. //
  9246. // If the new state is encrypted and the file is not currently encrypted then convert to
  9247. // non-resident.
  9248. //
  9249. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED ) &&
  9250. NtfsIsAttributeResident( Attribute )) {
  9251. NtfsConvertToNonresident( IrpContext,
  9252. Fcb,
  9253. Attribute,
  9254. FALSE,
  9255. &AttrContext );
  9256. }
  9257. }
  9258. //
  9259. // Remember which flags are changing.
  9260. //
  9261. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) !=
  9262. FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9263. ChangeCompression = TRUE;
  9264. }
  9265. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_SPARSE ) !=
  9266. FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_SPARSE )) {
  9267. ChangeSparse = TRUE;
  9268. }
  9269. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED ) !=
  9270. FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  9271. ChangeEncryption = TRUE;
  9272. }
  9273. //
  9274. // Point to the current attribute and save the current flags.
  9275. //
  9276. NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
  9277. Attribute = NtfsFoundAttribute( &AttrContext );
  9278. //
  9279. // Compute the new compression size. Use the following to determine this
  9280. //
  9281. // - New state is not compressed/sparse - Unit/UnitShift = 0
  9282. // - New state includes compressed/sparse
  9283. // - Current state includes compressed/sparse - No change
  9284. // - Stream is compressible - Default values (64K max)
  9285. // - Stream is not compressible - Unit/UnitShift = 0
  9286. //
  9287. NewCompressionUnit = Scb->CompressionUnit;
  9288. NewCompressionUnitShift = Scb->CompressionUnitShift;
  9289. //
  9290. // Set the correct compression unit but only for data streams. We
  9291. // don't want to change this value for the Index Root.
  9292. //
  9293. if (NtfsIsTypeCodeCompressible( Attribute->TypeCode )) {
  9294. //
  9295. // We need a compression unit for the attribute now.
  9296. //
  9297. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9298. if (!FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9299. ChangeTotalAllocated = TRUE;
  9300. NewCompressionUnit = BytesFromClusters( Scb->Vcb, 1 << NTFS_CLUSTERS_PER_COMPRESSION );
  9301. NewCompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
  9302. //
  9303. // If the compression unit is larger than 64K then find the correct
  9304. // compression unit to reach exactly 64k.
  9305. //
  9306. while (NewCompressionUnit > Vcb->SparseFileUnit) {
  9307. NewCompressionUnitShift -= 1;
  9308. NewCompressionUnit /= 2;
  9309. }
  9310. }
  9311. } else {
  9312. //
  9313. // Check if we to remove the extra total allocated field.
  9314. //
  9315. if (FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9316. ChangeTotalAllocated = TRUE;
  9317. }
  9318. NewCompressionUnit = 0;
  9319. NewCompressionUnitShift = 0;
  9320. }
  9321. }
  9322. //
  9323. // If the attribute is resident, copy it here and remember its
  9324. // header size.
  9325. //
  9326. if (NtfsIsAttributeResident( Attribute )) {
  9327. RtlCopyMemory( &NewAttribute, Attribute, SIZEOF_RESIDENT_ATTRIBUTE_HEADER );
  9328. AttributeSizeChange = SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  9329. //
  9330. // Else if it is nonresident, copy it here, set the compression parameter,
  9331. // and remember its size.
  9332. //
  9333. } else {
  9334. ASSERT( NtfsIsTypeCodeCompressible( Attribute->TypeCode ));
  9335. //
  9336. // Pad the allocation if the new type includes sparse or compressed and file is
  9337. // not sparse or compressed (non-resident only).
  9338. //
  9339. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ) &&
  9340. !FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9341. LONGLONG Temp;
  9342. ULONG CompressionUnitInClusters;
  9343. //
  9344. // If we are turning compression on, then we need to fill out the
  9345. // allocation of the compression unit containing file size, or else
  9346. // it will be interpreted as compressed when we fault it in. This
  9347. // is peanuts compared to the dual copies of clusters we keep around
  9348. // in the loop below when we rewrite the file. We don't do this
  9349. // work if the file is sparse because the allocation has already
  9350. // been rounded up.
  9351. //
  9352. CompressionUnitInClusters = 1 << NewCompressionUnitShift;
  9353. Temp = LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart );
  9354. //
  9355. // If FileSize is not already at a cluster boundary, then add
  9356. // allocation.
  9357. //
  9358. if ((ULONG) Temp & (CompressionUnitInClusters - 1)) {
  9359. NtfsAddAllocation( IrpContext,
  9360. NULL,
  9361. Scb,
  9362. Temp,
  9363. CompressionUnitInClusters - ((ULONG)Temp & (CompressionUnitInClusters - 1)),
  9364. FALSE,
  9365. NULL );
  9366. //
  9367. // Update the duplicate info.
  9368. //
  9369. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  9370. Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  9371. SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  9372. UpdateDuplicate = TRUE;
  9373. }
  9374. NtfsWriteFileSizes( IrpContext,
  9375. Scb,
  9376. &Scb->Header.ValidDataLength.QuadPart,
  9377. FALSE,
  9378. TRUE,
  9379. TRUE );
  9380. //
  9381. // The attribute may have moved. We will cleanup the attribute
  9382. // context and look it up again.
  9383. //
  9384. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  9385. NtfsInitializeAttributeContext( &AttrContext );
  9386. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  9387. NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
  9388. Attribute = NtfsFoundAttribute( &AttrContext );
  9389. }
  9390. }
  9391. AttributeSizeChange = Attribute->Form.Nonresident.MappingPairsOffset;
  9392. if (Attribute->NameOffset != 0) {
  9393. AttributeSizeChange = Attribute->NameOffset;
  9394. }
  9395. RtlCopyMemory( &NewAttribute, Attribute, AttributeSizeChange );
  9396. }
  9397. //
  9398. // Set the new attribute flags.
  9399. //
  9400. NewAttribute.Flags = NewAttributeFlags;
  9401. //
  9402. // Now, log the changed attribute.
  9403. //
  9404. (VOID)NtfsWriteLog( IrpContext,
  9405. Vcb->MftScb,
  9406. NtfsFoundBcb( &AttrContext ),
  9407. UpdateResidentValue,
  9408. &NewAttribute,
  9409. AttributeSizeChange,
  9410. UpdateResidentValue,
  9411. Attribute,
  9412. AttributeSizeChange,
  9413. NtfsMftOffset( &AttrContext ),
  9414. PtrOffset( NtfsContainingFileRecord( &AttrContext ), Attribute),
  9415. 0,
  9416. Vcb->BytesPerFileRecordSegment );
  9417. //
  9418. // Change the attribute by calling the same routine called at restart.
  9419. //
  9420. NtfsRestartChangeValue( IrpContext,
  9421. NtfsContainingFileRecord( &AttrContext ),
  9422. PtrOffset( NtfsContainingFileRecord( &AttrContext ), Attribute ),
  9423. 0,
  9424. &NewAttribute,
  9425. AttributeSizeChange,
  9426. FALSE );
  9427. //
  9428. // See if we need to either add or remove a total allocated field.
  9429. //
  9430. if (ChangeTotalAllocated) {
  9431. NtfsSetTotalAllocatedField( IrpContext,
  9432. Scb,
  9433. (USHORT) FlagOn( NewAttributeFlags,
  9434. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ));
  9435. }
  9436. //
  9437. // If this is the main stream for a file we want to change the file attribute
  9438. // for this stream in both the standard information and duplicate
  9439. // information structure.
  9440. //
  9441. if (ChangeCompression &&
  9442. (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA ) ||
  9443. (Attribute->TypeCode == $INDEX_ALLOCATION))) {
  9444. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9445. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9446. } else {
  9447. ClearFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9448. }
  9449. ASSERTMSG( "conflict with flush",
  9450. NtfsIsSharedFcb( Fcb ) ||
  9451. (Fcb->PagingIoResource != NULL &&
  9452. NtfsIsSharedFcbPagingIo( Fcb )) );
  9453. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  9454. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  9455. UpdateDuplicate = TRUE;
  9456. }
  9457. if (ChangeSparse &&
  9458. FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_SPARSE ) &&
  9459. !FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE )) {
  9460. ASSERTMSG( "conflict with flush",
  9461. NtfsIsSharedFcb( Fcb ) ||
  9462. (Fcb->PagingIoResource != NULL &&
  9463. NtfsIsSharedFcbPagingIo( Fcb )) );
  9464. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  9465. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  9466. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  9467. UpdateDuplicate = TRUE;
  9468. }
  9469. if (ChangeEncryption &&
  9470. FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED ) &&
  9471. !FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_ENCRYPTED )) {
  9472. ASSERTMSG( "conflict with flush",
  9473. NtfsIsSharedFcb( Fcb ) ||
  9474. (Fcb->PagingIoResource != NULL &&
  9475. NtfsIsSharedFcbPagingIo( Fcb )) );
  9476. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_ENCRYPTED );
  9477. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  9478. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  9479. UpdateDuplicate = TRUE;
  9480. }
  9481. //
  9482. // Now put the new compression values in the Scb.
  9483. //
  9484. Scb->CompressionUnit = NewCompressionUnit;
  9485. Scb->CompressionUnitShift = NewCompressionUnitShift;
  9486. Scb->AttributeFlags = NewAttributeFlags;
  9487. } finally {
  9488. DebugUnwind( NtfsModifyAttributeFlags );
  9489. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  9490. }
  9491. return UpdateDuplicate;
  9492. }
  9493. PFCB
  9494. NtfsInitializeFileInExtendDirectory (
  9495. IN PIRP_CONTEXT IrpContext,
  9496. IN PVCB Vcb,
  9497. IN PCUNICODE_STRING FileName,
  9498. IN BOOLEAN ViewIndex,
  9499. IN ULONG CreateIfNotExist
  9500. )
  9501. /*++
  9502. Routine Description:
  9503. This routine creates/opens a file in the $Extend directory, by file name,
  9504. and returns an Fcb for the file.
  9505. Arguments:
  9506. Vcb - Pointer to the Vcb for the volume
  9507. FileName - Name of file to create in extend directory
  9508. ViewIndex - Indicates that the file is a view index.
  9509. CreateIfNotExist - Supplies TRUE if file should be created if it does not
  9510. already exist, or FALSE if file should not be created.
  9511. Return Value:
  9512. Fcb file existed or was created, NULL if file did not exist and was not created.
  9513. --*/
  9514. {
  9515. struct {
  9516. FILE_NAME FileName;
  9517. WCHAR FileNameChars[10];
  9518. } FileNameAttr;
  9519. FILE_REFERENCE FileReference;
  9520. LONGLONG FileRecordOffset;
  9521. PINDEX_ENTRY IndexEntry;
  9522. PBCB FileRecordBcb = NULL;
  9523. PBCB IndexEntryBcb = NULL;
  9524. PBCB ParentSecurityBcb = NULL;
  9525. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  9526. UCHAR FileNameFlags;
  9527. BOOLEAN FoundEntry;
  9528. PFCB ExtendFcb = Vcb->ExtendDirectory->Fcb;
  9529. PFCB Fcb = NULL;
  9530. BOOLEAN AcquiredFcbTable = FALSE;
  9531. BOOLEAN ReturnedExistingFcb = TRUE;
  9532. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  9533. PAGED_CODE();
  9534. ASSERT( NtfsIsExclusiveScb( Vcb->ExtendDirectory ) );
  9535. //
  9536. // Initialize the FileName.
  9537. //
  9538. ASSERT((FileName->Length / sizeof( WCHAR )) <= 10);
  9539. RtlZeroMemory( &FileNameAttr, sizeof(FileNameAttr) );
  9540. FileNameAttr.FileName.ParentDirectory = ExtendFcb->FileReference;
  9541. FileNameAttr.FileName.FileNameLength = (UCHAR)(FileName->Length / sizeof( WCHAR ));
  9542. RtlCopyMemory( FileNameAttr.FileName.FileName, FileName->Buffer, FileName->Length );
  9543. NtfsInitializeAttributeContext( &Context );
  9544. try {
  9545. //
  9546. // Does the file already exist?
  9547. //
  9548. FoundEntry = NtfsFindIndexEntry( IrpContext,
  9549. Vcb->ExtendDirectory,
  9550. &FileNameAttr,
  9551. FALSE,
  9552. NULL,
  9553. &IndexEntryBcb,
  9554. &IndexEntry,
  9555. NULL );
  9556. //
  9557. // Only procede if we either found the file or are supposed to create it.
  9558. //
  9559. if (FoundEntry || CreateIfNotExist) {
  9560. //
  9561. // If we did not find it, then start creating the file.
  9562. //
  9563. if (!FoundEntry) {
  9564. //
  9565. // We will now try to do all of the on-disk operations. This means first
  9566. // allocating and initializing an Mft record. After that we create
  9567. // an Fcb to use to access this record.
  9568. //
  9569. FileReference = NtfsAllocateMftRecord( IrpContext, Vcb, FALSE );
  9570. //
  9571. // Pin the file record we need.
  9572. //
  9573. NtfsPinMftRecord( IrpContext,
  9574. Vcb,
  9575. &FileReference,
  9576. TRUE,
  9577. &FileRecordBcb,
  9578. &FileRecord,
  9579. &FileRecordOffset );
  9580. //
  9581. // Initialize the file record header.
  9582. //
  9583. NtfsInitializeMftRecord( IrpContext,
  9584. Vcb,
  9585. &FileReference,
  9586. FileRecord,
  9587. FileRecordBcb,
  9588. FALSE );
  9589. //
  9590. // If we found the file, then just get its FileReference out of the
  9591. // IndexEntry.
  9592. //
  9593. } else {
  9594. FileReference = IndexEntry->FileReference;
  9595. }
  9596. //
  9597. // Now that we know the FileReference, we can create the Fcb.
  9598. //
  9599. NtfsAcquireFcbTable( IrpContext, Vcb );
  9600. AcquiredFcbTable = TRUE;
  9601. Fcb = NtfsCreateFcb( IrpContext,
  9602. Vcb,
  9603. FileReference,
  9604. FALSE,
  9605. ViewIndex,
  9606. &ReturnedExistingFcb );
  9607. //
  9608. // Reference the Fcb so it doesn't go away.
  9609. //
  9610. Fcb->ReferenceCount += 1;
  9611. NtfsReleaseFcbTable( IrpContext, Vcb );
  9612. AcquiredFcbTable = FALSE;
  9613. //
  9614. // Try to do a fast acquire, otherwise we need to release
  9615. // the parent extend directory and acquire in the canonical order
  9616. // child and then parent.
  9617. // Use AcquireWithPaging for don't wait functionality. Since the flag
  9618. // isn't set despite its name this will only acquire main
  9619. //
  9620. if (!NtfsAcquireFcbWithPaging( IrpContext, Fcb, ACQUIRE_DONT_WAIT )) {
  9621. NtfsReleaseScb( IrpContext, Vcb->ExtendDirectory );
  9622. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  9623. NtfsAcquireExclusiveScb( IrpContext, Vcb->ExtendDirectory );
  9624. }
  9625. NtfsAcquireFcbTable( IrpContext, Vcb );
  9626. Fcb->ReferenceCount -= 1;
  9627. NtfsReleaseFcbTable( IrpContext, Vcb );
  9628. //
  9629. // If we are creating this file, then carry on.
  9630. //
  9631. if (!FoundEntry) {
  9632. BOOLEAN LogIt = FALSE;
  9633. //
  9634. // Just copy the Security Id from the parent.
  9635. //
  9636. NtfsAcquireFcbSecurity( Fcb->Vcb );
  9637. Fcb->SecurityId = ExtendFcb->SecurityId;
  9638. ASSERT( Fcb->SharedSecurity == NULL );
  9639. Fcb->SharedSecurity = ExtendFcb->SharedSecurity;
  9640. Fcb->SharedSecurity->ReferenceCount++;
  9641. NtfsReleaseFcbSecurity( Fcb->Vcb );
  9642. //
  9643. // The changes to make on disk are first to create a standard information
  9644. // attribute. We start by filling the Fcb with the information we
  9645. // know and creating the attribute on disk.
  9646. //
  9647. NtfsInitializeFcbAndStdInfo( IrpContext,
  9648. Fcb,
  9649. FALSE,
  9650. ViewIndex,
  9651. FALSE,
  9652. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  9653. NULL );
  9654. //
  9655. // Now link the file into the $Extend directory.
  9656. //
  9657. NtfsAddLink( IrpContext,
  9658. TRUE,
  9659. Vcb->ExtendDirectory,
  9660. Fcb,
  9661. (PFILE_NAME)&FileNameAttr,
  9662. &LogIt,
  9663. &FileNameFlags,
  9664. NULL,
  9665. NULL,
  9666. NULL );
  9667. //
  9668. // Set this flag to indicate that the file is to be locked via the Scb
  9669. // pointers in the Vcb.
  9670. //
  9671. SetFlag( FileRecord->Flags, FILE_SYSTEM_FILE );
  9672. //
  9673. // Log the file record.
  9674. //
  9675. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  9676. Vcb->MftScb,
  9677. FileRecordBcb,
  9678. InitializeFileRecordSegment,
  9679. FileRecord,
  9680. FileRecord->FirstFreeByte,
  9681. Noop,
  9682. NULL,
  9683. 0,
  9684. FileRecordOffset,
  9685. 0,
  9686. 0,
  9687. Vcb->BytesPerFileRecordSegment );
  9688. //
  9689. // Verify that the file record for this file is valid.
  9690. //
  9691. } else {
  9692. ULONG CorruptHint;
  9693. if (!NtfsLookupAttributeByCode( IrpContext,
  9694. Fcb,
  9695. &Fcb->FileReference,
  9696. $STANDARD_INFORMATION,
  9697. &Context ) ||
  9698. !NtfsCheckFileRecord( Vcb, NtfsContainingFileRecord( &Context ), &Fcb->FileReference, &CorruptHint )) {
  9699. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, &Fcb->FileReference, NULL );
  9700. }
  9701. }
  9702. //
  9703. // Update Fcb fields from disk.
  9704. //
  9705. SetFlag( Fcb->FcbState, FCB_STATE_SYSTEM_FILE );
  9706. NtfsUpdateFcbInfoFromDisk( IrpContext, TRUE, Fcb, NULL );
  9707. }
  9708. } finally {
  9709. NtfsCleanupAttributeContext( IrpContext, &Context );
  9710. NtfsUnpinBcb( IrpContext, &FileRecordBcb );
  9711. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  9712. NtfsUnpinBcb( IrpContext, &ParentSecurityBcb );
  9713. //
  9714. // On any kind of error, nuke the Fcb.
  9715. //
  9716. if (AbnormalTermination()) {
  9717. //
  9718. // If some error caused us to abort, then delete
  9719. // the Fcb, because we are the only ones who will.
  9720. //
  9721. if (!ReturnedExistingFcb && Fcb) {
  9722. if (!AcquiredFcbTable) {
  9723. NtfsAcquireFcbTable( IrpContext, Vcb );
  9724. AcquiredFcbTable = TRUE;
  9725. }
  9726. NtfsDeleteFcb( IrpContext, &Fcb, &AcquiredFcbTable );
  9727. ASSERT(!AcquiredFcbTable);
  9728. }
  9729. if (AcquiredFcbTable) {
  9730. NtfsReleaseFcbTable( IrpContext, Vcb );
  9731. }
  9732. }
  9733. }
  9734. return Fcb;
  9735. }
  9736. VOID
  9737. NtfsFillBasicInfo (
  9738. OUT PFILE_BASIC_INFORMATION Buffer,
  9739. IN PSCB Scb
  9740. )
  9741. /*++
  9742. Routine Description:
  9743. This is the common routine which transfers data from the Scb/Fcb to the BasicInfo structure.
  9744. Arguments:
  9745. Buffer - Pointer to structure to fill in. Our caller has already validated it.
  9746. Scb - Stream the caller has a handle to.
  9747. Return Value:
  9748. None
  9749. --*/
  9750. {
  9751. PFCB Fcb = Scb->Fcb;
  9752. PAGED_CODE();
  9753. //
  9754. // Zero the output buffer.
  9755. //
  9756. RtlZeroMemory( Buffer, sizeof( FILE_BASIC_INFORMATION ));
  9757. //
  9758. // Fill in the basic information fields
  9759. //
  9760. Buffer->CreationTime.QuadPart = Fcb->Info.CreationTime;
  9761. Buffer->LastWriteTime.QuadPart = Fcb->Info.LastModificationTime;
  9762. Buffer->ChangeTime.QuadPart = Fcb->Info.LastChangeTime;
  9763. Buffer->LastAccessTime.QuadPart = Fcb->CurrentLastAccess;
  9764. //
  9765. // Capture the attributes from the Fcb except for the stream specific values.
  9766. // Also mask out any private Ntfs attribute flags.
  9767. //
  9768. Buffer->FileAttributes = Fcb->Info.FileAttributes;
  9769. ClearFlag( Buffer->FileAttributes,
  9770. (~FILE_ATTRIBUTE_VALID_FLAGS |
  9771. FILE_ATTRIBUTE_COMPRESSED |
  9772. FILE_ATTRIBUTE_TEMPORARY |
  9773. FILE_ATTRIBUTE_SPARSE_FILE |
  9774. FILE_ATTRIBUTE_ENCRYPTED) );
  9775. //
  9776. // Pick up the sparse, encrypted and temp bits for this stream from the Scb.
  9777. //
  9778. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  9779. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  9780. }
  9781. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  9782. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_ENCRYPTED );
  9783. }
  9784. if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
  9785. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
  9786. }
  9787. //
  9788. // If this is an index stream then mark it as a directory. Capture the compressed
  9789. // state from either the Fcb or Scb.
  9790. //
  9791. if (Scb->AttributeTypeCode == $INDEX_ALLOCATION) {
  9792. if (IsDirectory( &Fcb->Info ) || IsViewIndex( &Fcb->Info )) {
  9793. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
  9794. //
  9795. // Capture the compression state from the Fcb.
  9796. //
  9797. SetFlag( Buffer->FileAttributes,
  9798. FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED ));
  9799. //
  9800. // Otherwise capture the value in the Scb itself.
  9801. //
  9802. } else if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9803. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9804. }
  9805. //
  9806. // In all other cases we can use the value in the Scb.
  9807. //
  9808. } else {
  9809. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9810. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9811. }
  9812. }
  9813. //
  9814. // If there are no flags set then explicitly set the NORMAL flag.
  9815. //
  9816. if (Buffer->FileAttributes == 0) {
  9817. Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  9818. }
  9819. return;
  9820. }
  9821. VOID
  9822. NtfsFillStandardInfo (
  9823. OUT PFILE_STANDARD_INFORMATION Buffer,
  9824. IN PSCB Scb,
  9825. IN PCCB Ccb OPTIONAL
  9826. )
  9827. /*++
  9828. Routine Description:
  9829. This is the common routine which transfers data from the Scb/Fcb to the StandardInfo structure.
  9830. Arguments:
  9831. Buffer - Pointer to structure to fill in. Our caller has already validated it.
  9832. Scb - Stream the caller has a handle to.
  9833. Ccb - Ccb for the user's open.
  9834. Return Value:
  9835. None
  9836. --*/
  9837. {
  9838. PFCB Fcb = Scb->Fcb;
  9839. PAGED_CODE();
  9840. //
  9841. // Zero out the output buffer.
  9842. //
  9843. RtlZeroMemory( Buffer, sizeof( FILE_STANDARD_INFORMATION ));
  9844. //
  9845. // Fill in the buffer from the Scb, Fcb and Ccb.
  9846. //
  9847. //
  9848. // Return sizes only for non-index streams.
  9849. //
  9850. if ((Scb->AttributeTypeCode != $INDEX_ALLOCATION) ||
  9851. (!IsDirectory( &Fcb->Info ) && !IsViewIndex( &Fcb->Info ))) {
  9852. Buffer->AllocationSize.QuadPart = Scb->TotalAllocated;
  9853. Buffer->EndOfFile = Scb->Header.FileSize;
  9854. }
  9855. Buffer->NumberOfLinks = Fcb->LinkCount;
  9856. //
  9857. // Let's initialize these boolean fields.
  9858. //
  9859. Buffer->DeletePending = Buffer->Directory = FALSE;
  9860. //
  9861. // Get the delete and directory flags from the Fcb/Scb state. Note that
  9862. // the sense of the delete pending bit refers to the file if opened as
  9863. // file. Otherwise it refers to the attribute only.
  9864. //
  9865. // But only do the test if the Ccb has been supplied.
  9866. //
  9867. if (ARGUMENT_PRESENT( Ccb )) {
  9868. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  9869. if ((Scb->Fcb->LinkCount == 0) ||
  9870. ((Ccb->Lcb != NULL) && FlagOn( Ccb->Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE ))) {
  9871. Buffer->DeletePending = TRUE;
  9872. }
  9873. Buffer->Directory = BooleanIsDirectory( &Scb->Fcb->Info );
  9874. } else {
  9875. Buffer->DeletePending = BooleanFlagOn( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
  9876. }
  9877. } else {
  9878. Buffer->Directory = BooleanIsDirectory( &Scb->Fcb->Info );
  9879. }
  9880. return;
  9881. }
  9882. VOID
  9883. NtfsFillNetworkOpenInfo (
  9884. OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
  9885. IN PSCB Scb
  9886. )
  9887. /*++
  9888. Routine Description:
  9889. This is the common routine which transfers data from the Scb/Fcb to the NetworkOpenInfo structure.
  9890. Arguments:
  9891. Buffer - Pointer to structure to fill in. Our caller has already validated it.
  9892. Scb - Stream the caller has a handle to.
  9893. Return Value:
  9894. None
  9895. --*/
  9896. {
  9897. PFCB Fcb = Scb->Fcb;
  9898. PAGED_CODE();
  9899. //
  9900. // Zero the output buffer.
  9901. //
  9902. RtlZeroMemory( Buffer, sizeof( FILE_NETWORK_OPEN_INFORMATION ));
  9903. //
  9904. // Fill in the basic information fields
  9905. //
  9906. Buffer->CreationTime.QuadPart = Fcb->Info.CreationTime;
  9907. Buffer->LastWriteTime.QuadPart = Fcb->Info.LastModificationTime;
  9908. Buffer->ChangeTime.QuadPart = Fcb->Info.LastChangeTime;
  9909. Buffer->LastAccessTime.QuadPart = Fcb->CurrentLastAccess;
  9910. //
  9911. // Capture the attributes from the Fcb except for the stream specific values.
  9912. // Also mask out any private Ntfs attribute flags.
  9913. //
  9914. Buffer->FileAttributes = Fcb->Info.FileAttributes;
  9915. ClearFlag( Buffer->FileAttributes,
  9916. (~FILE_ATTRIBUTE_VALID_FLAGS |
  9917. FILE_ATTRIBUTE_COMPRESSED |
  9918. FILE_ATTRIBUTE_TEMPORARY |
  9919. FILE_ATTRIBUTE_SPARSE_FILE |
  9920. FILE_ATTRIBUTE_ENCRYPTED) );
  9921. //
  9922. // Pick up the sparse, encrypted and temp bits for this stream from the Scb.
  9923. //
  9924. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  9925. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  9926. }
  9927. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  9928. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_ENCRYPTED );
  9929. }
  9930. if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
  9931. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
  9932. }
  9933. //
  9934. // If this is an index stream then mark it as a directory. Capture the compressed
  9935. // state from either the Fcb or Scb.
  9936. //
  9937. if (Scb->AttributeTypeCode == $INDEX_ALLOCATION) {
  9938. if (IsDirectory( &Fcb->Info ) || IsViewIndex( &Fcb->Info )) {
  9939. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
  9940. //
  9941. // Capture the compression state from the Fcb.
  9942. //
  9943. SetFlag( Buffer->FileAttributes,
  9944. FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED ));
  9945. //
  9946. // Otherwise capture the value in the Scb itself.
  9947. //
  9948. } else if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9949. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9950. }
  9951. //
  9952. // In all other cases we can use the value in the Scb.
  9953. //
  9954. } else {
  9955. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9956. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9957. }
  9958. //
  9959. // In the non-index case we use the sizes from the Scb.
  9960. //
  9961. Buffer->AllocationSize.QuadPart = Scb->TotalAllocated;
  9962. Buffer->EndOfFile = Scb->Header.FileSize;
  9963. }
  9964. //
  9965. // If there are no flags set then explicitly set the NORMAL flag.
  9966. //
  9967. if (Buffer->FileAttributes == 0) {
  9968. Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  9969. }
  9970. return;
  9971. }
  9972. //
  9973. // Internal support routine
  9974. //
  9975. BOOLEAN
  9976. NtfsLookupInFileRecord (
  9977. IN PIRP_CONTEXT IrpContext,
  9978. IN PFCB Fcb,
  9979. IN PFILE_REFERENCE BaseFileReference OPTIONAL,
  9980. IN ATTRIBUTE_TYPE_CODE QueriedTypeCode,
  9981. IN PCUNICODE_STRING QueriedName OPTIONAL,
  9982. IN PVCN Vcn OPTIONAL,
  9983. IN BOOLEAN IgnoreCase,
  9984. IN PVOID QueriedValue OPTIONAL,
  9985. IN ULONG QueriedValueLength,
  9986. OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  9987. )
  9988. /*++
  9989. Routine Description:
  9990. This routine attempts to find the fist occurrence of an attribute with
  9991. the specified AttributeTypeCode and the specified QueriedName in the
  9992. specified BaseFileReference. If we find one, its attribute record is
  9993. pinned and returned.
  9994. Arguments:
  9995. Fcb - Requested file.
  9996. BaseFileReference - The base entry for this file in the MFT. Only needed
  9997. on initial invocation.
  9998. QueriedTypeCode - The attribute code to search for, if present.
  9999. QueriedName - The attribute name to search for, if present.
  10000. Vcn - Search for the nonresident attribute instance that has this Vcn
  10001. IgnoreCase - Ignore case while comparing names. Ignored if QueriedName
  10002. not present.
  10003. QueriedValue - The actual attribute value to search for, if present.
  10004. QueriedValueLength - The length of the attribute value to search for.
  10005. Ignored if QueriedValue is not present.
  10006. Context - Describes the prior found attribute on invocation (if
  10007. this was not the initial enumeration), and contains the next found
  10008. attribute on return.
  10009. Return Value:
  10010. BOOLEAN - True if we found an attribute, false otherwise.
  10011. --*/
  10012. {
  10013. PATTRIBUTE_RECORD_HEADER Attribute;
  10014. BOOLEAN Result = FALSE;
  10015. PAGED_CODE();
  10016. DebugTrace( +1, Dbg, ("NtfsLookupInFileRecord\n") );
  10017. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10018. DebugTrace( 0, Dbg, ("BaseFileReference = %08I64x\n",
  10019. ARGUMENT_PRESENT(BaseFileReference) ?
  10020. NtfsFullSegmentNumber( BaseFileReference ) :
  10021. 0xFFFFFFFFFFFF) );
  10022. DebugTrace( 0, Dbg, ("QueriedTypeCode = %08lx\n", QueriedTypeCode) );
  10023. DebugTrace( 0, Dbg, ("QueriedName = %08lx\n", QueriedName) );
  10024. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  10025. DebugTrace( 0, Dbg, ("QueriedValue = %08lx\n", QueriedValue) );
  10026. DebugTrace( 0, Dbg, ("QueriedValueLength = %08lx\n", QueriedValueLength) );
  10027. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10028. //
  10029. // Is this the initial enumeration? If so start at the beginning.
  10030. //
  10031. if (Context->FoundAttribute.Bcb == NULL) {
  10032. PBCB Bcb;
  10033. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10034. PATTRIBUTE_RECORD_HEADER TempAttribute;
  10035. ASSERT(!ARGUMENT_PRESENT(QueriedName) || !ARGUMENT_PRESENT(QueriedValue));
  10036. NtfsReadFileRecord( IrpContext,
  10037. Fcb->Vcb,
  10038. BaseFileReference,
  10039. &Bcb,
  10040. &FileRecord,
  10041. &TempAttribute,
  10042. &Context->FoundAttribute.MftFileOffset );
  10043. Attribute = TempAttribute;
  10044. //
  10045. // Initialize the found attribute context
  10046. //
  10047. Context->FoundAttribute.Bcb = Bcb;
  10048. Context->FoundAttribute.FileRecord = FileRecord;
  10049. //
  10050. // And show that we have neither found nor used the External
  10051. // Attributes List attribute.
  10052. //
  10053. Context->AttributeList.Bcb = NULL;
  10054. Context->AttributeList.AttributeList = NULL;
  10055. //
  10056. // The Usn Journal support uses the Usn Journal Fcb to look up $STANDARD_INFORMATION
  10057. // in an arbitrary file. We will detect the case of $STANDARD_INFORMATION and the
  10058. // "wrong" Fcb and get out.
  10059. //
  10060. if (ARGUMENT_PRESENT( BaseFileReference ) &&
  10061. !NtfsEqualMftRef( BaseFileReference, &Fcb->FileReference ) &&
  10062. (QueriedTypeCode == $STANDARD_INFORMATION) &&
  10063. (Attribute->TypeCode == $STANDARD_INFORMATION)) {
  10064. //
  10065. // We found it. Return it in the enumeration context.
  10066. //
  10067. Context->FoundAttribute.Attribute = Attribute;
  10068. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10069. Attribute ));
  10070. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (No code or SI)\n") );
  10071. try_return( Result = TRUE );
  10072. }
  10073. //
  10074. // Scan to see if there is an attribute list, and if so, defer
  10075. // immediately to NtfsLookupExternalAttribute - we must guide the
  10076. // enumeration by the attribute list.
  10077. //
  10078. while (TempAttribute->TypeCode <= $ATTRIBUTE_LIST) {
  10079. if (TempAttribute->RecordLength == 0) {
  10080. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10081. }
  10082. if (TempAttribute->TypeCode == $ATTRIBUTE_LIST) {
  10083. ULONG AttributeListLength;
  10084. PATTRIBUTE_LIST_CONTEXT Ex = &Context->AttributeList;
  10085. Context->FoundAttribute.Attribute = TempAttribute;
  10086. if ((QueriedTypeCode != $UNUSED) &&
  10087. (QueriedTypeCode == $ATTRIBUTE_LIST)) {
  10088. //
  10089. // We found it. Return it in the enumeration context.
  10090. //
  10091. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10092. TempAttribute) );
  10093. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (attribute list)\n") );
  10094. try_return( Result = TRUE );
  10095. }
  10096. //
  10097. // Build up the context for the attribute list by hand here
  10098. // for efficiency, so that we can call NtfsMapAttributeValue.
  10099. //
  10100. Ex->AttributeList = TempAttribute;
  10101. NtfsMapAttributeValue( IrpContext,
  10102. Fcb,
  10103. (PVOID *)&Ex->FirstEntry,
  10104. &AttributeListLength,
  10105. &Ex->Bcb,
  10106. Context );
  10107. Ex->Entry = Ex->FirstEntry;
  10108. Ex->BeyondFinalEntry = Add2Ptr( Ex->FirstEntry, AttributeListLength );
  10109. //
  10110. // If the list is non-resident then remember the correct Bcb for
  10111. // the list.
  10112. //
  10113. if (!NtfsIsAttributeResident( TempAttribute )) {
  10114. Ex->NonresidentListBcb = Ex->Bcb;
  10115. Ex->Bcb = Context->FoundAttribute.Bcb;
  10116. Context->FoundAttribute.Bcb = NULL;
  10117. //
  10118. // Otherwise unpin the Bcb for the current attribute.
  10119. //
  10120. } else {
  10121. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10122. }
  10123. //
  10124. // We are now ready to iterate through the external attributes.
  10125. // The Context->FoundAttribute.Bcb being NULL signals
  10126. // NtfsLookupExternalAttribute that is should start at
  10127. // Context->External.Entry instead of the entry immediately following.
  10128. //
  10129. Result = NtfsLookupExternalAttribute( IrpContext,
  10130. Fcb,
  10131. QueriedTypeCode,
  10132. QueriedName,
  10133. Vcn,
  10134. IgnoreCase,
  10135. QueriedValue,
  10136. QueriedValueLength,
  10137. Context );
  10138. try_return( NOTHING );
  10139. }
  10140. TempAttribute = NtfsGetNextRecord( TempAttribute );
  10141. NtfsCheckRecordBound( TempAttribute, FileRecord, Fcb->Vcb->BytesPerFileRecordSegment );
  10142. }
  10143. if ((QueriedTypeCode == $UNUSED) ||
  10144. ((QueriedTypeCode == $STANDARD_INFORMATION) &&
  10145. (Attribute->TypeCode == $STANDARD_INFORMATION))) {
  10146. //
  10147. // We found it. Return it in the enumeration context.
  10148. //
  10149. Context->FoundAttribute.Attribute = Attribute;
  10150. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10151. Attribute ));
  10152. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (No code or SI)\n") );
  10153. try_return( Result = TRUE );
  10154. }
  10155. } else {
  10156. //
  10157. // Special case if the prior found attribute was $END, this is
  10158. // because we cannot search for the next entry after $END.
  10159. //
  10160. Attribute = Context->FoundAttribute.Attribute;
  10161. if (!Context->FoundAttribute.AttributeDeleted) {
  10162. Attribute = NtfsGetNextRecord( Attribute );
  10163. }
  10164. NtfsCheckRecordBound( Attribute, Context->FoundAttribute.FileRecord, Fcb->Vcb->BytesPerFileRecordSegment );
  10165. Context->FoundAttribute.AttributeDeleted = FALSE;
  10166. if (Attribute->TypeCode == $END) {
  10167. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> FALSE ($END)\n") );
  10168. try_return( Result = FALSE );
  10169. }
  10170. if (Attribute->RecordLength == 0) {
  10171. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10172. }
  10173. if (QueriedTypeCode == $UNUSED) {
  10174. //
  10175. // We found it. Return it in the enumeration context.
  10176. //
  10177. Context->FoundAttribute.Attribute = Attribute;
  10178. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10179. Attribute) );
  10180. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (No code)\n") );
  10181. try_return( Result = TRUE );
  10182. }
  10183. }
  10184. Result = NtfsFindInFileRecord( IrpContext,
  10185. Attribute,
  10186. &Context->FoundAttribute.Attribute,
  10187. QueriedTypeCode,
  10188. QueriedName,
  10189. IgnoreCase,
  10190. QueriedValue,
  10191. QueriedValueLength );
  10192. try_exit: NOTHING;
  10193. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord ->\n") );
  10194. return Result;
  10195. }
  10196. //
  10197. // Internal support routine
  10198. //
  10199. BOOLEAN
  10200. NtfsFindInFileRecord (
  10201. IN PIRP_CONTEXT IrpContext,
  10202. IN PATTRIBUTE_RECORD_HEADER Attribute,
  10203. OUT PATTRIBUTE_RECORD_HEADER *ReturnAttribute,
  10204. IN ATTRIBUTE_TYPE_CODE QueriedTypeCode,
  10205. IN PCUNICODE_STRING QueriedName OPTIONAL,
  10206. IN BOOLEAN IgnoreCase,
  10207. IN PVOID QueriedValue OPTIONAL,
  10208. IN ULONG QueriedValueLength
  10209. )
  10210. /*++
  10211. Routine Description:
  10212. This routine looks up an attribute in a file record. It returns
  10213. TRUE if the attribute was found, or FALSE if not found. If FALSE
  10214. is returned, the return attribute pointer points to the spot where
  10215. the described attribute should be inserted. Thus this routine
  10216. determines how attributes are collated within file records.
  10217. Arguments:
  10218. Attribute - The attribute within the file record at which the search
  10219. should begin.
  10220. ReturnAttribute - Pointer to the found attribute if returning TRUE,
  10221. or to the position to insert the attribute if returning
  10222. FALSE.
  10223. QueriedTypeCode - The attribute code to search for, if present.
  10224. QueriedName - The attribute name to search for, if present.
  10225. IgnoreCase - Ignore case while comparing names. Ignored if QueriedName
  10226. not present.
  10227. QueriedValue - The actual attribute value to search for, if present.
  10228. QueriedValueLength - The length of the attribute value to search for.
  10229. Ignored if QueriedValue is not present.
  10230. Return Value:
  10231. BOOLEAN - True if we found an attribute, false otherwise.
  10232. --*/
  10233. {
  10234. PWCH UpcaseTable = IrpContext->Vcb->UpcaseTable;
  10235. ULONG UpcaseTableSize = IrpContext->Vcb->UpcaseTableSize;
  10236. PAGED_CODE();
  10237. //
  10238. // Now walk through the base file record looking for the atttribute. If
  10239. // the query is "exhausted", i.e., if a type code, attribute name, or
  10240. // value is encountered which is greater than the one we are querying for,
  10241. // then we return FALSE immediately out of this loop. If an exact match
  10242. // is seen, we break, and return the match at the end of this routine.
  10243. // Otherwise we keep looping while the query is not exhausted.
  10244. //
  10245. // IMPORTANT NOTE:
  10246. //
  10247. // The exact semantics of this loop are important, as they determine the
  10248. // exact details of attribute ordering within the file record. A change
  10249. // in the order of the tests within this loop CHANGES THE FILE STRUCTURE,
  10250. // and possibly makes older NTFS volumes unreadable.
  10251. //
  10252. while ( TRUE ) {
  10253. //
  10254. // Mark this attribute position, since we may be returning TRUE
  10255. // or FALSE below.
  10256. //
  10257. *ReturnAttribute = Attribute;
  10258. //
  10259. // Leave with the correct current position intact, if we hit the
  10260. // end or a greater attribute type code.
  10261. //
  10262. // COLLATION RULE:
  10263. //
  10264. // Attributes are ordered by increasing attribute type code.
  10265. //
  10266. if (QueriedTypeCode < Attribute->TypeCode) {
  10267. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord->FALSE (Type Code)\n") );
  10268. return FALSE;
  10269. }
  10270. if (Attribute->RecordLength == 0) {
  10271. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
  10272. }
  10273. //
  10274. // If the attribute type code is a match, then need to check either
  10275. // the name or the value or return a match.
  10276. //
  10277. // COLLATION RULE:
  10278. //
  10279. // Within equal attribute type codes, attribute names are ordered
  10280. // by increasing lexigraphical order ignoring case. If two names
  10281. // exist which are equal when case is ignored, they must not be
  10282. // equal when compared with exact case, and within such equal
  10283. // names they are ordered by increasing lexical value with exact
  10284. // case.
  10285. //
  10286. if (QueriedTypeCode == Attribute->TypeCode) {
  10287. //
  10288. // Handle name-match case
  10289. //
  10290. if (ARGUMENT_PRESENT(QueriedName)) {
  10291. UNICODE_STRING AttributeName;
  10292. FSRTL_COMPARISON_RESULT Result;
  10293. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  10294. //
  10295. // See if we have a name match.
  10296. //
  10297. if (NtfsAreNamesEqual( UpcaseTable,
  10298. &AttributeName,
  10299. QueriedName,
  10300. IgnoreCase )) {
  10301. break;
  10302. }
  10303. //
  10304. // Compare the names ignoring case.
  10305. //
  10306. Result = NtfsCollateNames( UpcaseTable,
  10307. UpcaseTableSize,
  10308. QueriedName,
  10309. &AttributeName,
  10310. GreaterThan,
  10311. TRUE);
  10312. //
  10313. // Break out if the result is LessThan, or if the result
  10314. // is Equal to *and* the exact case compare yields LessThan.
  10315. //
  10316. if ((Result == LessThan) || ((Result == EqualTo) &&
  10317. (NtfsCollateNames( UpcaseTable,
  10318. UpcaseTableSize,
  10319. QueriedName,
  10320. &AttributeName,
  10321. GreaterThan,
  10322. FALSE) == LessThan))) {
  10323. return FALSE;
  10324. }
  10325. //
  10326. // Handle value-match case
  10327. //
  10328. // COLLATION RULE:
  10329. //
  10330. // Values are collated by increasing values with unsigned-byte
  10331. // compares. I.e., the first different byte is compared unsigned,
  10332. // and the value with the highest byte comes second. If a shorter
  10333. // value is exactly equal to the first part of a longer value, then
  10334. // the shorter value comes first.
  10335. //
  10336. // Note that for values which are actually Unicode strings, the
  10337. // collation is different from attribute name ordering above. However,
  10338. // attribute ordering is visible outside the file system (you can
  10339. // query "openable" attributes), whereas the ordering of indexed values
  10340. // is not visible (for example you cannot query links). In any event,
  10341. // the ordering of values must be considered up to the system, and
  10342. // *must* be considered nondetermistic from the standpoint of a user.
  10343. //
  10344. } else if (ARGUMENT_PRESENT( QueriedValue )) {
  10345. ULONG Diff, MinLength;
  10346. //
  10347. // Form the minimum of the ValueLength and the Attribute Value.
  10348. //
  10349. MinLength = Attribute->Form.Resident.ValueLength;
  10350. if (QueriedValueLength < MinLength) {
  10351. MinLength = QueriedValueLength;
  10352. }
  10353. //
  10354. // Find the first different byte.
  10355. //
  10356. Diff = (ULONG)RtlCompareMemory( QueriedValue,
  10357. NtfsGetValue(Attribute),
  10358. MinLength );
  10359. //
  10360. // The first substring was equal.
  10361. //
  10362. if (Diff == MinLength) {
  10363. //
  10364. // If the two lengths are equal, then we have an exact
  10365. // match.
  10366. //
  10367. if (QueriedValueLength == Attribute->Form.Resident.ValueLength) {
  10368. break;
  10369. }
  10370. //
  10371. // Otherwise the shorter guy comes first; we can return
  10372. // FALSE if the queried value is shorter.
  10373. //
  10374. if (QueriedValueLength < Attribute->Form.Resident.ValueLength) {
  10375. return FALSE;
  10376. }
  10377. //
  10378. // Otherwise some byte was different. Do an unsigned compare
  10379. // of that byte to determine the ordering. Time to leave if
  10380. // the queried value byte is less.
  10381. //
  10382. } else if (*((PUCHAR)QueriedValue + Diff) <
  10383. *((PUCHAR)NtfsGetValue(Attribute) + Diff)) {
  10384. return FALSE;
  10385. }
  10386. //
  10387. // Otherwise we have a simple match on code
  10388. //
  10389. } else {
  10390. break;
  10391. }
  10392. }
  10393. Attribute = NtfsGetNextRecord( Attribute );
  10394. NtfsCheckRecordBound( Attribute,
  10395. (ULONG_PTR)*ReturnAttribute & ~((ULONG_PTR)IrpContext->Vcb->BytesPerFileRecordSegment - 1),
  10396. IrpContext->Vcb->BytesPerFileRecordSegment );
  10397. }
  10398. return TRUE;
  10399. }
  10400. //
  10401. // Internal support routine
  10402. //
  10403. BOOLEAN
  10404. NtfsLookupExternalAttribute (
  10405. IN PIRP_CONTEXT IrpContext,
  10406. IN PFCB Fcb,
  10407. IN ATTRIBUTE_TYPE_CODE QueriedTypeCode,
  10408. IN PCUNICODE_STRING QueriedName OPTIONAL,
  10409. IN PVCN Vcn OPTIONAL,
  10410. IN BOOLEAN IgnoreCase,
  10411. IN PVOID QueriedValue OPTIONAL,
  10412. IN ULONG QueriedValueLength,
  10413. OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10414. )
  10415. /*++
  10416. Routine Description:
  10417. This routine attempts to find the first occurrence of an attribute with
  10418. the specified AttributeTypeCode and the specified QueriedName and Value
  10419. among the external attributes described by the Context. If we find one,
  10420. its attribute record is pinned and returned.
  10421. Arguments:
  10422. Fcb - Requested file.
  10423. QueriedTypeCode - The attribute code to search for, if present.
  10424. QueriedName - The attribute name to search for, if present.
  10425. Vcn - Lookup nonresident attribute instance with this Vcn
  10426. IgnoreCase - Ignore case while comparing names. Ignored if QueriedName
  10427. not present.
  10428. QueriedValue - The actual attribute value to search for, if present.
  10429. QueriedValueLength - The length of the attribute value to search for.
  10430. Ignored if QueriedValue is not present.
  10431. Context - Describes the prior found attribute on invocation (if
  10432. this was not the initial enumeration), and contains the next found
  10433. attribute on return.
  10434. Return Value:
  10435. BOOLEAN - True if we found an attribute, false otherwise.
  10436. --*/
  10437. {
  10438. PATTRIBUTE_LIST_ENTRY Entry, LastEntry;
  10439. PWCH UpcaseTable = IrpContext->Vcb->UpcaseTable;
  10440. ULONG UpcaseTableSize = IrpContext->Vcb->UpcaseTableSize;
  10441. BOOLEAN Terminating = FALSE;
  10442. BOOLEAN TerminateOnNext = FALSE;
  10443. PAGED_CODE();
  10444. DebugTrace( +1, Dbg, ("NtfsLookupExternalAttribute\n") );
  10445. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10446. DebugTrace( 0, Dbg, ("QueriedTypeCode = %08lx\n", QueriedTypeCode) );
  10447. DebugTrace( 0, Dbg, ("QueriedName = %08lx\n", QueriedName) );
  10448. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  10449. DebugTrace( 0, Dbg, ("QueriedValue = %08lx\n", QueriedValue) );
  10450. DebugTrace( 0, Dbg, ("QueriedValueLength = %08lx\n", QueriedValueLength) );
  10451. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10452. //
  10453. // Check that our list is kosher.
  10454. //
  10455. if ((Context->AttributeList.Entry >= Context->AttributeList.BeyondFinalEntry) &&
  10456. !Context->FoundAttribute.AttributeDeleted) {
  10457. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10458. }
  10459. //
  10460. // Is this the initial enumeration? If so start at the beginning.
  10461. //
  10462. LastEntry = NULL;
  10463. if (Context->FoundAttribute.Bcb == NULL) {
  10464. Entry = Context->AttributeList.Entry;
  10465. //
  10466. // Else set Entry and LastEntry appropriately.
  10467. //
  10468. } else if (!Context->FoundAttribute.AttributeDeleted) {
  10469. LastEntry = Context->AttributeList.Entry;
  10470. Entry = NtfsGetNextRecord( LastEntry );
  10471. } else {
  10472. Entry = Context->AttributeList.Entry;
  10473. Context->FoundAttribute.AttributeDeleted = FALSE;
  10474. //
  10475. // If we are beyond the attribute list, we return false. This will
  10476. // happen in the case where have removed an attribute record and
  10477. // there are no entries left in the attribute list.
  10478. //
  10479. if (Context->AttributeList.Entry >= Context->AttributeList.BeyondFinalEntry) {
  10480. //
  10481. // In case the caller is doing an insert, we will position him at the end
  10482. // of the first file record, an always try to insert new attributes there.
  10483. //
  10484. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10485. if (QueriedTypeCode != $UNUSED) {
  10486. NtfsReadFileRecord( IrpContext,
  10487. Fcb->Vcb,
  10488. &Fcb->FileReference,
  10489. &Context->FoundAttribute.Bcb,
  10490. &Context->FoundAttribute.FileRecord,
  10491. &Context->FoundAttribute.Attribute,
  10492. &Context->FoundAttribute.MftFileOffset );
  10493. //
  10494. // If returning FALSE, then take the time to really find the
  10495. // correct position in the file record for a subsequent insert.
  10496. //
  10497. NtfsFindInFileRecord( IrpContext,
  10498. Context->FoundAttribute.Attribute,
  10499. &Context->FoundAttribute.Attribute,
  10500. QueriedTypeCode,
  10501. QueriedName,
  10502. IgnoreCase,
  10503. QueriedValue,
  10504. QueriedValueLength );
  10505. }
  10506. DebugTrace( -1, Dbg, ("NtfsLookupExternalAttribute -> FALSE\n") );
  10507. return FALSE;
  10508. }
  10509. }
  10510. //
  10511. // Now walk through the entries looking for an atttribute.
  10512. //
  10513. while (TRUE) {
  10514. PATTRIBUTE_RECORD_HEADER Attribute;
  10515. UNICODE_STRING EntryName;
  10516. UNICODE_STRING AttributeName;
  10517. PATTRIBUTE_LIST_ENTRY NextEntry;
  10518. BOOLEAN CorrespondingAttributeFound;
  10519. //
  10520. // Check to see if we are now pointing beyond the final entry
  10521. // and if so fall in to the loop to terminate pointing just
  10522. // after the last entry.
  10523. //
  10524. if (Entry >= Context->AttributeList.BeyondFinalEntry) {
  10525. Terminating = TRUE;
  10526. TerminateOnNext = TRUE;
  10527. Entry = Context->AttributeList.Entry;
  10528. } else {
  10529. NtfsCheckRecordBound( Entry,
  10530. Context->AttributeList.FirstEntry,
  10531. PtrOffset( Context->AttributeList.FirstEntry,
  10532. Context->AttributeList.BeyondFinalEntry ));
  10533. if (Entry->RecordLength == 0) {
  10534. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10535. }
  10536. NextEntry = NtfsGetNextRecord( Entry );
  10537. }
  10538. Context->AttributeList.Entry = Entry;
  10539. //
  10540. // Compare the type codes. The external attribute entry list is
  10541. // ordered by type code, so if the queried type code is less than
  10542. // the entry type code we continue the while(), if it is
  10543. // greater than we break out of the while() and return failure.
  10544. // If equal, we move on to compare names.
  10545. //
  10546. if ((QueriedTypeCode != $UNUSED) &&
  10547. !Terminating &&
  10548. (QueriedTypeCode != Entry->AttributeTypeCode)) {
  10549. if (QueriedTypeCode > Entry->AttributeTypeCode) {
  10550. Entry = NextEntry;
  10551. continue;
  10552. //
  10553. // Set up to terminate on seeing a higher type code.
  10554. //
  10555. } else {
  10556. Terminating = TRUE;
  10557. }
  10558. }
  10559. //
  10560. // At this point we are OK by TypeCode, compare names.
  10561. //
  10562. EntryName.Length = EntryName.MaximumLength = Entry->AttributeNameLength * sizeof( WCHAR );
  10563. EntryName.Buffer = Add2Ptr( Entry, Entry->AttributeNameOffset );
  10564. if (ARGUMENT_PRESENT( QueriedName ) && !Terminating) {
  10565. FSRTL_COMPARISON_RESULT Result;
  10566. //
  10567. // See if we have a name match.
  10568. //
  10569. if (!NtfsAreNamesEqual( UpcaseTable,
  10570. &EntryName,
  10571. QueriedName,
  10572. IgnoreCase )) {
  10573. //
  10574. // Compare the names ignoring case.
  10575. //
  10576. Result = NtfsCollateNames( UpcaseTable,
  10577. UpcaseTableSize,
  10578. QueriedName,
  10579. &EntryName,
  10580. GreaterThan,
  10581. TRUE);
  10582. //
  10583. // Break out if the result is LessThan, or if the result
  10584. // is Equal to *and* the exact case compare yields LessThan.
  10585. //
  10586. if ((Result == LessThan) || ((Result == EqualTo) &&
  10587. (NtfsCollateNames( UpcaseTable,
  10588. UpcaseTableSize,
  10589. QueriedName,
  10590. &EntryName,
  10591. GreaterThan,
  10592. FALSE) == LessThan))) {
  10593. Terminating = TRUE;
  10594. } else {
  10595. Entry = NextEntry;
  10596. continue;
  10597. }
  10598. }
  10599. }
  10600. //
  10601. // Now search for the right Vcn range, if specified. If we were passed a
  10602. // Vcn then look for the matching range in the current attribute. In some
  10603. // cases we may be looking for the lowest range in the following complete
  10604. // attribute. In those cases skip forward.
  10605. //
  10606. if (ARGUMENT_PRESENT( Vcn ) && !Terminating) {
  10607. //
  10608. // Skip to the next attribute record under the following conditions.
  10609. //
  10610. // 1 - We are already past the Vcn point we are looking for in the current
  10611. // attribute. Typically this happens when the caller is looking for
  10612. // the first attribute record for each of the attributes in the file.
  10613. //
  10614. // 2 - The desired Vcn for the current attribute falls in one of the
  10615. // subsequent attribute records.
  10616. //
  10617. if ((Entry->LowestVcn > *Vcn) ||
  10618. ((NextEntry < Context->AttributeList.BeyondFinalEntry) &&
  10619. (NextEntry->LowestVcn <= *Vcn) &&
  10620. (NextEntry->AttributeTypeCode == Entry->AttributeTypeCode) &&
  10621. (NextEntry->AttributeNameLength == Entry->AttributeNameLength) &&
  10622. (RtlEqualMemory( Add2Ptr( NextEntry, NextEntry->AttributeNameOffset ),
  10623. Add2Ptr( Entry, Entry->AttributeNameOffset ),
  10624. Entry->AttributeNameLength * sizeof( WCHAR ))))) {
  10625. Entry = NextEntry;
  10626. continue;
  10627. }
  10628. }
  10629. //
  10630. // Now we are also OK by name and Vcn, so now go find the attribute and
  10631. // compare against value, if specified.
  10632. //
  10633. if ((LastEntry == NULL) ||
  10634. !NtfsEqualMftRef( &LastEntry->SegmentReference, &Entry->SegmentReference )) {
  10635. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10636. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10637. NtfsReadFileRecord( IrpContext,
  10638. Fcb->Vcb,
  10639. &Entry->SegmentReference,
  10640. &Context->FoundAttribute.Bcb,
  10641. &FileRecord,
  10642. &Attribute,
  10643. &Context->FoundAttribute.MftFileOffset );
  10644. Context->FoundAttribute.FileRecord = FileRecord;
  10645. //
  10646. // If we already have the right record pinned, reload this pointer.
  10647. //
  10648. } else {
  10649. Attribute = NtfsFirstAttribute( Context->FoundAttribute.FileRecord );
  10650. }
  10651. //
  10652. // Now quickly loop through looking for the correct attribute
  10653. // instance.
  10654. //
  10655. CorrespondingAttributeFound = FALSE;
  10656. while (TRUE) {
  10657. //
  10658. // Check that we can safely access this attribute.
  10659. //
  10660. NtfsCheckRecordBound( Attribute,
  10661. Context->FoundAttribute.FileRecord,
  10662. Fcb->Vcb->BytesPerFileRecordSegment );
  10663. //
  10664. // Exit the loop if we have reached the $END record.
  10665. //
  10666. if (Attribute->TypeCode == $END) {
  10667. break;
  10668. }
  10669. //
  10670. // Check that the attribute has a non-zero length.
  10671. //
  10672. if (Attribute->RecordLength == 0) {
  10673. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10674. }
  10675. if (Entry->Instance == Attribute->Instance) {
  10676. //
  10677. // Well, the attribute list saved us from having to compare
  10678. // type code and name as we went through this file record,
  10679. // however now that we have found our attribute by its
  10680. // instance number, we will do a quick check to see that
  10681. // we got the right one. Else the file is corrupt.
  10682. //
  10683. if (Entry->AttributeTypeCode != Attribute->TypeCode) {
  10684. break;
  10685. }
  10686. if (ARGUMENT_PRESENT( QueriedName )) {
  10687. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  10688. if (!NtfsAreNamesEqual( UpcaseTable, &AttributeName, &EntryName, FALSE )) {
  10689. break;
  10690. }
  10691. }
  10692. //
  10693. // Show that we correctly found the attribute described in
  10694. // the attribute list.
  10695. //
  10696. CorrespondingAttributeFound = TRUE;
  10697. Context->FoundAttribute.Attribute = Attribute;
  10698. //
  10699. // Now we may just be here because we are terminating the
  10700. // scan on seeing the end, a higher attribute code, or a
  10701. // higher name. If so, return FALSE here.
  10702. //
  10703. if (Terminating) {
  10704. //
  10705. // If we hit the end of the attribute list, then we
  10706. // are supposed to terminate after advancing the
  10707. // attribute list entry.
  10708. //
  10709. if (TerminateOnNext) {
  10710. Context->AttributeList.Entry = NtfsGetNextRecord(Entry);
  10711. }
  10712. //
  10713. // In case the caller is doing an insert, we will position him at the end
  10714. // of the first file record, an always try to insert new attributes there.
  10715. //
  10716. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10717. if (QueriedTypeCode != $UNUSED) {
  10718. NtfsReadFileRecord( IrpContext,
  10719. Fcb->Vcb,
  10720. &Fcb->FileReference,
  10721. &Context->FoundAttribute.Bcb,
  10722. &Context->FoundAttribute.FileRecord,
  10723. &Context->FoundAttribute.Attribute,
  10724. &Context->FoundAttribute.MftFileOffset );
  10725. //
  10726. // If returning FALSE, then take the time to really find the
  10727. // correct position in the file record for a subsequent insert.
  10728. //
  10729. NtfsFindInFileRecord( IrpContext,
  10730. Context->FoundAttribute.Attribute,
  10731. &Context->FoundAttribute.Attribute,
  10732. QueriedTypeCode,
  10733. QueriedName,
  10734. IgnoreCase,
  10735. QueriedValue,
  10736. QueriedValueLength );
  10737. }
  10738. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10739. Attribute) );
  10740. DebugTrace( -1, Dbg, ("NtfsLookupExternalAttribute -> FALSE\n") );
  10741. return FALSE;
  10742. }
  10743. //
  10744. // Now compare the value, if so queried.
  10745. //
  10746. if (!ARGUMENT_PRESENT( QueriedValue ) ||
  10747. NtfsEqualAttributeValue( Attribute,
  10748. QueriedValue,
  10749. QueriedValueLength ) ) {
  10750. //
  10751. // It matches. Return it in the enumeration context.
  10752. //
  10753. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10754. Attribute ));
  10755. DebugTrace( -1, Dbg, ("NtfsLookupExternalAttribute -> TRUE\n") );
  10756. //
  10757. // Do basic attribute consistency check
  10758. //
  10759. if ((NtfsIsAttributeResident( Attribute )) &&
  10760. (Attribute->Form.Resident.ValueOffset + Attribute->Form.Resident.ValueLength > Attribute->RecordLength)) {
  10761. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10762. }
  10763. return TRUE;
  10764. }
  10765. }
  10766. //
  10767. // Get the next attribute, and continue.
  10768. //
  10769. Attribute = NtfsGetNextRecord( Attribute );
  10770. }
  10771. //
  10772. // Did we even find the attribute corresponding to the entry?
  10773. // If not, something is messed up. Raise file corrupt error.
  10774. //
  10775. if (!CorrespondingAttributeFound) {
  10776. //
  10777. // For the moment, ASSERT this falsehood so that we may have
  10778. // a chance to peek before raising.
  10779. //
  10780. ASSERT( CorrespondingAttributeFound );
  10781. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10782. }
  10783. Entry = NtfsGetNextRecord( Entry );
  10784. }
  10785. }
  10786. //
  10787. // Internal support routine
  10788. //
  10789. BOOLEAN
  10790. NtfsGetSpaceForAttribute (
  10791. IN PIRP_CONTEXT IrpContext,
  10792. IN PFCB Fcb,
  10793. IN ULONG Length,
  10794. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10795. )
  10796. /*++
  10797. Routine Description:
  10798. This routine gets space for a new attribute record at the position indicated
  10799. in the Context structure. As required, it will move attributes around,
  10800. allocate an additional record in the Mft, or convert some other existing
  10801. attribute to nonresident form. The caller should already have checked if
  10802. the new attribute he is inserting should be stored resident or nonresident.
  10803. On return, it is invalid to continue to use any previously-retrieved pointers,
  10804. Bcbs, or other position-dependent information retrieved from the Context
  10805. structure, as any of these values are liable to change. The file record in
  10806. which the space has been found will already be pinned.
  10807. Note, this routine DOES NOT actually make space for the attribute, it only
  10808. verifies that sufficient space is there. The caller may call
  10809. NtfsRestartInsertAttribute to actually insert the attribute in place.
  10810. Arguments:
  10811. Fcb - Requested file.
  10812. Length - Quad-aligned length required in bytes.
  10813. Context - Describes the position for the new attribute, as returned from
  10814. the enumeration which failed to find an existing occurrence of
  10815. the attribute. This pointer will either be pointing to some
  10816. other attribute in the record, or to the first free quad-aligned
  10817. byte if the new attribute is to go at the end.
  10818. Return Value:
  10819. FALSE - if a major move was necessary, and the caller should look up
  10820. its desired position again and call back.
  10821. TRUE - if the space was created
  10822. --*/
  10823. {
  10824. PATTRIBUTE_RECORD_HEADER NextAttribute;
  10825. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10826. PAGED_CODE();
  10827. DebugTrace( +1, Dbg, ("NtfsGetSpaceForAttribute\n") );
  10828. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10829. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  10830. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10831. ASSERT( IsQuadAligned( Length ) );
  10832. NextAttribute = NtfsFoundAttribute( Context );
  10833. FileRecord = NtfsContainingFileRecord( Context );
  10834. //
  10835. // Make sure the buffer is pinned.
  10836. //
  10837. NtfsPinMappedAttribute( IrpContext, Fcb->Vcb, Context );
  10838. //
  10839. // If the space is not there now, then make room and return with FALSE
  10840. //
  10841. if ((FileRecord->BytesAvailable - FileRecord->FirstFreeByte) < Length ) {
  10842. MakeRoomForAttribute( IrpContext, Fcb, Length, Context );
  10843. DebugTrace( -1, Dbg, ("NtfsGetSpaceForAttribute -> FALSE\n") );
  10844. return FALSE;
  10845. }
  10846. DebugTrace( -1, Dbg, ("NtfsGetSpaceForAttribute -> TRUE\n") );
  10847. return TRUE;
  10848. }
  10849. //
  10850. // Internal support routine
  10851. //
  10852. BOOLEAN
  10853. NtfsChangeAttributeSize (
  10854. IN PIRP_CONTEXT IrpContext,
  10855. IN PFCB Fcb,
  10856. IN ULONG Length,
  10857. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10858. )
  10859. /*++
  10860. Routine Description:
  10861. This routine adjustss the space occupied by the current attribute record
  10862. in the Context structure. As required, it will move attributes around,
  10863. allocate an additional record in the Mft, or convert some other existing
  10864. attribute to nonresident form. The caller should already have checked if
  10865. the current attribute he is inserting should rather be converted to
  10866. nonresident.
  10867. When done, this routine has updated any file records whose allocation was
  10868. changed, and also the RecordLength field in the adjusted attribute. No
  10869. other attribute fields are updated.
  10870. On return, it is invalid to continue to use any previously-retrieved pointers,
  10871. Bcbs, or other position-dependent information retrieved from the Context
  10872. structure, as any of these values are liable to change. The file record in
  10873. which the space has been found will already be pinned.
  10874. Arguments:
  10875. Fcb - Requested file.
  10876. Length - New quad-aligned length of attribute record in bytes
  10877. Context - Describes the current attribute.
  10878. Return Value:
  10879. FALSE - if a major move was necessary, and the caller should look up
  10880. its desired position again and call back.
  10881. TRUE - if the space was created
  10882. --*/
  10883. {
  10884. PATTRIBUTE_RECORD_HEADER Attribute;
  10885. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10886. LONG SizeChange;
  10887. PAGED_CODE();
  10888. DebugTrace( +1, Dbg, ("NtfsChangeAttributeSize\n") );
  10889. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10890. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  10891. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10892. ASSERT( IsQuadAligned( Length ) );
  10893. Attribute = NtfsFoundAttribute( Context );
  10894. FileRecord = NtfsContainingFileRecord( Context );
  10895. //
  10896. // Make sure the buffer is pinned.
  10897. //
  10898. NtfsPinMappedAttribute( IrpContext, Fcb->Vcb, Context );
  10899. //
  10900. // Calculate the change in attribute record size.
  10901. //
  10902. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  10903. SizeChange = Length - Attribute->RecordLength;
  10904. //
  10905. // If there is not currently enough space, then we have to make room
  10906. // and return FALSE to our caller.
  10907. //
  10908. if ( (LONG)(FileRecord->BytesAvailable - FileRecord->FirstFreeByte) < SizeChange ) {
  10909. MakeRoomForAttribute( IrpContext, Fcb, SizeChange, Context );
  10910. DebugTrace( -1, Dbg, ("NtfsChangeAttributeSize -> FALSE\n") );
  10911. return FALSE;
  10912. }
  10913. DebugTrace( -1, Dbg, ("NtfsChangeAttributeSize -> TRUE\n") );
  10914. return TRUE;
  10915. }
  10916. //
  10917. // Internal support routine
  10918. //
  10919. VOID
  10920. MakeRoomForAttribute (
  10921. IN PIRP_CONTEXT IrpContext,
  10922. IN PFCB Fcb,
  10923. IN ULONG SizeNeeded,
  10924. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10925. )
  10926. /*++
  10927. Routine Description:
  10928. This routine attempts to make additional room for a new attribute or
  10929. a growing attribute in a file record. The algorithm is as follows.
  10930. First continuously loop through the record looking at the largest n
  10931. attributes, from the largest down, to see which one of these attributes
  10932. is big enough to move, and which one qualifies for one of the following
  10933. actions:
  10934. 1. For an index root attribute, the indexing package may be called
  10935. to "push" the index root, i.e., add another level to the BTree
  10936. leaving only an end index record in the root.
  10937. 2. For a resident attribute which is allowed to be made nonresident,
  10938. the attribute is made nonresident, leaving only run information
  10939. in the root.
  10940. 3. If the attribute is already nonresident, then it can be moved to
  10941. a separate file record.
  10942. If none of the above operations can be performed, or not enough free space
  10943. is recovered, then as a last resort the file record is split in two. This
  10944. would typically indicate that the file record is populated with a large
  10945. number of small attributes.
  10946. The first time step 3 above or a split of the file record occurs, the
  10947. attribute list must be created for the file.
  10948. Arguments:
  10949. Fcb - Requested file.
  10950. SizeNeeded - Supplies the total amount of free space needed, in bytes.
  10951. Context - Describes the insertion point for the attribute which does
  10952. not fit. NOTE -- This context is not valid on return.
  10953. Return Value:
  10954. None
  10955. --*/
  10956. {
  10957. PATTRIBUTE_RECORD_HEADER LargestAttributes[MAX_MOVEABLE_ATTRIBUTES];
  10958. PATTRIBUTE_RECORD_HEADER Attribute;
  10959. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10960. ULONG i;
  10961. PVCB Vcb = Fcb->Vcb;
  10962. PAGED_CODE();
  10963. //
  10964. // Here is the current threshhold at which a move of an attribute will
  10965. // be considered.
  10966. //
  10967. FileRecord = NtfsContainingFileRecord( Context );
  10968. //
  10969. // Find the largest attributes for this file record.
  10970. //
  10971. FindLargestAttributes( FileRecord, MAX_MOVEABLE_ATTRIBUTES, LargestAttributes );
  10972. //
  10973. // Now loop from largest to smallest of the largest attributes,
  10974. // and see if there is something we can do.
  10975. //
  10976. for (i = 0; i < MAX_MOVEABLE_ATTRIBUTES; i += 1) {
  10977. Attribute = LargestAttributes[i];
  10978. //
  10979. // Look to the next attribute if there is no attribute at this array
  10980. // position.
  10981. //
  10982. if (Attribute == NULL) {
  10983. continue;
  10984. //
  10985. // If this is the Mft then any attribute that is 'BigEnoughToMove'
  10986. // except $DATA attributes outside the base file record.
  10987. // We need to keep those where they are in order to enforce the
  10988. // boot-strap mapping.
  10989. //
  10990. } else if (Fcb == Vcb->MftScb->Fcb) {
  10991. if (Attribute->TypeCode == $DATA &&
  10992. ((*(PLONGLONG) &FileRecord->BaseFileRecordSegment != 0) ||
  10993. (Attribute->RecordLength < Vcb->BigEnoughToMove))) {
  10994. continue;
  10995. }
  10996. //
  10997. // Any attribute in a non-Mft file which is 'BigEnoughToMove' can
  10998. // be considered. We also accept an $ATTRIBUTE_LIST attribute
  10999. // in a non-Mft file which must go non-resident in order for
  11000. // the attribute name to fit. Otherwise we could be trying to
  11001. // add an attribute with a large name into the base file record.
  11002. // We will need space to store the name twice, once for the
  11003. // attribute list entry and once in the attribute. This can take
  11004. // up 1024 bytes by itself. We want to force the attribute list
  11005. // non-resident first so that the new attribute will fit. We
  11006. // look at whether the attribute list followed by just the new data
  11007. // will fit in the file record.
  11008. //
  11009. } else if (Attribute->RecordLength < Vcb->BigEnoughToMove) {
  11010. if ((Attribute->TypeCode != $ATTRIBUTE_LIST) ||
  11011. ((PtrOffset( FileRecord, Attribute ) + Attribute->RecordLength + SizeNeeded + sizeof( LONGLONG)) <= FileRecord->BytesAvailable)) {
  11012. continue;
  11013. }
  11014. }
  11015. //
  11016. // If this attribute is an index root, then we can just call the
  11017. // indexing support to allocate a new index buffer and push the
  11018. // current resident contents down.
  11019. //
  11020. if (Attribute->TypeCode == $INDEX_ROOT) {
  11021. PSCB IndexScb;
  11022. UNICODE_STRING IndexName;
  11023. //
  11024. // Don't push the root now if we previously deferred pushing the root.
  11025. // Set the IrpContext flag to indicate we should do the push
  11026. // and raise CANT_WAIT.
  11027. //
  11028. if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_PUSH )) {
  11029. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_FORCE_PUSH );
  11030. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  11031. }
  11032. IndexName.Length =
  11033. IndexName.MaximumLength = (USHORT)Attribute->NameLength << 1;
  11034. IndexName.Buffer = Add2Ptr( Attribute, Attribute->NameOffset );
  11035. IndexScb = NtfsCreateScb( IrpContext,
  11036. Fcb,
  11037. $INDEX_ALLOCATION,
  11038. &IndexName,
  11039. FALSE,
  11040. NULL );
  11041. NtfsPushIndexRoot( IrpContext, IndexScb );
  11042. return;
  11043. //
  11044. // Otherwise, if this is a resident attribute which can go nonresident,
  11045. // then make it nonresident now.
  11046. //
  11047. } else if ((Attribute->FormCode == RESIDENT_FORM) &&
  11048. !FlagOn(NtfsGetAttributeDefinition(Vcb,
  11049. Attribute->TypeCode)->Flags,
  11050. ATTRIBUTE_DEF_MUST_BE_RESIDENT)) {
  11051. NtfsConvertToNonresident( IrpContext, Fcb, Attribute, FALSE, NULL );
  11052. return;
  11053. //
  11054. // Finally, if the attribute is nonresident already, move it to its
  11055. // own record unless it is an attribute list.
  11056. //
  11057. } else if ((Attribute->FormCode == NONRESIDENT_FORM)
  11058. && (Attribute->TypeCode != $ATTRIBUTE_LIST)) {
  11059. LONGLONG MftFileOffset;
  11060. MftFileOffset = Context->FoundAttribute.MftFileOffset;
  11061. MoveAttributeToOwnRecord( IrpContext,
  11062. Fcb,
  11063. Attribute,
  11064. Context,
  11065. NULL,
  11066. NULL );
  11067. return;
  11068. }
  11069. }
  11070. //
  11071. // If we get here, it is because we failed to find enough space above.
  11072. // Our last resort is to split into two file records, and this has
  11073. // to work. We should never reach this point for the Mft.
  11074. //
  11075. if (Fcb == Vcb->MftScb->Fcb) {
  11076. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  11077. }
  11078. SplitFileRecord( IrpContext, Fcb, SizeNeeded, Context );
  11079. }
  11080. //
  11081. // Internal support routine
  11082. //
  11083. VOID
  11084. FindLargestAttributes (
  11085. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  11086. IN ULONG Number,
  11087. OUT PATTRIBUTE_RECORD_HEADER *AttributeArray
  11088. )
  11089. /*++
  11090. Routine Description:
  11091. This routine returns the n largest attributes from a file record in an
  11092. array, ordered from largest to smallest.
  11093. Arguments:
  11094. FileRecord - Supplies file record to scan for largest attributes.
  11095. Number - Supplies the number of entries in the array.
  11096. AttributeArray - Supplies the array which is to receive pointers to the
  11097. largest attributes. This array must be zeroed prior
  11098. to calling this routine.
  11099. Return Value:
  11100. None
  11101. --*/
  11102. {
  11103. ULONG i, j;
  11104. PATTRIBUTE_RECORD_HEADER Attribute;
  11105. PAGED_CODE();
  11106. RtlZeroMemory( AttributeArray, Number * sizeof(PATTRIBUTE_RECORD_HEADER) );
  11107. Attribute = Add2Ptr( FileRecord, FileRecord->FirstAttributeOffset );
  11108. while (Attribute->TypeCode != $END) {
  11109. for (i = 0; i < Number; i++) {
  11110. if ((AttributeArray[i] == NULL)
  11111. ||
  11112. (AttributeArray[i]->RecordLength < Attribute->RecordLength)) {
  11113. for (j = Number - 1; j != i; j--) {
  11114. AttributeArray[j] = AttributeArray[j-1];
  11115. }
  11116. AttributeArray[i] = Attribute;
  11117. break;
  11118. }
  11119. }
  11120. Attribute = Add2Ptr( Attribute, Attribute->RecordLength );
  11121. }
  11122. }
  11123. //
  11124. // Internal support routine
  11125. //
  11126. LONGLONG
  11127. MoveAttributeToOwnRecord (
  11128. IN PIRP_CONTEXT IrpContext,
  11129. IN PFCB Fcb,
  11130. IN PATTRIBUTE_RECORD_HEADER Attribute,
  11131. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context,
  11132. OUT PBCB *NewBcb OPTIONAL,
  11133. OUT PFILE_RECORD_SEGMENT_HEADER *NewFileRecord OPTIONAL
  11134. )
  11135. /*++
  11136. Routine Description:
  11137. This routine may be called to move a particular attribute to a separate
  11138. file record. If the file does not already have an attribute list, then
  11139. one is created (else it is updated).
  11140. Arguments:
  11141. Fcb - Requested file.
  11142. Attribute - Supplies a pointer to the attribute which is to be moved.
  11143. Context - Supplies a pointer to a context which was used to look up
  11144. another attribute in the same file record. If this is an Mft
  11145. $DATA split we will point to the part that was split out of the
  11146. first file record on return. The call from NtfsAddAttributeAllocation
  11147. depends on this.
  11148. NewBcb - If supplied, returns the Bcb address for the file record
  11149. that the attribute was moved to. NewBcb and NewFileRecord must
  11150. either both be specified or neither specified.
  11151. NewFileRecord - If supplied, returns a pointer to the file record
  11152. that the attribute was moved to. The caller may assume
  11153. that the moved attribute is the first one in the file
  11154. record. NewBcb and NewFileRecord must either both be
  11155. specified or neither specified.
  11156. Return Value:
  11157. LONGLONG - Segment reference number of new record without a sequence number.
  11158. --*/
  11159. {
  11160. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  11161. ATTRIBUTE_ENUMERATION_CONTEXT MoveContext;
  11162. PFILE_RECORD_SEGMENT_HEADER FileRecord1, FileRecord2;
  11163. PATTRIBUTE_RECORD_HEADER Attribute2;
  11164. BOOLEAN FoundListContext;
  11165. MFT_SEGMENT_REFERENCE Reference2;
  11166. LONGLONG MftRecordNumber2;
  11167. WCHAR NameBuffer[8];
  11168. UNICODE_STRING AttributeName;
  11169. ATTRIBUTE_TYPE_CODE AttributeTypeCode;
  11170. VCN LowestVcn;
  11171. BOOLEAN IsNonresident = FALSE;
  11172. PBCB Bcb = NULL;
  11173. PATTRIBUTE_TYPE_CODE NewEnd;
  11174. PVCB Vcb = Fcb->Vcb;
  11175. ULONG NewListSize = 0;
  11176. BOOLEAN MftData = FALSE;
  11177. PATTRIBUTE_RECORD_HEADER OldPosition = NULL;
  11178. PAGED_CODE();
  11179. //
  11180. // Make sure the attribute is pinned.
  11181. //
  11182. NtfsPinMappedAttribute( IrpContext,
  11183. Vcb,
  11184. Context );
  11185. //
  11186. // See if we are being asked to move the Mft Data.
  11187. //
  11188. if ((Fcb == Vcb->MftScb->Fcb) && (Attribute->TypeCode == $DATA)) {
  11189. MftData = TRUE;
  11190. }
  11191. NtfsInitializeAttributeContext( &ListContext );
  11192. NtfsInitializeAttributeContext( &MoveContext );
  11193. FileRecord1 = NtfsContainingFileRecord(Context);
  11194. //
  11195. // Save a description of the attribute to help us look it up
  11196. // again, and to make clones if necessary.
  11197. //
  11198. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  11199. AttributeTypeCode = Attribute->TypeCode;
  11200. AttributeName.Length =
  11201. AttributeName.MaximumLength = (USHORT)Attribute->NameLength << 1;
  11202. AttributeName.Buffer = NameBuffer;
  11203. if (AttributeName.Length > sizeof(NameBuffer)) {
  11204. AttributeName.Buffer = NtfsAllocatePool( NonPagedPool, AttributeName.Length );
  11205. }
  11206. RtlCopyMemory( AttributeName.Buffer,
  11207. Add2Ptr(Attribute, Attribute->NameOffset),
  11208. AttributeName.Length );
  11209. if (Attribute->FormCode == NONRESIDENT_FORM) {
  11210. IsNonresident = TRUE;
  11211. LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  11212. }
  11213. try {
  11214. //
  11215. // Lookup the list context so that we know where it is at.
  11216. //
  11217. FoundListContext =
  11218. NtfsLookupAttributeByCode( IrpContext,
  11219. Fcb,
  11220. &Fcb->FileReference,
  11221. $ATTRIBUTE_LIST,
  11222. &ListContext );
  11223. //
  11224. // If we do not already have an attribute list, then calculate
  11225. // how big it must be. Note, there must only be one file record
  11226. // at this point.
  11227. //
  11228. if (!FoundListContext) {
  11229. ASSERT( FileRecord1 == NtfsContainingFileRecord(&ListContext) );
  11230. NewListSize = GetSizeForAttributeList( FileRecord1 );
  11231. //
  11232. // Now if the attribute list already exists, we have to look up
  11233. // the first one we are going to move in order to update the
  11234. // attribute list later.
  11235. //
  11236. } else {
  11237. if (!NtfsLookupAttributeByName( IrpContext,
  11238. Fcb,
  11239. &Fcb->FileReference,
  11240. Attribute->TypeCode,
  11241. &AttributeName,
  11242. IsNonresident ?
  11243. &LowestVcn :
  11244. NULL,
  11245. FALSE,
  11246. &MoveContext )) {
  11247. ASSERT( FALSE );
  11248. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11249. }
  11250. ASSERT(Attribute == NtfsFoundAttribute(&MoveContext));
  11251. }
  11252. //
  11253. // Allocate a new file record and move the attribute over.
  11254. //
  11255. FileRecord2 = NtfsCloneFileRecord( IrpContext, Fcb, MftData, &Bcb, &Reference2 );
  11256. //
  11257. // Remember the file record number for the new file record.
  11258. //
  11259. MftRecordNumber2 = NtfsFullSegmentNumber( &Reference2 );
  11260. Attribute2 = Add2Ptr( FileRecord2, FileRecord2->FirstAttributeOffset );
  11261. RtlCopyMemory( Attribute2, Attribute, (ULONG)Attribute->RecordLength );
  11262. Attribute2->Instance = FileRecord2->NextAttributeInstance++;
  11263. NewEnd = Add2Ptr( Attribute2, Attribute2->RecordLength );
  11264. *NewEnd = $END;
  11265. FileRecord2->FirstFreeByte = PtrOffset(FileRecord2, NewEnd)
  11266. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  11267. //
  11268. // If this is the Mft Data attribute, we cannot really move it, we
  11269. // have to move all but the first part of it.
  11270. //
  11271. if (MftData) {
  11272. PCHAR MappingPairs;
  11273. ULONG NewSize;
  11274. VCN OriginalLastVcn;
  11275. VCN LastVcn;
  11276. LONGLONG SavedFileSize = Attribute->Form.Nonresident.FileSize;
  11277. LONGLONG SavedValidDataLength = Attribute->Form.Nonresident.ValidDataLength;
  11278. PNTFS_MCB Mcb = &Vcb->MftScb->Mcb;
  11279. NtfsCleanupAttributeContext( IrpContext, Context );
  11280. NtfsInitializeAttributeContext( Context );
  11281. if (!NtfsLookupAttributeByCode( IrpContext,
  11282. Fcb,
  11283. &Fcb->FileReference,
  11284. $DATA,
  11285. Context )) {
  11286. ASSERT( FALSE );
  11287. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11288. }
  11289. //
  11290. // Calculate the number of clusters in the Mft up to (possibly past) the
  11291. // first user file record, and decrement to get LastVcn to stay in first
  11292. // file record.
  11293. //
  11294. LastVcn = LlClustersFromBytes( Vcb,
  11295. FIRST_USER_FILE_NUMBER *
  11296. Vcb->BytesPerFileRecordSegment ) - 1;
  11297. OriginalLastVcn = Attribute->Form.Nonresident.HighestVcn;
  11298. //
  11299. // Now truncate the first Mft record.
  11300. //
  11301. NtfsDeleteAttributeAllocation( IrpContext,
  11302. Vcb->MftScb,
  11303. TRUE,
  11304. &LastVcn,
  11305. Context,
  11306. FALSE );
  11307. //
  11308. // Now get the first Lcn for the new file record.
  11309. //
  11310. LastVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  11311. Attribute2->Form.Nonresident.LowestVcn = LastVcn;
  11312. //
  11313. // Calculate the size of the attribute record we will need.
  11314. // We only create mapping pairs through the highest Vcn on the
  11315. // disk. We don't include any that are being added through the
  11316. // Mcb yet.
  11317. //
  11318. NewSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER
  11319. + QuadAlign( AttributeName.Length )
  11320. + QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  11321. MAXULONG,
  11322. LastVcn,
  11323. &OriginalLastVcn,
  11324. &LastVcn ));
  11325. Attribute2->RecordLength = NewSize;
  11326. //
  11327. // Assume no attribute name, and calculate where the Mapping Pairs
  11328. // will go. (Update below if we are wrong.)
  11329. //
  11330. MappingPairs = (PCHAR)Attribute2 + SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  11331. //
  11332. // If the attribute has a name, take care of that now.
  11333. //
  11334. if (AttributeName.Length != 0) {
  11335. Attribute2->NameLength = (UCHAR)(AttributeName.Length / sizeof(WCHAR));
  11336. Attribute2->NameOffset = (USHORT)PtrOffset(Attribute2, MappingPairs);
  11337. RtlCopyMemory( MappingPairs,
  11338. AttributeName.Buffer,
  11339. AttributeName.Length );
  11340. MappingPairs += QuadAlign( AttributeName.Length );
  11341. }
  11342. //
  11343. // We always need the mapping pairs offset.
  11344. //
  11345. Attribute2->Form.Nonresident.MappingPairsOffset =
  11346. (USHORT)PtrOffset(Attribute2, MappingPairs);
  11347. NewEnd = Add2Ptr( Attribute2, Attribute2->RecordLength );
  11348. *NewEnd = $END;
  11349. FileRecord2->FirstFreeByte = PtrOffset(FileRecord2, NewEnd)
  11350. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  11351. //
  11352. // Now add the space in the file record.
  11353. //
  11354. *MappingPairs = 0;
  11355. NtfsBuildMappingPairs( Mcb,
  11356. Attribute2->Form.Nonresident.LowestVcn,
  11357. &LastVcn,
  11358. MappingPairs );
  11359. Attribute2->Form.Nonresident.HighestVcn = LastVcn;
  11360. } else {
  11361. //
  11362. // Now log these changes and fix up the first file record.
  11363. //
  11364. FileRecord1->Lsn =
  11365. NtfsWriteLog( IrpContext,
  11366. Vcb->MftScb,
  11367. NtfsFoundBcb(Context),
  11368. DeleteAttribute,
  11369. NULL,
  11370. 0,
  11371. CreateAttribute,
  11372. Attribute,
  11373. Attribute->RecordLength,
  11374. NtfsMftOffset( Context ),
  11375. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord1),
  11376. 0,
  11377. Vcb->BytesPerFileRecordSegment );
  11378. //
  11379. // Remember the old position for the CreateAttributeList
  11380. //
  11381. OldPosition = Attribute;
  11382. NtfsRestartRemoveAttribute( IrpContext,
  11383. FileRecord1,
  11384. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord1) );
  11385. }
  11386. FileRecord2->Lsn =
  11387. NtfsWriteLog( IrpContext,
  11388. Vcb->MftScb,
  11389. Bcb,
  11390. InitializeFileRecordSegment,
  11391. FileRecord2,
  11392. FileRecord2->FirstFreeByte,
  11393. Noop,
  11394. NULL,
  11395. 0,
  11396. LlBytesFromFileRecords( Vcb, MftRecordNumber2 ),
  11397. 0,
  11398. 0,
  11399. Vcb->BytesPerFileRecordSegment );
  11400. //
  11401. // Finally, create the attribute list attribute if needed.
  11402. //
  11403. if (!FoundListContext) {
  11404. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11405. NtfsInitializeAttributeContext( &ListContext );
  11406. CreateAttributeList( IrpContext,
  11407. Fcb,
  11408. FileRecord1,
  11409. MftData ? NULL : FileRecord2,
  11410. Reference2,
  11411. OldPosition,
  11412. NewListSize,
  11413. &ListContext );
  11414. //
  11415. // Otherwise we have to update the existing attribute list, but only
  11416. // if this is not the Mft data. In that case the attribute list is
  11417. // still correct since we haven't moved the attribute entirely.
  11418. //
  11419. } else if (!MftData) {
  11420. UpdateAttributeListEntry( IrpContext,
  11421. Fcb,
  11422. &MoveContext.AttributeList.Entry->SegmentReference,
  11423. MoveContext.AttributeList.Entry->Instance,
  11424. &Reference2,
  11425. Attribute2->Instance,
  11426. &ListContext );
  11427. }
  11428. NtfsCleanupAttributeContext( IrpContext, Context );
  11429. NtfsInitializeAttributeContext( Context );
  11430. if (!NtfsLookupAttributeByName( IrpContext,
  11431. Fcb,
  11432. &Fcb->FileReference,
  11433. AttributeTypeCode,
  11434. &AttributeName,
  11435. IsNonresident ? &LowestVcn : NULL,
  11436. FALSE,
  11437. Context )) {
  11438. ASSERT( FALSE );
  11439. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11440. }
  11441. ASSERT(!IsNonresident || (LowestVcn == NtfsFoundAttribute(Context)->Form.Nonresident.LowestVcn));
  11442. //
  11443. // For the case of the Mft split, we now add the final entry.
  11444. //
  11445. if (MftData) {
  11446. //
  11447. // Finally, we have to add the entry to the attribute list.
  11448. // The routine we have to do this gets most of its inputs
  11449. // out of an attribute context. Our context at this point
  11450. // does not have quite the right information, so we have to
  11451. // update it here before calling AddToAttributeList.
  11452. //
  11453. Context->FoundAttribute.FileRecord = FileRecord2;
  11454. Context->FoundAttribute.Attribute = Attribute2;
  11455. Context->AttributeList.Entry =
  11456. NtfsGetNextRecord(Context->AttributeList.Entry);
  11457. NtfsAddToAttributeList( IrpContext, Fcb, Reference2, Context );
  11458. NtfsCleanupAttributeContext( IrpContext, Context );
  11459. NtfsInitializeAttributeContext( Context );
  11460. if (!NtfsLookupAttributeByCode( IrpContext,
  11461. Fcb,
  11462. &Fcb->FileReference,
  11463. $DATA,
  11464. Context )) {
  11465. ASSERT( FALSE );
  11466. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11467. }
  11468. while (IsNonresident &&
  11469. (Attribute2->Form.Nonresident.LowestVcn !=
  11470. NtfsFoundAttribute(Context)->Form.Nonresident.LowestVcn)) {
  11471. if (!NtfsLookupNextAttributeByCode( IrpContext,
  11472. Fcb,
  11473. $DATA,
  11474. Context )) {
  11475. ASSERT( FALSE );
  11476. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11477. }
  11478. }
  11479. }
  11480. } finally {
  11481. if (AttributeName.Buffer != NameBuffer) {
  11482. NtfsFreePool(AttributeName.Buffer);
  11483. }
  11484. if (ARGUMENT_PRESENT(NewBcb)) {
  11485. ASSERT(ARGUMENT_PRESENT(NewFileRecord));
  11486. *NewBcb = Bcb;
  11487. *NewFileRecord = FileRecord2;
  11488. } else {
  11489. ASSERT(!ARGUMENT_PRESENT(NewFileRecord));
  11490. NtfsUnpinBcb( IrpContext, &Bcb );
  11491. }
  11492. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11493. NtfsCleanupAttributeContext( IrpContext, &MoveContext );
  11494. }
  11495. return MftRecordNumber2;
  11496. }
  11497. //
  11498. // Internal support routine
  11499. //
  11500. VOID
  11501. SplitFileRecord (
  11502. IN PIRP_CONTEXT IrpContext,
  11503. IN PFCB Fcb,
  11504. IN ULONG SizeNeeded,
  11505. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  11506. )
  11507. /*++
  11508. Routine Description:
  11509. This routine splits a file record in two, when it has been found that
  11510. there is no room for a new attribute. If the file does not already have
  11511. an attribute list attribute then one is created.
  11512. Essentially this routine finds the midpoint in the current file record
  11513. (accounting for a potential new attribute list and also the space needed).
  11514. Then it copies the second half of the file record over and fixes up the
  11515. first record. The attribute list is created at the end if required.
  11516. Arguments:
  11517. Fcb - Requested file.
  11518. SizeNeeded - Supplies the additional size needed, which is causing the split
  11519. to occur.
  11520. Context - Supplies the attribute enumeration context pointing to the spot
  11521. where the new attribute is to be inserted or grown.
  11522. Return Value:
  11523. None
  11524. --*/
  11525. {
  11526. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  11527. ATTRIBUTE_ENUMERATION_CONTEXT MoveContext;
  11528. PFILE_RECORD_SEGMENT_HEADER FileRecord1, FileRecord2;
  11529. PATTRIBUTE_RECORD_HEADER Attribute1, Attribute2, Attribute;
  11530. ULONG NewListOffset = 0;
  11531. ULONG NewListSize = 0;
  11532. ULONG NewAttributeOffset;
  11533. ULONG SizeToStay;
  11534. ULONG CurrentOffset, FutureOffset;
  11535. ULONG SizeToMove;
  11536. BOOLEAN FoundListContext;
  11537. MFT_SEGMENT_REFERENCE Reference1, Reference2;
  11538. LONGLONG MftFileRecord2;
  11539. PBCB Bcb = NULL;
  11540. ATTRIBUTE_TYPE_CODE EndCode = $END;
  11541. PVCB Vcb = Fcb->Vcb;
  11542. ULONG AdjustedAvailBytes;
  11543. PAGED_CODE();
  11544. //
  11545. // Make sure the attribute is pinned.
  11546. //
  11547. NtfsPinMappedAttribute( IrpContext,
  11548. Vcb,
  11549. Context );
  11550. //
  11551. // Something is broken if we decide to split an Mft record.
  11552. //
  11553. ASSERT(Fcb != Vcb->MftScb->Fcb);
  11554. NtfsInitializeAttributeContext( &ListContext );
  11555. NtfsInitializeAttributeContext( &MoveContext );
  11556. FileRecord1 = NtfsContainingFileRecord(Context);
  11557. Attribute1 = NtfsFoundAttribute(Context);
  11558. try {
  11559. //
  11560. // Lookup the list context so that we know where it is at.
  11561. //
  11562. FoundListContext =
  11563. NtfsLookupAttributeByCode( IrpContext,
  11564. Fcb,
  11565. &Fcb->FileReference,
  11566. $ATTRIBUTE_LIST,
  11567. &ListContext );
  11568. //
  11569. // If we do not already have an attribute list, then calculate
  11570. // where it will go and how big it must be. Note, there must
  11571. // only be one file record at this point.
  11572. //
  11573. if (!FoundListContext) {
  11574. ASSERT( FileRecord1 == NtfsContainingFileRecord(&ListContext) );
  11575. NewListOffset = PtrOffset( FileRecord1,
  11576. NtfsFoundAttribute(&ListContext) );
  11577. NewListSize = GetSizeForAttributeList( FileRecord1 ) +
  11578. SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  11579. }
  11580. //
  11581. // Similarly describe where the new attribute is to go, and how
  11582. // big it is (already in SizeNeeded).
  11583. //
  11584. NewAttributeOffset = PtrOffset( FileRecord1, Attribute1 );
  11585. //
  11586. // Now calculate the approximate number of bytes that is to be split
  11587. // across two file records, and divide it in two, and that should give
  11588. // the amount that is to stay in the first record.
  11589. //
  11590. SizeToStay = (FileRecord1->FirstFreeByte + NewListSize +
  11591. SizeNeeded + sizeof(FILE_RECORD_SEGMENT_HEADER)) / 2;
  11592. //
  11593. // We know that since we called this routine we need to split at
  11594. // least one entry from this file record. We also base our
  11595. // split logic by finding the first attribute which WILL lie beyond
  11596. // the split point (after adding an attribute list and possibly
  11597. // an intermediate attribute). We shrink the split point to the
  11598. // position at the end of where the current last attribute will be
  11599. // after adding the attribute list. If we also add space before
  11600. // the last attribute then we know the last attribute will surely
  11601. // be split out.
  11602. //
  11603. if (SizeToStay > (FileRecord1->FirstFreeByte - sizeof( LONGLONG ) + NewListSize)) {
  11604. SizeToStay = FileRecord1->FirstFreeByte - sizeof( LONGLONG ) + NewListSize;
  11605. }
  11606. //
  11607. // Now begin the loop through the attributes to find the splitting
  11608. // point. We stop when we reach the end record or are past the attribute
  11609. // which contains the split point. We will split at the current attribute
  11610. // if the remaining bytes after this attribute won't allow us to add
  11611. // the bytes we need for the caller or create an attribute list if
  11612. // it doesn't exist.
  11613. //
  11614. // At this point the following variables indicate the following:
  11615. //
  11616. // FutureOffset - This the offset of the current attribute
  11617. // after adding an attribute list and the attribute we
  11618. // are making space for.
  11619. //
  11620. // CurrentOffset - Current position in the file record of
  11621. // of attribute being examined now.
  11622. //
  11623. // NewListOffset - Offset to insert new attribute list into
  11624. // file record (0 indicates the list already exists).
  11625. //
  11626. // NewAttributeOffset - Offset in the file record of the new
  11627. // attribute. This refers to the file record as it exists
  11628. // when this routine is called.
  11629. //
  11630. FutureOffset =
  11631. CurrentOffset = (ULONG)FileRecord1->FirstAttributeOffset;
  11632. Attribute1 = Add2Ptr( FileRecord1, CurrentOffset );
  11633. AdjustedAvailBytes = FileRecord1->BytesAvailable
  11634. - QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  11635. while (Attribute1->TypeCode != $END) {
  11636. //
  11637. // See if the attribute list goes here.
  11638. //
  11639. if (CurrentOffset == NewListOffset) {
  11640. //
  11641. // This attribute and all later attributes will be moved
  11642. // by the size of attribute list.
  11643. //
  11644. FutureOffset += NewListSize;
  11645. }
  11646. //
  11647. // See if the new attribute goes here.
  11648. //
  11649. if (CurrentOffset == NewAttributeOffset) {
  11650. //
  11651. // This attribute and all later attributes will be moved
  11652. // by the size of new attribute.
  11653. //
  11654. FutureOffset += SizeNeeded;
  11655. }
  11656. FutureOffset += Attribute1->RecordLength;
  11657. //
  11658. // Check if we are at the split point. We split at this point
  11659. // if the end of the current attribute will be at or beyond the
  11660. // split point after adjusting for adding either an attribute list
  11661. // or new attribute. We make this test >= since these two values
  11662. // will be equal if we reach the last attribute without finding
  11663. // the split point. This way we guarantee a split will happen.
  11664. //
  11665. // Note that we will go to the next attribute if the current attribute
  11666. // is the first attribute in the file record. This can happen if the
  11667. // first attribute is resident and must stay resident but takes up
  11668. // half the file record or more (i.e. large filename attribute).
  11669. // We must make sure to split at least one attribute out of this
  11670. // record.
  11671. //
  11672. // Never split when pointing at $STANDARD_INFORMATION or $ATTRIBUTE_LIST.
  11673. //
  11674. if ((Attribute1->TypeCode > $ATTRIBUTE_LIST) &&
  11675. (FutureOffset >= SizeToStay) &&
  11676. (CurrentOffset != FileRecord1->FirstAttributeOffset)) {
  11677. break;
  11678. }
  11679. CurrentOffset += Attribute1->RecordLength;
  11680. Attribute1 = Add2Ptr( Attribute1, Attribute1->RecordLength );
  11681. }
  11682. SizeToMove = FileRecord1->FirstFreeByte - CurrentOffset;
  11683. //
  11684. // If we are pointing at the attribute list or at the end record
  11685. // we don't do the split. Raise INSUFFICIENT_RESOURCES so our caller
  11686. // knows that we can't do the split.
  11687. //
  11688. if ((Attribute1->TypeCode == $END) || (Attribute1->TypeCode <= $ATTRIBUTE_LIST)) {
  11689. NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
  11690. }
  11691. //
  11692. // Now if the attribute list already exists, we have to look up
  11693. // the first one we are going to move in order to update the
  11694. // attribute list later.
  11695. //
  11696. if (FoundListContext) {
  11697. UNICODE_STRING AttributeName;
  11698. BOOLEAN FoundIt;
  11699. AttributeName.Length =
  11700. AttributeName.MaximumLength = (USHORT)Attribute1->NameLength << 1;
  11701. AttributeName.Buffer = Add2Ptr( Attribute1, Attribute1->NameOffset );
  11702. FoundIt = NtfsLookupAttributeByName( IrpContext,
  11703. Fcb,
  11704. &Fcb->FileReference,
  11705. Attribute1->TypeCode,
  11706. &AttributeName,
  11707. (Attribute1->FormCode == NONRESIDENT_FORM) ?
  11708. &Attribute1->Form.Nonresident.LowestVcn :
  11709. NULL,
  11710. FALSE,
  11711. &MoveContext );
  11712. //
  11713. // If we are splitting the file record between multiple attributes with
  11714. // the same name (i.e. FILE_NAME attributes) then we need to find the
  11715. // correct attribute. Since this is an unusual case we will just scan
  11716. // forwards from the current attribute until we find the correct attribute.
  11717. //
  11718. while (FoundIt && (Attribute1 != NtfsFoundAttribute( &MoveContext ))) {
  11719. FoundIt = NtfsLookupNextAttributeByName( IrpContext,
  11720. Fcb,
  11721. Attribute1->TypeCode,
  11722. &AttributeName,
  11723. FALSE,
  11724. &MoveContext );
  11725. }
  11726. if (!FoundIt) {
  11727. ASSERT( FALSE );
  11728. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11729. }
  11730. ASSERT(Attribute1 == NtfsFoundAttribute(&MoveContext));
  11731. }
  11732. //
  11733. // Now Attribute1 is pointing to the first attribute to move.
  11734. // Allocate a new file record and move the rest of our attributes
  11735. // over.
  11736. //
  11737. if (FoundListContext) {
  11738. Reference1 = MoveContext.AttributeList.Entry->SegmentReference;
  11739. }
  11740. FileRecord2 = NtfsCloneFileRecord( IrpContext, Fcb, FALSE, &Bcb, &Reference2 );
  11741. //
  11742. // Capture the file record number of the new file record.
  11743. //
  11744. MftFileRecord2 = NtfsFullSegmentNumber( &Reference2 );
  11745. Attribute2 = Add2Ptr( FileRecord2, FileRecord2->FirstAttributeOffset );
  11746. RtlCopyMemory( Attribute2, Attribute1, SizeToMove );
  11747. FileRecord2->FirstFreeByte = (ULONG)FileRecord2->FirstAttributeOffset +
  11748. SizeToMove;
  11749. //
  11750. // Loop to update all of the attribute instance codes
  11751. //
  11752. for (Attribute = Attribute2;
  11753. Attribute < (PATTRIBUTE_RECORD_HEADER)Add2Ptr(FileRecord2, FileRecord2->FirstFreeByte)
  11754. && Attribute->TypeCode != $END;
  11755. Attribute = NtfsGetNextRecord(Attribute)) {
  11756. NtfsCheckRecordBound( Attribute, FileRecord2, Vcb->BytesPerFileRecordSegment );
  11757. if (FoundListContext) {
  11758. UpdateAttributeListEntry( IrpContext,
  11759. Fcb,
  11760. &Reference1,
  11761. Attribute->Instance,
  11762. &Reference2,
  11763. FileRecord2->NextAttributeInstance,
  11764. &ListContext );
  11765. }
  11766. Attribute->Instance = FileRecord2->NextAttributeInstance++;
  11767. }
  11768. //
  11769. // Now log these changes and fix up the first file record.
  11770. //
  11771. FileRecord2->Lsn = NtfsWriteLog( IrpContext,
  11772. Vcb->MftScb,
  11773. Bcb,
  11774. InitializeFileRecordSegment,
  11775. FileRecord2,
  11776. FileRecord2->FirstFreeByte,
  11777. Noop,
  11778. NULL,
  11779. 0,
  11780. LlBytesFromFileRecords( Vcb, MftFileRecord2 ),
  11781. 0,
  11782. 0,
  11783. Vcb->BytesPerFileRecordSegment );
  11784. FileRecord1->Lsn = NtfsWriteLog( IrpContext,
  11785. Vcb->MftScb,
  11786. NtfsFoundBcb(Context),
  11787. WriteEndOfFileRecordSegment,
  11788. &EndCode,
  11789. sizeof(ATTRIBUTE_TYPE_CODE),
  11790. WriteEndOfFileRecordSegment,
  11791. Attribute1,
  11792. SizeToMove,
  11793. NtfsMftOffset( Context ),
  11794. CurrentOffset,
  11795. 0,
  11796. Vcb->BytesPerFileRecordSegment );
  11797. NtfsRestartWriteEndOfFileRecord( FileRecord1,
  11798. Attribute1,
  11799. (PATTRIBUTE_RECORD_HEADER)&EndCode,
  11800. sizeof(ATTRIBUTE_TYPE_CODE) );
  11801. //
  11802. // Finally, create the attribute list attribute if needed.
  11803. //
  11804. if (!FoundListContext) {
  11805. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11806. NtfsInitializeAttributeContext( &ListContext );
  11807. CreateAttributeList( IrpContext,
  11808. Fcb,
  11809. FileRecord1,
  11810. FileRecord2,
  11811. Reference2,
  11812. NULL,
  11813. NewListSize - SIZEOF_RESIDENT_ATTRIBUTE_HEADER,
  11814. &ListContext );
  11815. }
  11816. } finally {
  11817. NtfsUnpinBcb( IrpContext, &Bcb );
  11818. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11819. NtfsCleanupAttributeContext( IrpContext, &MoveContext );
  11820. }
  11821. }
  11822. VOID
  11823. NtfsRestartWriteEndOfFileRecord (
  11824. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  11825. IN PATTRIBUTE_RECORD_HEADER OldAttribute,
  11826. IN PATTRIBUTE_RECORD_HEADER NewAttributes,
  11827. IN ULONG SizeOfNewAttributes
  11828. )
  11829. /*++
  11830. Routine Description:
  11831. This routine is called both in the running system and at restart to
  11832. modify the end of a file record, such as after it was split in two.
  11833. Arguments:
  11834. FileRecord - Supplies the pointer to the file record.
  11835. OldAttribute - Supplies a pointer to the first attribute to be overwritten.
  11836. NewAttributes - Supplies a pointer to the new attribute(s) to be copied to
  11837. the spot above.
  11838. SizeOfNewAttributes - Supplies the size to be copied in bytes.
  11839. Return Value:
  11840. None.
  11841. --*/
  11842. {
  11843. PAGED_CODE();
  11844. RtlMoveMemory( OldAttribute, NewAttributes, SizeOfNewAttributes );
  11845. FileRecord->FirstFreeByte = PtrOffset(FileRecord, OldAttribute) +
  11846. SizeOfNewAttributes;
  11847. //
  11848. // The size coming in may not be quad aligned.
  11849. //
  11850. FileRecord->FirstFreeByte = QuadAlign( FileRecord->FirstFreeByte );
  11851. }
  11852. //
  11853. // Internal support routine
  11854. //
  11855. PFILE_RECORD_SEGMENT_HEADER
  11856. NtfsCloneFileRecord (
  11857. IN PIRP_CONTEXT IrpContext,
  11858. IN PFCB Fcb,
  11859. IN BOOLEAN MftData,
  11860. OUT PBCB *Bcb,
  11861. OUT PMFT_SEGMENT_REFERENCE FileReference
  11862. )
  11863. /*++
  11864. Routine Description:
  11865. This routine allocates an additional file record for an already existing
  11866. and open file, for the purpose of overflowing attributes to this record.
  11867. Arguments:
  11868. Fcb - Requested file.
  11869. MftData - TRUE if the file record is being cloned to describe the
  11870. $DATA attribute for the Mft.
  11871. Bcb - Returns a pointer to the Bcb for the new file record.
  11872. FileReference - returns the file reference for the new file record.
  11873. Return Value:
  11874. Pointer to the allocated file record.
  11875. --*/
  11876. {
  11877. LONGLONG FileRecordOffset;
  11878. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  11879. PVCB Vcb = Fcb->Vcb;
  11880. PAGED_CODE();
  11881. //
  11882. // First allocate the record.
  11883. //
  11884. *FileReference = NtfsAllocateMftRecord( IrpContext,
  11885. Vcb,
  11886. MftData );
  11887. //
  11888. // Read it in and pin it.
  11889. //
  11890. NtfsPinMftRecord( IrpContext,
  11891. Vcb,
  11892. FileReference,
  11893. TRUE,
  11894. Bcb,
  11895. &FileRecord,
  11896. &FileRecordOffset );
  11897. //
  11898. // Initialize it.
  11899. //
  11900. NtfsInitializeMftRecord( IrpContext,
  11901. Vcb,
  11902. FileReference,
  11903. FileRecord,
  11904. *Bcb,
  11905. BooleanIsDirectory( &Fcb->Info ));
  11906. FileRecord->BaseFileRecordSegment = Fcb->FileReference;
  11907. FileRecord->ReferenceCount = 0;
  11908. FileReference->SequenceNumber = FileRecord->SequenceNumber;
  11909. return FileRecord;
  11910. }
  11911. //
  11912. // Internal support routine
  11913. //
  11914. ULONG
  11915. GetSizeForAttributeList (
  11916. IN PFILE_RECORD_SEGMENT_HEADER FileRecord
  11917. )
  11918. /*++
  11919. Routine Description:
  11920. This routine is designed to calculate the size that will be required for
  11921. an attribute list attribute, for a base file record which is just about
  11922. to split into two file record segments.
  11923. Arguments:
  11924. FileRecord - Pointer to the file record which is just about to split.
  11925. Return Value:
  11926. Size in bytes of the attribute list attribute that will be required,
  11927. not including the attribute header size.
  11928. --*/
  11929. {
  11930. PATTRIBUTE_RECORD_HEADER Attribute;
  11931. ULONG Size = 0;
  11932. PAGED_CODE();
  11933. //
  11934. // Point to first attribute.
  11935. //
  11936. Attribute = Add2Ptr(FileRecord, FileRecord->FirstAttributeOffset);
  11937. //
  11938. // Loop to add up size of required attribute list entries.
  11939. //
  11940. while (Attribute->TypeCode != $END) {
  11941. Size += QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  11942. + ((ULONG) Attribute->NameLength << 1));
  11943. Attribute = Add2Ptr( Attribute, Attribute->RecordLength );
  11944. }
  11945. return Size;
  11946. }
  11947. //
  11948. // Internal support routine
  11949. //
  11950. VOID
  11951. CreateAttributeList (
  11952. IN PIRP_CONTEXT IrpContext,
  11953. IN PFCB Fcb,
  11954. IN PFILE_RECORD_SEGMENT_HEADER FileRecord1,
  11955. IN PFILE_RECORD_SEGMENT_HEADER FileRecord2 OPTIONAL,
  11956. IN MFT_SEGMENT_REFERENCE SegmentReference2,
  11957. IN PATTRIBUTE_RECORD_HEADER OldPosition OPTIONAL,
  11958. IN ULONG SizeOfList,
  11959. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT ListContext
  11960. )
  11961. /*++
  11962. Routine Description:
  11963. This routine is intended to be called to create the attribute list attribute
  11964. the first time. The caller must have already calculated the size required
  11965. for the list to pass into this routine. The caller must have already
  11966. removed any attributes from the base file record (FileRecord1) which are
  11967. not to remain there. He must then pass in a pointer to the base file record
  11968. and optionally a pointer to a second file record from which the new
  11969. attribute list is to be created.
  11970. Arguments:
  11971. Fcb - Requested file.
  11972. FileRecord1 - Pointer to the base file record, currently holding only those
  11973. attributes to be described there.
  11974. FileRecord2 - Optionally points to a second file record from which the
  11975. second half of the attribute list is to be constructed.
  11976. SegmentReference2 - The Mft segment reference of the second file record,
  11977. if one was supplied.
  11978. OldPosition - Should only be specified if FileRecord2 is specified. In this
  11979. case it must point to an attribute position in FileRecord1 from
  11980. which a single attribute was moved to file record 2. It will be
  11981. used as an indication of where the attribute list entry should
  11982. be inserted.
  11983. SizeOfList - Exact size of the attribute list which will be required.
  11984. ListContext - Context resulting from an attempt to look up the attribute
  11985. list attribute, which failed.
  11986. Return Value:
  11987. None
  11988. --*/
  11989. {
  11990. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  11991. PATTRIBUTE_RECORD_HEADER Attribute;
  11992. PATTRIBUTE_LIST_ENTRY AttributeList, ListEntry;
  11993. MFT_SEGMENT_REFERENCE SegmentReference;
  11994. PAGED_CODE();
  11995. //
  11996. // Allocate space to construct the attribute list. (The list
  11997. // cannot be constructed in place, because that would destroy error
  11998. // recovery.)
  11999. //
  12000. ListEntry =
  12001. AttributeList = (PATTRIBUTE_LIST_ENTRY) NtfsAllocatePool(PagedPool, SizeOfList );
  12002. //
  12003. // Use try-finally to deallocate on the way out.
  12004. //
  12005. try {
  12006. //
  12007. // Loop to fill in the attribute list from the two file records
  12008. //
  12009. for (FileRecord = FileRecord1, SegmentReference = Fcb->FileReference;
  12010. FileRecord != NULL;
  12011. FileRecord = ((FileRecord == FileRecord1) ? FileRecord2 : NULL),
  12012. SegmentReference = SegmentReference2) {
  12013. //
  12014. // Point to first attribute.
  12015. //
  12016. Attribute = Add2Ptr( FileRecord, FileRecord->FirstAttributeOffset );
  12017. //
  12018. // Loop to add up size of required attribute list entries.
  12019. //
  12020. while (Attribute->TypeCode != $END) {
  12021. PATTRIBUTE_RECORD_HEADER NextAttribute;
  12022. //
  12023. // See if we are at the remembered position. If so:
  12024. //
  12025. // Save this attribute to be the next one.
  12026. // Point to the single attribute in FileRecord2 instead
  12027. // Clear FileRecord2, as we will "consume" it here.
  12028. // Set the Segment reference in the ListEntry
  12029. //
  12030. if ((Attribute == OldPosition) && (FileRecord2 != NULL)) {
  12031. NextAttribute = Attribute;
  12032. Attribute = Add2Ptr(FileRecord2, FileRecord2->FirstAttributeOffset);
  12033. FileRecord2 = NULL;
  12034. ListEntry->SegmentReference = SegmentReference2;
  12035. //
  12036. // Otherwise, this is the normal loop case. So:
  12037. //
  12038. // Set the next attribute pointer accordingly.
  12039. // Set the Segment reference from the loop control
  12040. //
  12041. } else {
  12042. NextAttribute = Add2Ptr(Attribute, Attribute->RecordLength);
  12043. ListEntry->SegmentReference = SegmentReference;
  12044. }
  12045. //
  12046. // Now fill in the list entry.
  12047. //
  12048. ListEntry->AttributeTypeCode = Attribute->TypeCode;
  12049. ListEntry->RecordLength = (USHORT) QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  12050. + ((ULONG) Attribute->NameLength << 1));
  12051. ListEntry->AttributeNameLength = Attribute->NameLength;
  12052. ListEntry->AttributeNameOffset =
  12053. (UCHAR)PtrOffset( ListEntry, &ListEntry->AttributeName[0] );
  12054. ListEntry->Instance = Attribute->Instance;
  12055. ListEntry->LowestVcn = 0;
  12056. if (Attribute->FormCode == NONRESIDENT_FORM) {
  12057. ListEntry->LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  12058. }
  12059. if (Attribute->NameLength != 0) {
  12060. RtlCopyMemory( &ListEntry->AttributeName[0],
  12061. Add2Ptr(Attribute, Attribute->NameOffset),
  12062. Attribute->NameLength << 1 );
  12063. }
  12064. ListEntry = Add2Ptr(ListEntry, ListEntry->RecordLength);
  12065. Attribute = NextAttribute;
  12066. }
  12067. }
  12068. //
  12069. // Now create the attribute list attribute.
  12070. //
  12071. NtfsCreateAttributeWithValue( IrpContext,
  12072. Fcb,
  12073. $ATTRIBUTE_LIST,
  12074. NULL,
  12075. AttributeList,
  12076. SizeOfList,
  12077. 0,
  12078. NULL,
  12079. TRUE,
  12080. ListContext );
  12081. } finally {
  12082. NtfsFreePool( AttributeList );
  12083. }
  12084. }
  12085. //
  12086. // Internal support routine
  12087. //
  12088. VOID
  12089. UpdateAttributeListEntry (
  12090. IN PIRP_CONTEXT IrpContext,
  12091. IN PFCB Fcb,
  12092. IN PMFT_SEGMENT_REFERENCE OldFileReference,
  12093. IN USHORT OldInstance,
  12094. IN PMFT_SEGMENT_REFERENCE NewFileReference,
  12095. IN USHORT NewInstance,
  12096. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT ListContext
  12097. )
  12098. /*++
  12099. Routine Description:
  12100. This routine may be called to update a range of the attribute list
  12101. as required by the movement of a range of attributes to a second record.
  12102. The caller must supply a pointer to the file record to which the attributes
  12103. have moved, along with the segment reference of that record.
  12104. Arguments:
  12105. Fcb - Requested file.
  12106. OldFileReference - Old File Reference for attribute
  12107. OldInstance - Old Instance number for attribute
  12108. NewFileReference - New File Reference for attribute
  12109. NewInstance - New Instance number for attribute
  12110. ListContext - The attribute enumeration context which was used to locate
  12111. the attribute list.
  12112. Return Value:
  12113. None
  12114. --*/
  12115. {
  12116. PATTRIBUTE_LIST_ENTRY AttributeList, ListEntry, BeyondList;
  12117. PBCB Bcb = NULL;
  12118. ULONG SizeOfList;
  12119. ATTRIBUTE_LIST_ENTRY NewEntry;
  12120. PATTRIBUTE_RECORD_HEADER Attribute;
  12121. PAGED_CODE();
  12122. //
  12123. // Map the attribute list if the attribute is non-resident. Otherwise the
  12124. // attribute is already mapped and we have a Bcb in the attribute context.
  12125. //
  12126. Attribute = NtfsFoundAttribute( ListContext );
  12127. if (!NtfsIsAttributeResident( Attribute )) {
  12128. NtfsMapAttributeValue( IrpContext,
  12129. Fcb,
  12130. (PVOID *) &AttributeList,
  12131. &SizeOfList,
  12132. &Bcb,
  12133. ListContext );
  12134. //
  12135. // Don't call the Map attribute routine because it NULLs the Bcb in the
  12136. // attribute list. This Bcb is needed for ChangeAttributeValue to mark
  12137. // the page dirty.
  12138. //
  12139. } else {
  12140. AttributeList = (PATTRIBUTE_LIST_ENTRY) NtfsAttributeValue( Attribute );
  12141. SizeOfList = Attribute->Form.Resident.ValueLength;
  12142. }
  12143. //
  12144. // Make sure we unpin the list.
  12145. //
  12146. try {
  12147. //
  12148. // Point beyond the end of the list.
  12149. //
  12150. BeyondList = (PATTRIBUTE_LIST_ENTRY)Add2Ptr( AttributeList, SizeOfList );
  12151. //
  12152. // Loop through all of the attribute list entries until we find the one
  12153. // we need to change.
  12154. //
  12155. for (ListEntry = AttributeList;
  12156. ListEntry < BeyondList;
  12157. ListEntry = NtfsGetNextRecord(ListEntry)) {
  12158. if ((ListEntry->Instance == OldInstance) &&
  12159. NtfsEqualMftRef(&ListEntry->SegmentReference, OldFileReference)) {
  12160. break;
  12161. }
  12162. }
  12163. ASSERT( (Fcb != Fcb->Vcb->MftScb->Fcb) ||
  12164. ((ULONGLONG)(ListEntry->LowestVcn) > (NtfsFullSegmentNumber( NewFileReference ) >> Fcb->Vcb->MftToClusterShift)) );
  12165. //
  12166. // We better have found it!
  12167. //
  12168. ASSERT(ListEntry < BeyondList);
  12169. if (ListEntry >= BeyondList) {
  12170. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  12171. }
  12172. //
  12173. // Make a copy of the fixed portion of the attribute list entry,
  12174. // and update to describe the new attribute location.
  12175. //
  12176. RtlCopyMemory( &NewEntry, ListEntry, sizeof(ATTRIBUTE_LIST_ENTRY) );
  12177. NewEntry.SegmentReference = *NewFileReference;
  12178. NewEntry.Instance = NewInstance;
  12179. //
  12180. // Update the attribute list entry.
  12181. //
  12182. NtfsChangeAttributeValue( IrpContext,
  12183. Fcb,
  12184. PtrOffset(AttributeList, ListEntry),
  12185. &NewEntry,
  12186. sizeof(ATTRIBUTE_LIST_ENTRY),
  12187. FALSE,
  12188. TRUE,
  12189. FALSE,
  12190. TRUE,
  12191. ListContext );
  12192. } finally {
  12193. NtfsUnpinBcb( IrpContext, &Bcb );
  12194. }
  12195. }
  12196. //
  12197. // Local support routine
  12198. //
  12199. VOID
  12200. NtfsAddNameToParent (
  12201. IN PIRP_CONTEXT IrpContext,
  12202. IN PSCB ParentScb,
  12203. IN PFCB ThisFcb,
  12204. IN BOOLEAN IgnoreCase,
  12205. IN PBOOLEAN LogIt,
  12206. IN PFILE_NAME FileNameAttr,
  12207. OUT PUCHAR FileNameFlags,
  12208. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  12209. IN PNAME_PAIR NamePair OPTIONAL,
  12210. IN PINDEX_CONTEXT IndexContext OPTIONAL
  12211. )
  12212. /*++
  12213. Routine Description:
  12214. This routine will create the filename attribute with the given name.
  12215. Depending on the IgnoreCase flag, this is either a link or an Ntfs
  12216. name. If it is an Ntfs name, we check if it is also the Dos name.
  12217. We build a file name attribute and then add it via ThisFcb, we then
  12218. add this entry to the parent.
  12219. If the name is a Dos name and we are given tunneling information on
  12220. the long name, we will add the long name attribute as well.
  12221. Arguments:
  12222. ParentScb - This is the parent directory for the file.
  12223. ThisFcb - This is the file to add the filename to.
  12224. IgnoreCase - Indicates if this name is case insensitive. Only for Posix
  12225. will this be FALSE.
  12226. LogIt - Indicates if we should log this operation. If FALSE and this is a large
  12227. name then log the file record and begin logging.
  12228. FileNameAttr - This contains a file name attribute structure to use.
  12229. FileNameFlags - We store a copy of the File name flags used in the file
  12230. name attribute.
  12231. QuickIndex - If specified, we store the information about the location of the
  12232. index entry added.
  12233. NamePair - If specified, we add the tunneled NTFS-only name if the name we are
  12234. directly adding is DOS-only.
  12235. IndexContext - Previous result of doing the lookup for the name in the index.
  12236. Return Value:
  12237. None - This routine will raise on error.
  12238. --*/
  12239. {
  12240. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  12241. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  12242. PAGED_CODE();
  12243. DebugTrace( +1, Dbg, ("NtfsAddNameToParent: Entered\n") );
  12244. NtfsInitializeAttributeContext( &AttrContext );
  12245. //
  12246. // Use a try-finally to facilitate cleanup.
  12247. //
  12248. try {
  12249. //
  12250. // Decide whether the name is a link, Ntfs-Only or Ntfs/8.3 combined name.
  12251. // Update the filename attribute to reflect this.
  12252. //
  12253. if (!IgnoreCase) {
  12254. *FileNameFlags = 0;
  12255. } else {
  12256. UNICODE_STRING FileName;
  12257. FileName.Length = FileName.MaximumLength = (USHORT)(FileNameAttr->FileNameLength * sizeof(WCHAR));
  12258. FileName.Buffer = FileNameAttr->FileName;
  12259. *FileNameFlags = FILE_NAME_NTFS;
  12260. if (NtfsIsFatNameValid( &FileName, FALSE )) {
  12261. *FileNameFlags |= FILE_NAME_DOS;
  12262. }
  12263. //
  12264. // If the name is DOS and there was a tunneled NTFS name, add it first if both names
  12265. // exist in the pair (there may only be one in the long side). Note that we
  12266. // really need to do this first so we lay down the correct filename flags.
  12267. //
  12268. if (NamePair &&
  12269. (NamePair->Long.Length > 0) &&
  12270. (NamePair->Short.Length > 0) &&
  12271. (*FileNameFlags == (FILE_NAME_NTFS | FILE_NAME_DOS))) {
  12272. if (NtfsAddTunneledNtfsOnlyName(IrpContext,
  12273. ParentScb,
  12274. ThisFcb,
  12275. &NamePair->Long,
  12276. LogIt )) {
  12277. //
  12278. // Name didn't conflict and was added, so fix up the FileNameFlags
  12279. //
  12280. *FileNameFlags = FILE_NAME_DOS;
  12281. //
  12282. // Make sure we reposition in the index for the actual insertion.
  12283. //
  12284. IndexContext = NULL;
  12285. //
  12286. // We also need to upcase the short DOS name since we don't know the
  12287. // case of what the user handed us and all DOS names are upcase. Note
  12288. // that prior to tunneling being supported it was not possible for a user
  12289. // to specify a short name, so this is a new situation.
  12290. //
  12291. RtlUpcaseUnicodeString(&FileName, &FileName, FALSE);
  12292. }
  12293. }
  12294. }
  12295. //
  12296. // Now update the file name attribute.
  12297. //
  12298. FileNameAttr->Flags = *FileNameFlags;
  12299. //
  12300. // If we haven't been logging and this is a large name then begin logging.
  12301. //
  12302. if (!(*LogIt) &&
  12303. (FileNameAttr->FileNameLength > 100)) {
  12304. //
  12305. // Look up the file record and log its current state.
  12306. //
  12307. if (!NtfsLookupAttributeByCode( IrpContext,
  12308. ThisFcb,
  12309. &ThisFcb->FileReference,
  12310. $STANDARD_INFORMATION,
  12311. &AttrContext )) {
  12312. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, ThisFcb );
  12313. }
  12314. NtfsPinMappedAttribute( IrpContext, ThisFcb->Vcb, &AttrContext );
  12315. FileRecord = NtfsContainingFileRecord( &AttrContext );
  12316. //
  12317. // Log the current state of the file record.
  12318. //
  12319. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  12320. ThisFcb->Vcb->MftScb,
  12321. NtfsFoundBcb( &AttrContext ),
  12322. InitializeFileRecordSegment,
  12323. FileRecord,
  12324. FileRecord->FirstFreeByte,
  12325. Noop,
  12326. NULL,
  12327. 0,
  12328. NtfsMftOffset( &AttrContext ),
  12329. 0,
  12330. 0,
  12331. ThisFcb->Vcb->BytesPerFileRecordSegment );
  12332. *LogIt = TRUE;
  12333. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12334. NtfsInitializeAttributeContext( &AttrContext );
  12335. }
  12336. //
  12337. // Put it in the file record.
  12338. //
  12339. NtfsCreateAttributeWithValue( IrpContext,
  12340. ThisFcb,
  12341. $FILE_NAME,
  12342. NULL,
  12343. FileNameAttr,
  12344. NtfsFileNameSize( FileNameAttr ),
  12345. 0,
  12346. &FileNameAttr->ParentDirectory,
  12347. *LogIt,
  12348. &AttrContext );
  12349. //
  12350. // Now put it in the index entry.
  12351. //
  12352. NtfsAddIndexEntry( IrpContext,
  12353. ParentScb,
  12354. FileNameAttr,
  12355. NtfsFileNameSize( FileNameAttr ),
  12356. &ThisFcb->FileReference,
  12357. IndexContext,
  12358. QuickIndex );
  12359. } finally {
  12360. DebugUnwind( NtfsAddNameToParent );
  12361. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12362. DebugTrace( -1, Dbg, ("NtfsAddNameToParent: Exit\n") );
  12363. }
  12364. return;
  12365. }
  12366. //
  12367. // Local support routine
  12368. //
  12369. VOID
  12370. NtfsAddDosOnlyName (
  12371. IN PIRP_CONTEXT IrpContext,
  12372. IN PSCB ParentScb,
  12373. IN PFCB ThisFcb,
  12374. IN UNICODE_STRING FileName,
  12375. IN BOOLEAN LogIt,
  12376. IN PUNICODE_STRING SuggestedDosName OPTIONAL
  12377. )
  12378. /*++
  12379. Routine Description:
  12380. This routine is called to build a Dos only name attribute an put it in
  12381. the file record and the parent index. We need to allocate pool large
  12382. enough to hold the name (easy for 8.3) and then check that the generated
  12383. names don't already exist in the parent. Use the suggested name first if
  12384. possible.
  12385. Arguments:
  12386. ParentScb - This is the parent directory for the file.
  12387. ThisFcb - This is the file to add the filename to.
  12388. FileName - This is the file name to add.
  12389. LogIt - Indicates if we should log this operation.
  12390. SuggestedDosName - If supplied, a name to try to use before auto-generation
  12391. Return Value:
  12392. None - This routine will raise on error.
  12393. --*/
  12394. {
  12395. GENERATE_NAME_CONTEXT NameContext;
  12396. PFILE_NAME FileNameAttr;
  12397. UNICODE_STRING Name8dot3;
  12398. PINDEX_ENTRY IndexEntry;
  12399. PBCB IndexEntryBcb;
  12400. UCHAR TrailingDotAdj;
  12401. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  12402. BOOLEAN TrySuggestedDosName = TRUE;
  12403. PAGED_CODE();
  12404. DebugTrace( +1, Dbg, ("NtfsAddDosOnlyName: Entered\n") );
  12405. IndexEntryBcb = NULL;
  12406. RtlZeroMemory( &NameContext, sizeof( GENERATE_NAME_CONTEXT ));
  12407. if (SuggestedDosName == NULL || SuggestedDosName->Length == 0) {
  12408. //
  12409. // The SuggestedDosName can be zero length if we have a tunneled
  12410. // link or a tunneled file which was created whilst short name
  12411. // generation was disabled. It is a bad thing to drop down null
  12412. // filenames ...
  12413. //
  12414. TrySuggestedDosName = FALSE;
  12415. }
  12416. //
  12417. // The maximum length is 24 bytes, but 2 are already defined with the
  12418. // FILE_NAME structure.
  12419. //
  12420. FileNameAttr = NtfsAllocatePool(PagedPool, sizeof( FILE_NAME ) + 22 );
  12421. //
  12422. // Use a try-finally to facilitate cleanup.
  12423. //
  12424. try {
  12425. NtfsInitializeAttributeContext( &AttrContext );
  12426. //
  12427. // Set up the string to hold the generated name. It will be part
  12428. // of the file name attribute structure.
  12429. //
  12430. Name8dot3.Buffer = FileNameAttr->FileName;
  12431. Name8dot3.MaximumLength = 24;
  12432. FileNameAttr->ParentDirectory = ParentScb->Fcb->FileReference;
  12433. FileNameAttr->Flags = FILE_NAME_DOS;
  12434. //
  12435. // Copy the info values into the filename attribute.
  12436. //
  12437. RtlCopyMemory( &FileNameAttr->Info,
  12438. &ThisFcb->Info,
  12439. sizeof( DUPLICATED_INFORMATION ));
  12440. //
  12441. // We will loop indefinitely. We generate a name, look in the parent
  12442. // for it. If found we continue generating. If not then we have the
  12443. // name we need. Attempt to use the suggested name first.
  12444. //
  12445. while( TRUE ) {
  12446. TrailingDotAdj = 0;
  12447. if (TrySuggestedDosName) {
  12448. Name8dot3.Length = SuggestedDosName->Length;
  12449. RtlCopyMemory(Name8dot3.Buffer, SuggestedDosName->Buffer, SuggestedDosName->Length);
  12450. Name8dot3.MaximumLength = SuggestedDosName->MaximumLength;
  12451. } else {
  12452. RtlGenerate8dot3Name( &FileName,
  12453. BooleanFlagOn(NtfsData.Flags,NTFS_FLAGS_ALLOW_EXTENDED_CHAR),
  12454. &NameContext,
  12455. &Name8dot3 );
  12456. if ((Name8dot3.Buffer[(Name8dot3.Length / sizeof( WCHAR )) - 1] == L'.') &&
  12457. (Name8dot3.Length > sizeof( WCHAR ))) {
  12458. TrailingDotAdj = 1;
  12459. }
  12460. }
  12461. FileNameAttr->FileNameLength = (UCHAR)(Name8dot3.Length / sizeof( WCHAR )) - TrailingDotAdj;
  12462. if (!NtfsFindIndexEntry( IrpContext,
  12463. ParentScb,
  12464. FileNameAttr,
  12465. TRUE,
  12466. NULL,
  12467. &IndexEntryBcb,
  12468. &IndexEntry,
  12469. NULL )) {
  12470. break;
  12471. }
  12472. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  12473. if (TrySuggestedDosName) {
  12474. //
  12475. // Failed to use the suggested name, so fix up the 8.3 space
  12476. //
  12477. Name8dot3.Buffer = FileNameAttr->FileName;
  12478. Name8dot3.MaximumLength = 24;
  12479. TrySuggestedDosName = FALSE;
  12480. }
  12481. }
  12482. //
  12483. // We add this entry to the file record.
  12484. //
  12485. NtfsCreateAttributeWithValue( IrpContext,
  12486. ThisFcb,
  12487. $FILE_NAME,
  12488. NULL,
  12489. FileNameAttr,
  12490. NtfsFileNameSize( FileNameAttr ),
  12491. 0,
  12492. &FileNameAttr->ParentDirectory,
  12493. LogIt,
  12494. &AttrContext );
  12495. //
  12496. // We add this entry to the parent.
  12497. //
  12498. NtfsAddIndexEntry( IrpContext,
  12499. ParentScb,
  12500. FileNameAttr,
  12501. NtfsFileNameSize( FileNameAttr ),
  12502. &ThisFcb->FileReference,
  12503. NULL,
  12504. NULL );
  12505. } finally {
  12506. DebugUnwind( NtfsAddDosOnlyName );
  12507. NtfsFreePool( FileNameAttr );
  12508. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  12509. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12510. DebugTrace( -1, Dbg, ("NtfsAddDosOnlyName: Exit -> %08lx\n") );
  12511. }
  12512. return;
  12513. }
  12514. //
  12515. // Local support routine
  12516. //
  12517. BOOLEAN
  12518. NtfsAddTunneledNtfsOnlyName (
  12519. IN PIRP_CONTEXT IrpContext,
  12520. IN PSCB ParentScb,
  12521. IN PFCB ThisFcb,
  12522. IN PUNICODE_STRING FileName,
  12523. IN PBOOLEAN LogIt
  12524. )
  12525. /*++
  12526. Routine Description:
  12527. This routine is called to attempt to insert a tunneled NTFS-only name
  12528. attribute and put it in the file record and the parent index. If the
  12529. name collides with an existing name nothing occurs.
  12530. Arguments:
  12531. ParentScb - This is the parent directory for the file.
  12532. ThisFcb - This is the file to add the filename to.
  12533. FileName - This is the file name to add.
  12534. LogIt - Indicates if we should log this operation. If FALSE and this is a large
  12535. name then log the file record and begin logging.
  12536. Return Value:
  12537. Boolean true if the name is added, false otherwise
  12538. --*/
  12539. {
  12540. PFILE_NAME FileNameAttr;
  12541. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  12542. PINDEX_ENTRY IndexEntry;
  12543. PBCB IndexEntryBcb;
  12544. BOOLEAN Added = FALSE;
  12545. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  12546. PAGED_CODE();
  12547. DebugTrace( +1, Dbg, ("NtfsAddTunneledNtfsOnlyName: Entered\n") );
  12548. IndexEntryBcb = NULL;
  12549. //
  12550. // One WCHAR is already defined with the FILE_NAME structure. It is unfortunate
  12551. // that we need to go to pool to do this ...
  12552. //
  12553. FileNameAttr = NtfsAllocatePool(PagedPool, sizeof( FILE_NAME ) + FileName->Length - sizeof(WCHAR) );
  12554. //
  12555. // Use a try-finally to facilitate cleanup.
  12556. //
  12557. try {
  12558. NtfsInitializeAttributeContext( &AttrContext );
  12559. RtlCopyMemory( FileNameAttr->FileName,
  12560. FileName->Buffer,
  12561. FileName->Length );
  12562. FileNameAttr->FileNameLength = (UCHAR)(FileName->Length / sizeof(WCHAR));
  12563. FileNameAttr->ParentDirectory = ParentScb->Fcb->FileReference;
  12564. FileNameAttr->Flags = FILE_NAME_NTFS;
  12565. //
  12566. // Copy the info values into the filename attribute.
  12567. //
  12568. RtlCopyMemory( &FileNameAttr->Info,
  12569. &ThisFcb->Info,
  12570. sizeof( DUPLICATED_INFORMATION ));
  12571. //
  12572. // Try out the name
  12573. //
  12574. if (!NtfsFindIndexEntry( IrpContext,
  12575. ParentScb,
  12576. FileNameAttr,
  12577. TRUE,
  12578. NULL,
  12579. &IndexEntryBcb,
  12580. &IndexEntry,
  12581. NULL )) {
  12582. //
  12583. // Restore the case of the tunneled name
  12584. //
  12585. RtlCopyMemory( FileNameAttr->FileName,
  12586. FileName->Buffer,
  12587. FileName->Length );
  12588. //
  12589. // If we haven't been logging and this is a large name then begin logging.
  12590. //
  12591. if (!(*LogIt) &&
  12592. (FileName->Length > 200)) {
  12593. //
  12594. // Look up the file record and log its current state.
  12595. //
  12596. if (!NtfsLookupAttributeByCode( IrpContext,
  12597. ThisFcb,
  12598. &ThisFcb->FileReference,
  12599. $STANDARD_INFORMATION,
  12600. &AttrContext )) {
  12601. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, ThisFcb );
  12602. }
  12603. NtfsPinMappedAttribute( IrpContext, ThisFcb->Vcb, &AttrContext );
  12604. FileRecord = NtfsContainingFileRecord( &AttrContext );
  12605. //
  12606. // Log the current state of the file record.
  12607. //
  12608. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  12609. ThisFcb->Vcb->MftScb,
  12610. NtfsFoundBcb( &AttrContext ),
  12611. InitializeFileRecordSegment,
  12612. FileRecord,
  12613. FileRecord->FirstFreeByte,
  12614. Noop,
  12615. NULL,
  12616. 0,
  12617. NtfsMftOffset( &AttrContext ),
  12618. 0,
  12619. 0,
  12620. ThisFcb->Vcb->BytesPerFileRecordSegment );
  12621. *LogIt = TRUE;
  12622. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12623. NtfsInitializeAttributeContext( &AttrContext );
  12624. }
  12625. //
  12626. // We add this entry to the file record.
  12627. //
  12628. NtfsCreateAttributeWithValue( IrpContext,
  12629. ThisFcb,
  12630. $FILE_NAME,
  12631. NULL,
  12632. FileNameAttr,
  12633. NtfsFileNameSize( FileNameAttr ),
  12634. 0,
  12635. &FileNameAttr->ParentDirectory,
  12636. *LogIt,
  12637. &AttrContext );
  12638. //
  12639. // We add this entry to the parent.
  12640. //
  12641. NtfsAddIndexEntry( IrpContext,
  12642. ParentScb,
  12643. FileNameAttr,
  12644. NtfsFileNameSize( FileNameAttr ),
  12645. &ThisFcb->FileReference,
  12646. NULL,
  12647. NULL );
  12648. //
  12649. // Flag the addition
  12650. //
  12651. Added = TRUE;
  12652. }
  12653. } finally {
  12654. DebugUnwind( NtfsAddTunneledNtfsOnlyName );
  12655. NtfsFreePool( FileNameAttr );
  12656. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  12657. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12658. DebugTrace( -1, Dbg, ("NtfsAddTunneledNtfsOnlyName: Exit -> %08lx\n", Added) );
  12659. }
  12660. return Added;
  12661. }
  12662. //
  12663. // Local support routine
  12664. //
  12665. USHORT
  12666. NtfsScanForFreeInstance (
  12667. IN PIRP_CONTEXT IrpContext,
  12668. IN PVCB Vcb,
  12669. IN PFILE_RECORD_SEGMENT_HEADER FileRecord
  12670. )
  12671. /*++
  12672. Routine Description:
  12673. This routine is called when we are adding a new attribute to this file record
  12674. but the instance number is significant. We don't want the instance numbers
  12675. to roll over so we will scan for a free instance number.
  12676. Arguments:
  12677. Vcb - Vcb for this volume.
  12678. FileRecord - This is the file record to look at.
  12679. Return Value:
  12680. USHORT - Return the lowest free instance in the file record.
  12681. --*/
  12682. {
  12683. PATTRIBUTE_RECORD_HEADER Attribute;
  12684. ULONG AttributeCount = 0;
  12685. ULONG CurrentIndex;
  12686. ULONG MinIndex;
  12687. ULONG LowIndex;
  12688. USHORT CurrentMinInstance;
  12689. USHORT CurrentInstances[0x80];
  12690. USHORT LastInstance = 0xffff;
  12691. PAGED_CODE();
  12692. //
  12693. // Insert the existing attributes into our array.
  12694. //
  12695. Attribute = NtfsFirstAttribute( FileRecord );
  12696. while (Attribute->TypeCode != $END) {
  12697. //
  12698. // Store this instance in the current position in the array.
  12699. //
  12700. CurrentInstances[AttributeCount] = Attribute->Instance;
  12701. AttributeCount += 1;
  12702. Attribute = NtfsGetNextRecord( Attribute );
  12703. NtfsCheckRecordBound( Attribute, FileRecord, Vcb->BytesPerFileRecordSegment );
  12704. }
  12705. //
  12706. // If there are no entries then return 0 as the instance to use.
  12707. //
  12708. if (AttributeCount == 0) {
  12709. return 0;
  12710. //
  12711. // If there is only one entry then either return 0 or 1.
  12712. //
  12713. } else if (AttributeCount == 1) {
  12714. if (CurrentInstances[0] == 0) {
  12715. return 1;
  12716. } else {
  12717. return 0;
  12718. }
  12719. }
  12720. //
  12721. // We will start sorting the array. We can stop as soon as we find a gap.
  12722. //
  12723. LowIndex = 0;
  12724. while (LowIndex < AttributeCount) {
  12725. //
  12726. // Walk through from our current position and find the lowest value.
  12727. //
  12728. MinIndex = LowIndex;
  12729. CurrentMinInstance = CurrentInstances[MinIndex];
  12730. CurrentIndex = LowIndex + 1;
  12731. while (CurrentIndex < AttributeCount) {
  12732. if (CurrentInstances[CurrentIndex] < CurrentMinInstance) {
  12733. CurrentMinInstance = CurrentInstances[CurrentIndex];
  12734. MinIndex = CurrentIndex;
  12735. }
  12736. CurrentIndex += 1;
  12737. }
  12738. //
  12739. // If there is a gap between the previous value and the current instance then
  12740. // we are done.
  12741. //
  12742. if ((USHORT) (LastInstance + 1) != CurrentMinInstance) {
  12743. return LastInstance + 1;
  12744. }
  12745. //
  12746. // Otherwise move to the next index.
  12747. //
  12748. CurrentInstances[MinIndex] = CurrentInstances[LowIndex];
  12749. CurrentInstances[LowIndex] = CurrentMinInstance;
  12750. LastInstance = CurrentMinInstance;
  12751. LowIndex += 1;
  12752. }
  12753. //
  12754. // We walked through all of the existing without finding a free entry. Go ahead and
  12755. // return the next known instance.
  12756. //
  12757. return (USHORT) AttributeCount;
  12758. }
  12759. //
  12760. // Local support routine
  12761. //
  12762. VOID
  12763. NtfsMergeFileRecords (
  12764. IN PIRP_CONTEXT IrpContext,
  12765. IN PSCB Scb,
  12766. IN BOOLEAN RestoreContext,
  12767. IN PATTRIBUTE_ENUMERATION_CONTEXT Context
  12768. )
  12769. /*++
  12770. Routine Description:
  12771. This routine is called to possibly merge two file records which each consist of a single hole.
  12772. We are given a context which points to either the first of second record. We always
  12773. remove the second and update the first if we can find the holes.
  12774. NOTE - We always want to remove the second attribute not the first. The first may have a
  12775. TotalAllocated field which we can't lose.
  12776. Arguments:
  12777. Scb - Scb for the stream being modified.
  12778. RestoreContext - Indicates if we should be pointing at the merged record on exit.
  12779. Context - This points to either the first or second record of the merge. On return it will
  12780. be in an indeterminant state unless our caller has specified that we should be pointing
  12781. to the combined record.
  12782. Return Value:
  12783. None
  12784. --*/
  12785. {
  12786. PATTRIBUTE_RECORD_HEADER NewAttribute = NULL;
  12787. PATTRIBUTE_RECORD_HEADER Attribute;
  12788. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  12789. ULONG MappingPairsSize;
  12790. VCN LastVcn;
  12791. VCN RestoreVcn;
  12792. ULONG PassCount = 0;
  12793. VCN NewFinalVcn;
  12794. VCN NewStartVcn;
  12795. PUCHAR NextMappingPairs;
  12796. UCHAR VLength;
  12797. UCHAR LLength;
  12798. ULONG BytesAvail;
  12799. BOOLEAN TryPrior = TRUE;
  12800. PAGED_CODE();
  12801. //
  12802. // Use a try finally to facilitate cleanup.
  12803. //
  12804. try {
  12805. //
  12806. // Capture the file record and attribute.
  12807. //
  12808. Attribute = NtfsFoundAttribute( Context );
  12809. FileRecord = NtfsContainingFileRecord( Context );
  12810. //
  12811. // Remember the end of the current file record and the space available.
  12812. //
  12813. NewFinalVcn = Attribute->Form.Nonresident.HighestVcn;
  12814. RestoreVcn = NewStartVcn = Attribute->Form.Nonresident.LowestVcn;
  12815. BytesAvail = FileRecord->BytesAvailable - FileRecord->FirstFreeByte;
  12816. //
  12817. // Start by checking if we can merge with the following file record.
  12818. //
  12819. if (NtfsLookupNextAttributeForScb( IrpContext, Scb, Context )) {
  12820. Attribute = NtfsFoundAttribute( Context );
  12821. //
  12822. // If this attribute also consists entirely of a hole then merge the
  12823. // previous hole.
  12824. //
  12825. NextMappingPairs = Add2Ptr( Attribute, Attribute->Form.Nonresident.MappingPairsOffset );
  12826. LLength = *NextMappingPairs >> 4;
  12827. VLength = *NextMappingPairs & 0x0f;
  12828. NextMappingPairs = Add2Ptr( NextMappingPairs, LLength + VLength + 1);
  12829. //
  12830. // Perform the merge if the current file record is a hole and
  12831. // there is space in the previous record. There is space if the
  12832. // prior record has at least 8 available bytes or we know that
  12833. // the mapping pairs will only take 8 bytes (6 bytes for the Vcn).
  12834. // We don't want to deal with the rare (if not nonexistent) case
  12835. // where we need to grow the attribute in a full file record.
  12836. // Also check that the next range is contiguous. In some cases we
  12837. // may split an existing filerecord into a hole and an allocated
  12838. // range. We don't want to look ahead if we haven't written the
  12839. // next range.
  12840. //
  12841. if ((Attribute->Form.Nonresident.LowestVcn == NewFinalVcn + 1) &&
  12842. (LLength == 0) &&
  12843. (*NextMappingPairs == 0) &&
  12844. ((BytesAvail >= 8) ||
  12845. (Attribute->Form.Nonresident.HighestVcn - NewStartVcn <= 0x7fffffffffff))) {
  12846. TryPrior = FALSE;
  12847. //
  12848. // Update the new highest vcn value.
  12849. //
  12850. NewFinalVcn = Attribute->Form.Nonresident.HighestVcn;
  12851. }
  12852. }
  12853. //
  12854. // If we couldn't find a following file record then check for a
  12855. // previous file record.
  12856. //
  12857. if (TryPrior) {
  12858. //
  12859. // Reinitialize the context and look up the attribute again if there
  12860. // is no previous or look up the previous attribute.
  12861. //
  12862. if (NewStartVcn != 0) {
  12863. //
  12864. // Back up to the previous file record.
  12865. //
  12866. NewStartVcn -= 1;
  12867. //
  12868. // If we were already at the first file record then there is
  12869. // nothing more to try.
  12870. //
  12871. } else {
  12872. try_return( NOTHING );
  12873. }
  12874. NtfsCleanupAttributeContext( IrpContext, Context );
  12875. NtfsInitializeAttributeContext( Context );
  12876. NtfsLookupAttributeForScb( IrpContext, Scb, &NewStartVcn, Context );
  12877. Attribute = NtfsFoundAttribute( Context );
  12878. FileRecord = NtfsContainingFileRecord( Context );
  12879. NextMappingPairs = Add2Ptr( Attribute, Attribute->Form.Nonresident.MappingPairsOffset );
  12880. LLength = *NextMappingPairs >> 4;
  12881. VLength = *NextMappingPairs & 0x0f;
  12882. NextMappingPairs = Add2Ptr( NextMappingPairs, LLength + VLength + 1);
  12883. BytesAvail = FileRecord->BytesAvailable - FileRecord->FirstFreeByte;
  12884. //
  12885. // Update the new lowest vcn value.
  12886. //
  12887. NewStartVcn = Attribute->Form.Nonresident.LowestVcn;
  12888. //
  12889. // Perform the merge if the current file record is a hole and
  12890. // there is space in the current record. There is space if the
  12891. // current record has at least 8 available bytes or we know that
  12892. // the mapping pairs will only take 8 bytes (6 bytes for the Vcn).
  12893. //
  12894. if ((LLength != 0) ||
  12895. (*NextMappingPairs != 0) ||
  12896. ((BytesAvail < 8) &&
  12897. (NewFinalVcn - NewStartVcn > 0x7fffffffffff))) {
  12898. try_return( NOTHING );
  12899. }
  12900. }
  12901. //
  12902. // Now update the NtfsMcb to reflect the merge. Start by unloading the existing
  12903. // ranges and then define a new range.
  12904. //
  12905. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  12906. NewStartVcn,
  12907. NewFinalVcn,
  12908. FALSE,
  12909. FALSE );
  12910. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  12911. NewStartVcn,
  12912. NewFinalVcn,
  12913. FALSE );
  12914. NtfsAddNtfsMcbEntry( &Scb->Mcb,
  12915. NewStartVcn,
  12916. UNUSED_LCN,
  12917. NewFinalVcn - NewStartVcn + 1,
  12918. FALSE );
  12919. //
  12920. // We need two passes through this loop, one for each record.
  12921. //
  12922. while (TRUE) {
  12923. //
  12924. // Update our pointers to point to the attribute and file record.
  12925. //
  12926. Attribute = NtfsFoundAttribute( Context );
  12927. //
  12928. // If we are at the first record then update the entry.
  12929. //
  12930. if (Attribute->Form.Nonresident.LowestVcn == NewStartVcn) {
  12931. FileRecord = NtfsContainingFileRecord( Context );
  12932. //
  12933. // Allocate a buffer to hold the new attribute. Copy the existing attribute
  12934. // into this buffer and update the final vcn field.
  12935. //
  12936. NewAttribute = NtfsAllocatePool( PagedPool, Attribute->RecordLength + 8 );
  12937. RtlCopyMemory( NewAttribute, Attribute, Attribute->RecordLength );
  12938. LastVcn = NewAttribute->Form.Nonresident.HighestVcn = NewFinalVcn;
  12939. //
  12940. // Now get the new mapping pairs size and build the mapping pairs.
  12941. // We could easily do it by hand but we want to always use the same
  12942. // routines to build these.
  12943. //
  12944. MappingPairsSize = NtfsGetSizeForMappingPairs( &Scb->Mcb,
  12945. 0x10,
  12946. NewStartVcn,
  12947. &LastVcn,
  12948. &LastVcn );
  12949. ASSERT( LastVcn > NewFinalVcn );
  12950. NtfsBuildMappingPairs( &Scb->Mcb,
  12951. NewStartVcn,
  12952. &LastVcn,
  12953. Add2Ptr( NewAttribute,
  12954. NewAttribute->Form.Nonresident.MappingPairsOffset ));
  12955. NewAttribute->RecordLength = QuadAlign( NewAttribute->Form.Nonresident.MappingPairsOffset + MappingPairsSize );
  12956. //
  12957. // Make sure the current attribute is pinned.
  12958. //
  12959. NtfsPinMappedAttribute( IrpContext, Scb->Vcb, Context );
  12960. //
  12961. // Now log the old and new attribute.
  12962. //
  12963. FileRecord->Lsn =
  12964. NtfsWriteLog( IrpContext,
  12965. Scb->Vcb->MftScb,
  12966. NtfsFoundBcb( Context ),
  12967. DeleteAttribute,
  12968. NULL,
  12969. 0,
  12970. CreateAttribute,
  12971. Attribute,
  12972. Attribute->RecordLength,
  12973. NtfsMftOffset( Context ),
  12974. PtrOffset( FileRecord, Attribute ),
  12975. 0,
  12976. Scb->Vcb->BytesPerFileRecordSegment );
  12977. //
  12978. // Now update the file record.
  12979. //
  12980. NtfsRestartRemoveAttribute( IrpContext, FileRecord, PtrOffset( FileRecord, Attribute ));
  12981. FileRecord->Lsn =
  12982. NtfsWriteLog( IrpContext,
  12983. Scb->Vcb->MftScb,
  12984. NtfsFoundBcb( Context ),
  12985. CreateAttribute,
  12986. NewAttribute,
  12987. NewAttribute->RecordLength,
  12988. DeleteAttribute,
  12989. NULL,
  12990. 0,
  12991. NtfsMftOffset( Context ),
  12992. PtrOffset( FileRecord, Attribute ),
  12993. 0,
  12994. Scb->Vcb->BytesPerFileRecordSegment );
  12995. NtfsRestartInsertAttribute( IrpContext,
  12996. FileRecord,
  12997. PtrOffset( FileRecord, Attribute ),
  12998. NewAttribute,
  12999. NULL,
  13000. NULL,
  13001. 0 );
  13002. //
  13003. // Now we want to move to the next attribute and remove it if we
  13004. // haven't already.
  13005. //
  13006. if (PassCount == 0) {
  13007. if (!NtfsLookupNextAttributeForScb( IrpContext, Scb, Context )) {
  13008. ASSERTMSG( "Could not find next attribute for Scb\n", FALSE );
  13009. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  13010. }
  13011. //
  13012. // We are pointing to the correct file record in this case.
  13013. //
  13014. } else {
  13015. RestoreContext = FALSE;
  13016. }
  13017. } else {
  13018. //
  13019. // Tell the delete routine to log the data and free the file record if
  13020. // possible but not to deallocate any clusters. Since there are no
  13021. // clusters we can save the overhead of calling DeleteAllocation.
  13022. //
  13023. NtfsDeleteAttributeRecord( IrpContext,
  13024. Scb->Fcb,
  13025. DELETE_LOG_OPERATION | DELETE_RELEASE_FILE_RECORD,
  13026. Context );
  13027. //
  13028. // If this is our first pass then move to the previous file record
  13029. //
  13030. if (PassCount == 0) {
  13031. NtfsCleanupAttributeContext( IrpContext, Context );
  13032. NtfsInitializeAttributeContext( Context );
  13033. NtfsLookupAttributeForScb( IrpContext, Scb, &NewStartVcn, Context );
  13034. }
  13035. }
  13036. if (PassCount == 1) { break; }
  13037. PassCount += 1;
  13038. }
  13039. try_exit: NOTHING;
  13040. //
  13041. // Restore the context if required.
  13042. //
  13043. if (RestoreContext) {
  13044. NtfsCleanupAttributeContext( IrpContext, Context );
  13045. NtfsInitializeAttributeContext( Context );
  13046. NtfsLookupAttributeForScb( IrpContext, Scb, &RestoreVcn, Context );
  13047. }
  13048. } finally {
  13049. DebugUnwind( NtfsMergeFileRecords );
  13050. if (NewAttribute != NULL) {
  13051. NtfsFreePool( NewAttribute );
  13052. }
  13053. }
  13054. return;
  13055. }
  13056. //
  13057. // Local support routine
  13058. //
  13059. NTSTATUS
  13060. NtfsCheckLocksInZeroRange (
  13061. IN PIRP_CONTEXT IrpContext,
  13062. IN PIRP Irp,
  13063. IN PSCB Scb,
  13064. IN PFILE_OBJECT FileObject,
  13065. IN PLONGLONG StartingOffset,
  13066. IN ULONG ByteCount
  13067. )
  13068. /*++
  13069. Routine Description:
  13070. This routine is called from ZeroRangeInStream to verify that we can modify the data
  13071. in the specified range. We check both oplocks and filelocks here.
  13072. Arguments:
  13073. Irp - This is the Irp for the request. We set the next stack location to look like
  13074. a write so that the file lock package has some context to use.
  13075. Scb - Scb for the stream being modified.
  13076. FileObject - File object used to originate the request.
  13077. StartingOffset - This is the offset for the start of the request.
  13078. ByteCount - This is the length of the current request.
  13079. Return Value:
  13080. NTSTATUS - STATUS_PENDING if the request is posted for an oplock operation, STATUS_SUCCESS
  13081. if the operation can proceed. Otherwise this is the status to fail the request with.
  13082. --*/
  13083. {
  13084. PIO_STACK_LOCATION IrpSp;
  13085. NTSTATUS Status;
  13086. PAGED_CODE();
  13087. Status = FsRtlCheckOplock( &Scb->ScbType.Data.Oplock,
  13088. Irp,
  13089. IrpContext,
  13090. NtfsOplockComplete,
  13091. NtfsPrePostIrp );
  13092. //
  13093. // Proceed if we have SUCCESS.
  13094. //
  13095. if (Status == STATUS_SUCCESS) {
  13096. //
  13097. // This oplock call can affect whether fast IO is possible.
  13098. // We may have broken an oplock to no oplock held. If the
  13099. // current state of the file is FastIoIsNotPossible then
  13100. // recheck the fast IO state.
  13101. //
  13102. if (Scb->Header.IsFastIoPossible == FastIoIsNotPossible) {
  13103. NtfsAcquireFsrtlHeader( Scb );
  13104. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  13105. NtfsReleaseFsrtlHeader( Scb );
  13106. }
  13107. //
  13108. // We have to check for write access according to the current
  13109. // state of the file locks.
  13110. //
  13111. if (Scb->ScbType.Data.FileLock != NULL) {
  13112. //
  13113. // Update the Irp to point to the next stack location.
  13114. //
  13115. try {
  13116. IoSetNextIrpStackLocation( Irp );
  13117. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  13118. IrpSp->MajorFunction = IRP_MJ_WRITE;
  13119. IrpSp->MinorFunction = 0;
  13120. IrpSp->Flags = 0;
  13121. IrpSp->Control = 0;
  13122. IrpSp->Parameters.Write.Length = ByteCount;
  13123. IrpSp->Parameters.Write.Key = 0;
  13124. IrpSp->Parameters.Write.ByteOffset.QuadPart = *StartingOffset;
  13125. IrpSp->DeviceObject = Scb->Vcb->Vpb->DeviceObject;
  13126. IrpSp->FileObject = FileObject;
  13127. if (!FsRtlCheckLockForWriteAccess( Scb->ScbType.Data.FileLock, Irp )) {
  13128. Status = STATUS_FILE_LOCK_CONFLICT;
  13129. }
  13130. //
  13131. // Always handle the exception initially in order to restore the Irp.
  13132. //
  13133. } except( EXCEPTION_EXECUTE_HANDLER ) {
  13134. //
  13135. // Zero out the current stack location and back up one position.
  13136. //
  13137. Status = GetExceptionCode();
  13138. }
  13139. //
  13140. // Restore the Irp to its previous state.
  13141. //
  13142. IoSkipCurrentIrpStackLocation( Irp );
  13143. //
  13144. // Raise any non-success status.
  13145. //
  13146. if (Status != STATUS_SUCCESS) {
  13147. NtfsRaiseStatus( IrpContext, Status, NULL, Scb->Fcb );
  13148. }
  13149. }
  13150. }
  13151. return Status;
  13152. }