Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

18307 lines
597 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 = BlockAlign( Scb->Header.FileSize.QuadPart, (LONG)Scb->CompressionUnit );
  420. }
  421. //
  422. // If allocated clusters are greater than file clusters, mark
  423. // the Scb to truncate on close.
  424. //
  425. if (AllocationClusters > FileClusters) {
  426. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  427. }
  428. }
  429. //
  430. // Update compression information if this is not an index
  431. //
  432. if (Scb->AttributeTypeCode != $INDEX_ALLOCATION) {
  433. Scb->AttributeFlags = AttrHeader->Flags;
  434. if (FlagOn( AttrHeader->Flags,
  435. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  436. //
  437. // For sparse files indicate CC should flush when they're mapped
  438. // to keep reservations accurate
  439. //
  440. if (FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_SPARSE )) {
  441. SetFlag( Scb->Header.Flags2, FSRTL_FLAG2_PURGE_WHEN_MAPPED );
  442. }
  443. //
  444. // Only support compression on data streams.
  445. //
  446. if ((Scb->AttributeTypeCode != $DATA) &&
  447. FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  448. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  449. Scb->CompressionUnit = 0;
  450. Scb->CompressionUnitShift = 0;
  451. ClearFlag( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK );
  452. } else {
  453. ASSERT( NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
  454. //
  455. // Do not try to infer whether we are writing compressed or not
  456. // if we are actively changing the compression state.
  457. //
  458. if (!FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE )) {
  459. SetFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  460. if (!FlagOn( AttrHeader->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  461. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  462. }
  463. }
  464. //
  465. // If the attribute is resident, then we will use our current
  466. // default.
  467. //
  468. if (Scb->CompressionUnit == 0) {
  469. Scb->CompressionUnit = BytesFromClusters( Scb->Vcb, 1 << NTFS_CLUSTERS_PER_COMPRESSION );
  470. Scb->CompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
  471. while (Scb->CompressionUnit > Scb->Vcb->SparseFileUnit) {
  472. Scb->CompressionUnit >>= 1;
  473. Scb->CompressionUnitShift -= 1;
  474. }
  475. }
  476. }
  477. } else {
  478. //
  479. // If this file is NOT compressed or sparse, the WRITE_COMPRESSED flag
  480. // has no reason to be ON, irrespective of the REALLOCATE_ON_WRITE flag.
  481. // If we don't clear the flag here unconditionally, we can end up with Scbs with
  482. // WRITE_COMPRESSED flags switched on, but CompressionUnits of 0.
  483. //
  484. ClearFlag( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED );
  485. //
  486. // Make sure compression unit is 0
  487. //
  488. Scb->CompressionUnit = 0;
  489. Scb->CompressionUnitShift = 0;
  490. }
  491. }
  492. //
  493. // If the compression unit is non-zero or this is a resident file
  494. // then set the flag in the common header for the Modified page writer.
  495. //
  496. NtfsAcquireFsrtlHeader( Scb );
  497. if (NodeType( Scb ) == NTFS_NTC_SCB_DATA) {
  498. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  499. } else {
  500. Scb->Header.IsFastIoPossible = FastIoIsNotPossible;
  501. }
  502. NtfsReleaseFsrtlHeader( Scb );
  503. //
  504. // Set the flag indicating this is the data attribute.
  505. //
  506. if (Scb->AttributeTypeCode == $DATA
  507. && Scb->AttributeName.Length == 0) {
  508. SetFlag( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  509. } else {
  510. ClearFlag( Scb->ScbState, SCB_STATE_UNNAMED_DATA );
  511. }
  512. SetFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED );
  513. if (NtfsIsExclusiveScb(Scb)) {
  514. NtfsSnapshotScb( IrpContext, Scb );
  515. }
  516. } finally {
  517. DebugUnwind( NtfsUpdateScbFromAttribute );
  518. //
  519. // Cleanup the attribute context.
  520. //
  521. if (CleanupAttrContext) {
  522. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  523. }
  524. DebugTrace( -1, Dbg, ("NtfsUpdateScbFromAttribute: Exit\n") );
  525. }
  526. return;
  527. }
  528. BOOLEAN
  529. NtfsUpdateFcbInfoFromDisk (
  530. IN PIRP_CONTEXT IrpContext,
  531. IN BOOLEAN LoadSecurity,
  532. IN OUT PFCB Fcb,
  533. OUT POLD_SCB_SNAPSHOT UnnamedDataSizes OPTIONAL
  534. )
  535. /*++
  536. Routine Description:
  537. This routine is called to update an Fcb from the on-disk attributes
  538. for a file. We read the standard information and ea information.
  539. The first one must be present, we raise if not. The other does not
  540. have to exist. If this is not a directory, then we also need the
  541. size of the unnamed data attribute.
  542. Arguments:
  543. LoadSecurity - Indicates if we should load the security for this file
  544. if not already present.
  545. Fcb - This is the Fcb to update.
  546. UnnamedDataSizes - If specified, then we store the details of the unnamed
  547. data attribute as we encounter it.
  548. Return Value:
  549. TRUE - if we updated the unnamedatasizes
  550. --*/
  551. {
  552. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  553. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  554. BOOLEAN FoundEntry;
  555. BOOLEAN CorruptDisk = FALSE;
  556. BOOLEAN UpdatedNamedDataSizes = FALSE;
  557. PBCB Bcb = NULL;
  558. PDUPLICATED_INFORMATION Info;
  559. PAGED_CODE();
  560. DebugTrace( +1, Dbg, ("NtfsUpdateFcbInfoFromDisk: Entered\n") );
  561. NtfsInitializeAttributeContext( &AttrContext );
  562. //
  563. // Use a try-finally to facilitate cleanup.
  564. //
  565. try {
  566. //
  567. // Look for standard information. This routine assumes it must be
  568. // the first attribute.
  569. //
  570. if (FoundEntry = NtfsLookupAttribute( IrpContext,
  571. Fcb,
  572. &Fcb->FileReference,
  573. &AttrContext )) {
  574. //
  575. // Verify that we found the standard information attribute.
  576. //
  577. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  578. if (AttributeHeader->TypeCode != $STANDARD_INFORMATION) {
  579. try_return( CorruptDisk = TRUE );
  580. }
  581. } else {
  582. try_return( CorruptDisk = TRUE );
  583. }
  584. Info = &Fcb->Info;
  585. //
  586. // Copy out the standard information values.
  587. //
  588. {
  589. PSTANDARD_INFORMATION StandardInformation;
  590. StandardInformation = (PSTANDARD_INFORMATION) NtfsAttributeValue( AttributeHeader );
  591. Info->CreationTime = StandardInformation->CreationTime;
  592. Info->LastModificationTime = StandardInformation->LastModificationTime;
  593. Info->LastChangeTime = StandardInformation->LastChangeTime;
  594. Info->LastAccessTime = StandardInformation->LastAccessTime;
  595. Info->FileAttributes = StandardInformation->FileAttributes;
  596. if (AttributeHeader->Form.Resident.ValueLength >=
  597. sizeof(STANDARD_INFORMATION)) {
  598. Fcb->OwnerId = StandardInformation->OwnerId;
  599. Fcb->SecurityId = StandardInformation->SecurityId;
  600. Fcb->Usn = StandardInformation->Usn;
  601. if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_USN_DELETE )) {
  602. Fcb->Usn = 0;
  603. }
  604. SetFlag(Fcb->FcbState, FCB_STATE_LARGE_STD_INFO);
  605. }
  606. }
  607. Fcb->CurrentLastAccess = Info->LastAccessTime;
  608. //
  609. // We initialize the fields that describe the EaSize or the tag of a reparse point.
  610. // ReparsePointTag is a ULONG that is the union of PackedEaSize and Reserved.
  611. //
  612. Info->ReparsePointTag = 0;
  613. //
  614. // We get the FILE_NAME_INDEX_PRESENT bit by reading the
  615. // file record.
  616. //
  617. if (FlagOn( NtfsContainingFileRecord( &AttrContext )->Flags,
  618. FILE_FILE_NAME_INDEX_PRESENT )) {
  619. SetFlag( Info->FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  620. } else {
  621. ClearFlag( Info->FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  622. }
  623. //
  624. // Ditto for the VIEW_INDEX_PRESENT bit.
  625. //
  626. if (FlagOn( NtfsContainingFileRecord( &AttrContext )->Flags,
  627. FILE_VIEW_INDEX_PRESENT )) {
  628. SetFlag( Info->FileAttributes, DUP_VIEW_INDEX_PRESENT );
  629. } else {
  630. ClearFlag( Info->FileAttributes, DUP_VIEW_INDEX_PRESENT );
  631. }
  632. //
  633. // We now walk through all of the filename attributes, counting the
  634. // number of non-8dot3-only links.
  635. //
  636. Fcb->TotalLinks =
  637. Fcb->LinkCount = 0;
  638. FoundEntry = NtfsLookupNextAttributeByCode( IrpContext,
  639. Fcb,
  640. $FILE_NAME,
  641. &AttrContext );
  642. while (FoundEntry) {
  643. PFILE_NAME FileName;
  644. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  645. if (AttributeHeader->TypeCode != $FILE_NAME) {
  646. break;
  647. }
  648. FileName = (PFILE_NAME) NtfsAttributeValue( AttributeHeader );
  649. //
  650. // We increment the count as long as this is not a 8.3 link
  651. // only.
  652. //
  653. if (FileName->Flags != FILE_NAME_DOS) {
  654. Fcb->LinkCount += 1;
  655. Fcb->TotalLinks += 1;
  656. }
  657. //
  658. // Now look for the next link.
  659. //
  660. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  661. Fcb,
  662. &AttrContext );
  663. }
  664. //
  665. // There better be at least one unless this is a system file.
  666. //
  667. if ((Fcb->LinkCount == 0) &&
  668. (NtfsSegmentNumber( &Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER)) {
  669. try_return( CorruptDisk = TRUE );
  670. }
  671. //
  672. // If we are to load the security and it is not already present we
  673. // find the security attribute.
  674. //
  675. if (LoadSecurity && Fcb->SharedSecurity == NULL) {
  676. //
  677. // We have two sources of security descriptors. First, we have
  678. // the SecurityId that is present in a large $STANDARD_INFORMATION.
  679. // The other case is where we don't have such a security Id and must
  680. // retrieve it from the $SECURITY_DESCRIPTOR attribute
  681. //
  682. // In the case where we have the Id, we load it from the volume
  683. // cache or index.
  684. //
  685. if (FlagOn( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO ) &&
  686. (Fcb->SecurityId != SECURITY_ID_INVALID) &&
  687. (Fcb->Vcb->SecurityDescriptorStream != NULL)) {
  688. ASSERT( Fcb->SharedSecurity == NULL );
  689. Fcb->SharedSecurity = NtfsCacheSharedSecurityBySecurityId( IrpContext,
  690. Fcb->Vcb,
  691. Fcb->SecurityId );
  692. ASSERT( Fcb->SharedSecurity != NULL );
  693. } else {
  694. PSECURITY_DESCRIPTOR SecurityDescriptor;
  695. ULONG SecurityDescriptorLength;
  696. //
  697. // We may have to walk forward to the security descriptor.
  698. //
  699. while (FoundEntry) {
  700. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  701. if (AttributeHeader->TypeCode == $SECURITY_DESCRIPTOR) {
  702. NtfsMapAttributeValue( IrpContext,
  703. Fcb,
  704. (PVOID *)&SecurityDescriptor,
  705. &SecurityDescriptorLength,
  706. &Bcb,
  707. &AttrContext );
  708. NtfsSetFcbSecurityFromDescriptor(
  709. IrpContext,
  710. Fcb,
  711. SecurityDescriptor,
  712. SecurityDescriptorLength,
  713. FALSE );
  714. //
  715. // If the security descriptor was resident then the Bcb field
  716. // in the attribute context was stored in the returned Bcb and
  717. // the Bcb in the attribute context was cleared. In that case
  718. // the resumption of the attribute search will fail because
  719. // this module using the Bcb field to determine if this
  720. // is the initial enumeration.
  721. //
  722. if (NtfsIsAttributeResident( AttributeHeader )) {
  723. NtfsFoundBcb( &AttrContext ) = Bcb;
  724. Bcb = NULL;
  725. }
  726. } else if (AttributeHeader->TypeCode > $SECURITY_DESCRIPTOR) {
  727. break;
  728. }
  729. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  730. Fcb,
  731. &AttrContext );
  732. }
  733. }
  734. }
  735. //
  736. // If this is not a directory, we need the file size.
  737. //
  738. if (!IsDirectory( Info ) && !IsViewIndex( Info )) {
  739. BOOLEAN FoundData = FALSE;
  740. //
  741. // Look for the unnamed data attribute.
  742. //
  743. while (FoundEntry) {
  744. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  745. if (AttributeHeader->TypeCode > $DATA) {
  746. break;
  747. }
  748. if ((AttributeHeader->TypeCode == $DATA) &&
  749. (AttributeHeader->NameLength == 0)) {
  750. //
  751. // This can vary depending whether the attribute is resident
  752. // or nonresident.
  753. //
  754. if (NtfsIsAttributeResident( AttributeHeader )) {
  755. //
  756. // Verify the resident value length.
  757. //
  758. if (AttributeHeader->Form.Resident.ValueLength > AttributeHeader->RecordLength - AttributeHeader->Form.Resident.ValueOffset) {
  759. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  760. }
  761. Info->AllocatedLength = AttributeHeader->Form.Resident.ValueLength;
  762. Info->FileSize = Info->AllocatedLength;
  763. ((ULONG)Info->AllocatedLength) = QuadAlign( (ULONG)(Info->AllocatedLength) );
  764. //
  765. // If the user passed in a ScbSnapshot, then copy the attribute
  766. // sizes to that. We use the trick of setting the low bit of the
  767. // attribute size to indicate a resident attribute.
  768. //
  769. if (ARGUMENT_PRESENT( UnnamedDataSizes )) {
  770. UnnamedDataSizes->TotalAllocated =
  771. UnnamedDataSizes->AllocationSize = Info->AllocatedLength;
  772. UnnamedDataSizes->FileSize = Info->FileSize;
  773. UnnamedDataSizes->ValidDataLength = Info->FileSize;
  774. UnnamedDataSizes->Resident = TRUE;
  775. UnnamedDataSizes->CompressionUnit = 0;
  776. UnnamedDataSizes->AttributeFlags = AttributeHeader->Flags;
  777. NtfsVerifySizesLongLong( UnnamedDataSizes );
  778. UpdatedNamedDataSizes = TRUE;
  779. }
  780. FoundData = TRUE;
  781. } else if (AttributeHeader->Form.Nonresident.LowestVcn == 0) {
  782. Info->AllocatedLength = AttributeHeader->Form.Nonresident.AllocatedLength;
  783. Info->FileSize = AttributeHeader->Form.Nonresident.FileSize;
  784. if (ARGUMENT_PRESENT( UnnamedDataSizes )) {
  785. UnnamedDataSizes->TotalAllocated =
  786. UnnamedDataSizes->AllocationSize = Info->AllocatedLength;
  787. UnnamedDataSizes->FileSize = Info->FileSize;
  788. UnnamedDataSizes->ValidDataLength = AttributeHeader->Form.Nonresident.ValidDataLength;
  789. UnnamedDataSizes->Resident = FALSE;
  790. UnnamedDataSizes->CompressionUnit = AttributeHeader->Form.Nonresident.CompressionUnit;
  791. NtfsVerifySizesLongLong( UnnamedDataSizes );
  792. //
  793. // Remember if it is compressed.
  794. //
  795. UnnamedDataSizes->AttributeFlags = AttributeHeader->Flags;
  796. UpdatedNamedDataSizes = TRUE;
  797. }
  798. if (FlagOn( AttributeHeader->Flags,
  799. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  800. Info->AllocatedLength = AttributeHeader->Form.Nonresident.TotalAllocated;
  801. if (ARGUMENT_PRESENT( UnnamedDataSizes )) {
  802. UnnamedDataSizes->TotalAllocated = Info->AllocatedLength;
  803. if (UnnamedDataSizes->TotalAllocated < 0) {
  804. UnnamedDataSizes->TotalAllocated = 0;
  805. } else if (UnnamedDataSizes->TotalAllocated > Info->AllocatedLength) {
  806. UnnamedDataSizes->TotalAllocated = Info->AllocatedLength;
  807. }
  808. }
  809. }
  810. FoundData = TRUE;
  811. }
  812. break;
  813. }
  814. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  815. Fcb,
  816. &AttrContext );
  817. }
  818. //
  819. // The following test is bad for the 5.0 support. Assume if someone is actually
  820. // trying to open the unnamed data attribute, that the right thing will happen.
  821. //
  822. //
  823. // if (!FoundData) {
  824. //
  825. // try_return( CorruptDisk = TRUE );
  826. // }
  827. } else {
  828. //
  829. // Since it is a directory, try to find the $INDEX_ROOT.
  830. //
  831. while (FoundEntry) {
  832. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  833. if (AttributeHeader->TypeCode > $INDEX_ROOT) {
  834. //
  835. // We thought this was a directory, yet it has now index
  836. // root. That's not a legal state to be in, so let's
  837. // take the corrupt disk path out of here.
  838. //
  839. ASSERT( FALSE );
  840. try_return( CorruptDisk = TRUE );
  841. break;
  842. }
  843. //
  844. // Look for encryption bit and store in Fcb.
  845. //
  846. if (AttributeHeader->TypeCode == $INDEX_ROOT) {
  847. if (FlagOn( AttributeHeader->Flags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  848. SetFlag( Fcb->FcbState, FCB_STATE_DIRECTORY_ENCRYPTED );
  849. }
  850. break;
  851. }
  852. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  853. Fcb,
  854. &AttrContext );
  855. }
  856. Info->AllocatedLength = 0;
  857. Info->FileSize = 0;
  858. }
  859. //
  860. // Now we look for a reparse point attribute. This one doesn't have to
  861. // be there. It may also not be resident.
  862. //
  863. while (FoundEntry) {
  864. PREPARSE_DATA_BUFFER ReparseInformation;
  865. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  866. if (AttributeHeader->TypeCode > $REPARSE_POINT) {
  867. break;
  868. } else if (AttributeHeader->TypeCode == $REPARSE_POINT) {
  869. if (NtfsIsAttributeResident( AttributeHeader )) {
  870. ReparseInformation = (PREPARSE_DATA_BUFFER) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  871. } else {
  872. ULONG Length;
  873. if (AttributeHeader->Form.Nonresident.FileSize > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) {
  874. NtfsRaiseStatus( IrpContext,STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  875. }
  876. NtfsMapAttributeValue( IrpContext,
  877. Fcb,
  878. (PVOID *)&ReparseInformation, // point to the value
  879. &Length,
  880. &Bcb,
  881. &AttrContext );
  882. }
  883. Info->ReparsePointTag = ReparseInformation->ReparseTag;
  884. break;
  885. }
  886. FoundEntry = NtfsLookupNextAttribute( IrpContext,
  887. Fcb,
  888. &AttrContext );
  889. }
  890. //
  891. // Now we look for an Ea information attribute. This one doesn't have to
  892. // be there.
  893. //
  894. while (FoundEntry) {
  895. PEA_INFORMATION EaInformation;
  896. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  897. if (AttributeHeader->TypeCode > $EA_INFORMATION) {
  898. break;
  899. } else if (AttributeHeader->TypeCode == $EA_INFORMATION) {
  900. EaInformation = (PEA_INFORMATION) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  901. Info->PackedEaSize = EaInformation->PackedEaSize;
  902. break;
  903. }
  904. FoundEntry = NtfsLookupNextAttributeByCode( IrpContext,
  905. Fcb,
  906. $EA_INFORMATION,
  907. &AttrContext );
  908. }
  909. //
  910. // Set the flag in the Fcb to indicate that we set these fields.
  911. //
  912. SetFlag( Fcb->FcbState, FCB_STATE_DUP_INITIALIZED );
  913. try_exit: NOTHING;
  914. } finally {
  915. DebugUnwind( NtfsUpdateFcbInfoFromDisk );
  916. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  917. NtfsUnpinBcb( IrpContext, &Bcb );
  918. DebugTrace( -1, Dbg, ("NtfsUpdateFcbInfoFromDisk: Exit\n") );
  919. }
  920. //
  921. // If we encountered a corrupt disk, we generate a popup and raise the file
  922. // corrupt error.
  923. //
  924. if (CorruptDisk) {
  925. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  926. }
  927. return UpdatedNamedDataSizes;
  928. }
  929. VOID
  930. NtfsCleanupAttributeContext (
  931. IN OUT PIRP_CONTEXT IrpContext,
  932. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT AttributeContext
  933. )
  934. /*++
  935. Routine Description:
  936. This routine is called to free any resources claimed within an enumeration
  937. context and to unpin mapped or pinned data.
  938. Arguments:
  939. IrpContext - context of the call
  940. AttributeContext - Pointer to the enumeration context to perform cleanup
  941. on.
  942. Return Value:
  943. None.
  944. --*/
  945. {
  946. UNREFERENCED_PARAMETER( IrpContext );
  947. PAGED_CODE();
  948. DebugTrace( +1, Dbg, ("NtfsCleanupAttributeContext\n") );
  949. //
  950. // TEMPCODE We need a call to cleanup any Scb's created.
  951. //
  952. //
  953. // Unpin any Bcb's pinned here.
  954. //
  955. NtfsUnpinBcb( IrpContext, &AttributeContext->FoundAttribute.Bcb );
  956. NtfsUnpinBcb( IrpContext, &AttributeContext->AttributeList.Bcb );
  957. NtfsUnpinBcb( IrpContext, &AttributeContext->AttributeList.NonresidentListBcb );
  958. //
  959. // Originally, we zeroed the entire context at this point. This is
  960. // wildly inefficient since the context is either deallocated soon thereafter
  961. // or is initialized again.
  962. //
  963. // RtlZeroMemory( AttributeContext, sizeof(ATTRIBUTE_ENUMERATION_CONTEXT) );
  964. //
  965. // Set entire contents to -1 (and reset Bcb's to NULL) to verify
  966. // that no one reuses this data structure
  967. #if DBG
  968. RtlFillMemory( AttributeContext, sizeof( *AttributeContext ), -1 );
  969. AttributeContext->FoundAttribute.Bcb = NULL;
  970. AttributeContext->AttributeList.Bcb = NULL;
  971. AttributeContext->AttributeList.NonresidentListBcb = NULL;
  972. #endif
  973. DebugTrace( -1, Dbg, ("NtfsCleanupAttributeContext -> VOID\n") );
  974. return;
  975. }
  976. BOOLEAN
  977. NtfsWriteFileSizes (
  978. IN PIRP_CONTEXT IrpContext,
  979. IN PSCB Scb,
  980. IN PLONGLONG ValidDataLength,
  981. IN BOOLEAN AdvanceOnly,
  982. IN BOOLEAN LogIt,
  983. IN BOOLEAN RollbackMemStructures
  984. )
  985. /*++
  986. Routine Description:
  987. This routine is called to modify the filesize and valid data size
  988. on the disk from the Scb.
  989. Arguments:
  990. Scb - Scb whose attribute is being modified.
  991. ValidDataLength - Supplies pointer to the new desired ValidDataLength
  992. AdvanceOnly - TRUE if the valid data length should be set only if
  993. greater than the current value on disk. FALSE if
  994. the valid data length should be set only if
  995. less than the current value on disk.
  996. LogIt - Indicates whether we should log this change.
  997. RollbackMemStructures - If true then there had better be snapshots to support doing this
  998. if not this indicates we're transferring persisted in memory
  999. changes to disk. I.e a the final writefilesizes at close time
  1000. or the check_attribute_sizes related calls
  1001. Return Value:
  1002. TRUE if a log record was written out
  1003. --*/
  1004. {
  1005. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1006. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  1007. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1008. NEW_ATTRIBUTE_SIZES OldAttributeSizes;
  1009. NEW_ATTRIBUTE_SIZES NewAttributeSizes;
  1010. ULONG LogRecordSize = SIZEOF_PARTIAL_ATTRIBUTE_SIZES;
  1011. BOOLEAN SparseAllocation = FALSE;
  1012. BOOLEAN UpdateMft = FALSE;
  1013. BOOLEAN Logged = FALSE;
  1014. PAGED_CODE();
  1015. UNREFERENCED_PARAMETER( RollbackMemStructures );
  1016. //
  1017. // Return immediately if the volume is locked unless we have grown the Mft or the Bitmap.
  1018. // In some cases the user can grow the Mft with the volume locked (i.e. add
  1019. // a Usn journal).
  1020. //
  1021. if (FlagOn( Scb->Vcb->VcbState, VCB_STATE_LOCKED ) &&
  1022. (Scb != Scb->Vcb->MftScb) && (Scb != Scb->Vcb->BitmapScb)) {
  1023. return Logged;
  1024. }
  1025. DebugTrace( +1, Dbg, ("NtfsWriteFileSizes: Entered\n") );
  1026. ASSERT( (Scb->ScbSnapshot != NULL) || !RollbackMemStructures );
  1027. //
  1028. // Use a try_finally to facilitate cleanup.
  1029. //
  1030. try {
  1031. //
  1032. // Find the attribute on the disk.
  1033. //
  1034. NtfsInitializeAttributeContext( &AttrContext );
  1035. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  1036. //
  1037. // Pull the pointers out of the attribute context.
  1038. //
  1039. FileRecord = NtfsContainingFileRecord( &AttrContext );
  1040. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  1041. //
  1042. // Check if this is a resident attribute, and if it is then we only
  1043. // want to assert that the file sizes match and then return to
  1044. // our caller
  1045. //
  1046. if (NtfsIsAttributeResident( AttributeHeader )) {
  1047. try_return( NOTHING );
  1048. }
  1049. //
  1050. // Remember the existing values.
  1051. //
  1052. OldAttributeSizes.TotalAllocated =
  1053. OldAttributeSizes.AllocationSize = AttributeHeader->Form.Nonresident.AllocatedLength;
  1054. OldAttributeSizes.ValidDataLength = AttributeHeader->Form.Nonresident.ValidDataLength;
  1055. OldAttributeSizes.FileSize = AttributeHeader->Form.Nonresident.FileSize;
  1056. NtfsVerifySizesLongLong( &OldAttributeSizes );
  1057. if (FlagOn( AttributeHeader->Flags,
  1058. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  1059. SparseAllocation = TRUE;
  1060. OldAttributeSizes.TotalAllocated = AttributeHeader->Form.Nonresident.TotalAllocated;
  1061. }
  1062. //
  1063. // Copy these values.
  1064. //
  1065. NewAttributeSizes = OldAttributeSizes;
  1066. //
  1067. // We only want to modify the sizes if the current thread owns the
  1068. // EOF. The exception is the TotalAllocated field for a compressed file.
  1069. // Otherwise this transaction might update the file size on disk at the
  1070. // same time an operation on EOF might roll back the Scb value. The
  1071. // two resulting numbers would conflict.
  1072. //
  1073. // Use the same test that NtfsRestoreScbSnapshots uses.
  1074. //
  1075. ASSERT( !RollbackMemStructures ||
  1076. !NtfsSnapshotFileSizesTest( IrpContext, Scb ) ||
  1077. (Scb->ScbSnapshot->OwnerIrpContext == IrpContext) ||
  1078. (Scb->ScbSnapshot->OwnerIrpContext == IrpContext->TopLevelIrpContext));
  1079. if (((Scb->ScbSnapshot != NULL) &&
  1080. ((Scb->ScbSnapshot->OwnerIrpContext == IrpContext) ||
  1081. (Scb->ScbSnapshot->OwnerIrpContext == IrpContext->TopLevelIrpContext))) ||
  1082. (!RollbackMemStructures && NtfsSnapshotFileSizesTest( IrpContext, Scb ))) {
  1083. //
  1084. // Check if we will be modifying the valid data length on
  1085. // disk. Don't acquire this for the paging file in case the
  1086. // current code block needs to be paged in.
  1087. //
  1088. if (!FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  1089. NtfsAcquireFsrtlHeader( Scb );
  1090. }
  1091. if ((AdvanceOnly
  1092. && (*ValidDataLength > OldAttributeSizes.ValidDataLength))
  1093. || (!AdvanceOnly
  1094. && (*ValidDataLength < OldAttributeSizes.ValidDataLength))) {
  1095. //
  1096. // Copy the valid data length into the new size structure.
  1097. //
  1098. NewAttributeSizes.ValidDataLength = *ValidDataLength;
  1099. UpdateMft = TRUE;
  1100. }
  1101. //
  1102. // Now check if we're modifying the filesize.
  1103. //
  1104. if (Scb->Header.FileSize.QuadPart != OldAttributeSizes.FileSize) {
  1105. NewAttributeSizes.FileSize = Scb->Header.FileSize.QuadPart;
  1106. UpdateMft = TRUE;
  1107. }
  1108. if (!FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  1109. NtfsReleaseFsrtlHeader(Scb);
  1110. }
  1111. //
  1112. // Finally, update the allocated length from the Scb if it is different.
  1113. //
  1114. if (Scb->Header.AllocationSize.QuadPart != AttributeHeader->Form.Nonresident.AllocatedLength) {
  1115. NewAttributeSizes.AllocationSize = Scb->Header.AllocationSize.QuadPart;
  1116. UpdateMft = TRUE;
  1117. }
  1118. }
  1119. //
  1120. // If this is compressed then check if totally allocated has changed.
  1121. //
  1122. if (SparseAllocation) {
  1123. LogRecordSize = SIZEOF_FULL_ATTRIBUTE_SIZES;
  1124. if (Scb->TotalAllocated != OldAttributeSizes.TotalAllocated) {
  1125. ASSERT( !RollbackMemStructures || (Scb->ScbSnapshot != NULL) );
  1126. NewAttributeSizes.TotalAllocated = Scb->TotalAllocated;
  1127. UpdateMft = TRUE;
  1128. }
  1129. }
  1130. //
  1131. // Continue on if we need to update the Mft.
  1132. //
  1133. if (UpdateMft) {
  1134. //
  1135. // Pin the attribute.
  1136. //
  1137. NtfsPinMappedAttribute( IrpContext,
  1138. Scb->Vcb,
  1139. &AttrContext );
  1140. AttributeHeader = NtfsFoundAttribute( &AttrContext );
  1141. if (NewAttributeSizes.ValidDataLength > NewAttributeSizes.FileSize) {
  1142. NewAttributeSizes.ValidDataLength = NewAttributeSizes.FileSize;
  1143. }
  1144. ASSERT(NewAttributeSizes.FileSize <= NewAttributeSizes.AllocationSize);
  1145. ASSERT(NewAttributeSizes.ValidDataLength <= NewAttributeSizes.AllocationSize);
  1146. NtfsVerifySizesLongLong( &NewAttributeSizes );
  1147. //
  1148. // Log this change to the attribute header.
  1149. //
  1150. if (LogIt) {
  1151. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  1152. Scb->Vcb->MftScb,
  1153. NtfsFoundBcb( &AttrContext ),
  1154. SetNewAttributeSizes,
  1155. &NewAttributeSizes,
  1156. LogRecordSize,
  1157. SetNewAttributeSizes,
  1158. &OldAttributeSizes,
  1159. LogRecordSize,
  1160. NtfsMftOffset( &AttrContext ),
  1161. PtrOffset( FileRecord, AttributeHeader ),
  1162. 0,
  1163. Scb->Vcb->BytesPerFileRecordSegment );
  1164. Logged = TRUE;
  1165. } else {
  1166. CcSetDirtyPinnedData( NtfsFoundBcb( &AttrContext ), NULL );
  1167. }
  1168. AttributeHeader->Form.Nonresident.AllocatedLength = NewAttributeSizes.AllocationSize;
  1169. AttributeHeader->Form.Nonresident.FileSize = NewAttributeSizes.FileSize;
  1170. AttributeHeader->Form.Nonresident.ValidDataLength = NewAttributeSizes.ValidDataLength;
  1171. //
  1172. // Don't modify the total allocated field unless there is an actual field for it.
  1173. //
  1174. if (SparseAllocation &&
  1175. ((AttributeHeader->NameOffset >= SIZEOF_FULL_NONRES_ATTR_HEADER) ||
  1176. ((AttributeHeader->NameOffset == 0) &&
  1177. (AttributeHeader->Form.Nonresident.MappingPairsOffset >= SIZEOF_FULL_NONRES_ATTR_HEADER)))) {
  1178. AttributeHeader->Form.Nonresident.TotalAllocated = NewAttributeSizes.TotalAllocated;
  1179. }
  1180. }
  1181. try_exit: NOTHING;
  1182. } finally {
  1183. DebugUnwind( NtfsWriteFileSizes );
  1184. //
  1185. // Cleanup the attribute context.
  1186. //
  1187. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1188. DebugTrace( -1, Dbg, ("NtfsWriteFileSizes: Exit\n") );
  1189. }
  1190. return Logged;
  1191. }
  1192. VOID
  1193. NtfsUpdateStandardInformation (
  1194. IN PIRP_CONTEXT IrpContext,
  1195. IN PFCB Fcb
  1196. )
  1197. /*++
  1198. Routine Description:
  1199. This routine is called to update the standard information attribute
  1200. for a file from the information in the Fcb. The fields being modified
  1201. are the time fields and the file attributes.
  1202. Arguments:
  1203. Fcb - Fcb for the file to modify.
  1204. Return Value:
  1205. None
  1206. --*/
  1207. {
  1208. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1209. STANDARD_INFORMATION StandardInformation;
  1210. ULONG Length;
  1211. PAGED_CODE();
  1212. DebugTrace( +1, Dbg, ("NtfsUpdateStandardInformation: Entered\n") );
  1213. //
  1214. // Return immediately if the volume is mounted readonly.
  1215. //
  1216. if (NtfsIsVolumeReadOnly( Fcb->Vcb )) {
  1217. return;
  1218. }
  1219. //
  1220. // Use a try-finally to cleanup the attribute context.
  1221. //
  1222. try {
  1223. //
  1224. // Initialize the context structure.
  1225. //
  1226. NtfsInitializeAttributeContext( &AttrContext );
  1227. //
  1228. // Locate the standard information, it must be there.
  1229. //
  1230. if (!NtfsLookupAttributeByCode( IrpContext,
  1231. Fcb,
  1232. &Fcb->FileReference,
  1233. $STANDARD_INFORMATION,
  1234. &AttrContext )) {
  1235. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  1236. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1237. }
  1238. Length = NtfsFoundAttribute( &AttrContext )->Form.Resident.ValueLength;
  1239. //
  1240. // Copy the existing standard information to our buffer.
  1241. //
  1242. RtlCopyMemory( &StandardInformation,
  1243. NtfsAttributeValue( NtfsFoundAttribute( &AttrContext )),
  1244. Length);
  1245. //
  1246. // Since we are updating standard information, make sure the last
  1247. // access time is up-to-date.
  1248. //
  1249. if (Fcb->Info.LastAccessTime != Fcb->CurrentLastAccess) {
  1250. Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
  1251. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
  1252. }
  1253. //
  1254. // No need to update last access standard information later.
  1255. //
  1256. ClearFlag( Fcb->InfoFlags, FCB_INFO_UPDATE_LAST_ACCESS );
  1257. //
  1258. // Change the relevant time fields.
  1259. //
  1260. StandardInformation.CreationTime = Fcb->Info.CreationTime;
  1261. StandardInformation.LastModificationTime = Fcb->Info.LastModificationTime;
  1262. StandardInformation.LastChangeTime = Fcb->Info.LastChangeTime;
  1263. StandardInformation.LastAccessTime = Fcb->Info.LastAccessTime;
  1264. StandardInformation.FileAttributes = Fcb->Info.FileAttributes;
  1265. //
  1266. // We clear the directory bit.
  1267. //
  1268. ClearFlag( StandardInformation.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  1269. //
  1270. // Fill in the new fields if necessary.
  1271. //
  1272. if (FlagOn(Fcb->FcbState, FCB_STATE_LARGE_STD_INFO)) {
  1273. StandardInformation.ClassId = 0;
  1274. StandardInformation.OwnerId = Fcb->OwnerId;
  1275. StandardInformation.SecurityId = Fcb->SecurityId;
  1276. StandardInformation.Usn = Fcb->Usn;
  1277. }
  1278. //
  1279. // Call to change the attribute value.
  1280. //
  1281. NtfsChangeAttributeValue( IrpContext,
  1282. Fcb,
  1283. 0,
  1284. &StandardInformation,
  1285. Length,
  1286. FALSE,
  1287. FALSE,
  1288. FALSE,
  1289. FALSE,
  1290. &AttrContext );
  1291. } finally {
  1292. DebugUnwind( NtfsUpdateStandadInformation );
  1293. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1294. DebugTrace( -1, Dbg, ("NtfsUpdateStandardInformation: Exit\n") );
  1295. }
  1296. return;
  1297. }
  1298. VOID
  1299. NtfsGrowStandardInformation (
  1300. IN PIRP_CONTEXT IrpContext,
  1301. IN PFCB Fcb
  1302. )
  1303. /*++
  1304. Routine Description:
  1305. This routine is called to grow and update the standard information
  1306. attribute for a file from the information in the Fcb.
  1307. Arguments:
  1308. Fcb - Fcb for the file to modify.
  1309. Return Value:
  1310. None
  1311. --*/
  1312. {
  1313. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1314. STANDARD_INFORMATION StandardInformation;
  1315. PAGED_CODE();
  1316. DebugTrace( +1, Dbg, ("NtfsGrowStandardInformation: Entered\n") );
  1317. //
  1318. // Use a try-finally to cleanup the attribute context.
  1319. //
  1320. try {
  1321. //
  1322. // Initialize the context structure.
  1323. //
  1324. NtfsInitializeAttributeContext( &AttrContext );
  1325. //
  1326. // Locate the standard information, it must be there.
  1327. //
  1328. if (!NtfsLookupAttributeByCode( IrpContext,
  1329. Fcb,
  1330. &Fcb->FileReference,
  1331. $STANDARD_INFORMATION,
  1332. &AttrContext )) {
  1333. DebugTrace( 0, Dbg, ("Can't find standard information\n") );
  1334. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1335. }
  1336. if (NtfsFoundAttribute( &AttrContext )->Form.Resident.ValueLength ==
  1337. SIZEOF_OLD_STANDARD_INFORMATION) {
  1338. //
  1339. // Copy the existing standard information to our buffer.
  1340. //
  1341. RtlCopyMemory( &StandardInformation,
  1342. NtfsAttributeValue( NtfsFoundAttribute( &AttrContext )),
  1343. SIZEOF_OLD_STANDARD_INFORMATION);
  1344. RtlZeroMemory((PCHAR) &StandardInformation +
  1345. SIZEOF_OLD_STANDARD_INFORMATION,
  1346. sizeof( STANDARD_INFORMATION) -
  1347. SIZEOF_OLD_STANDARD_INFORMATION);
  1348. }
  1349. //
  1350. // Since we are updating standard information, make sure the last
  1351. // access time is up-to-date.
  1352. //
  1353. if (Fcb->Info.LastAccessTime != Fcb->CurrentLastAccess) {
  1354. Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
  1355. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
  1356. }
  1357. //
  1358. // Change the relevant time fields.
  1359. //
  1360. StandardInformation.CreationTime = Fcb->Info.CreationTime;
  1361. StandardInformation.LastModificationTime = Fcb->Info.LastModificationTime;
  1362. StandardInformation.LastChangeTime = Fcb->Info.LastChangeTime;
  1363. StandardInformation.LastAccessTime = Fcb->Info.LastAccessTime;
  1364. StandardInformation.FileAttributes = Fcb->Info.FileAttributes;
  1365. //
  1366. // We clear the directory bit.
  1367. //
  1368. ClearFlag( StandardInformation.FileAttributes, DUP_FILE_NAME_INDEX_PRESENT );
  1369. //
  1370. // Fill in the new fields.
  1371. //
  1372. StandardInformation.ClassId = 0;
  1373. StandardInformation.OwnerId = Fcb->OwnerId;
  1374. StandardInformation.SecurityId = Fcb->SecurityId;
  1375. StandardInformation.Usn = Fcb->Usn;
  1376. //
  1377. // Call to change the attribute value.
  1378. //
  1379. NtfsChangeAttributeValue( IrpContext,
  1380. Fcb,
  1381. 0,
  1382. &StandardInformation,
  1383. sizeof( STANDARD_INFORMATION),
  1384. TRUE,
  1385. FALSE,
  1386. FALSE,
  1387. FALSE,
  1388. &AttrContext );
  1389. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  1390. SetFlag( Fcb->FcbState, FCB_STATE_LARGE_STD_INFO );
  1391. } finally {
  1392. DebugUnwind( NtfsGrowStandadInformation );
  1393. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1394. DebugTrace( -1, Dbg, ("NtfsGrowStandardInformation: Exit\n") );
  1395. }
  1396. return;
  1397. }
  1398. BOOLEAN
  1399. NtfsLookupEntry (
  1400. IN PIRP_CONTEXT IrpContext,
  1401. IN PSCB ParentScb,
  1402. IN BOOLEAN IgnoreCase,
  1403. IN OUT PUNICODE_STRING Name,
  1404. IN OUT PFILE_NAME *FileNameAttr,
  1405. IN OUT PUSHORT FileNameAttrLength,
  1406. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  1407. OUT PINDEX_ENTRY *IndexEntry,
  1408. OUT PBCB *IndexEntryBcb,
  1409. OUT PINDEX_CONTEXT IndexContext OPTIONAL
  1410. )
  1411. /*++
  1412. Routine Description:
  1413. This routine is called to look up a particular file name in a directory.
  1414. It takes a single component name and a parent Scb to search in.
  1415. To do the search, we need to construct a FILE_NAME attribute.
  1416. We use a reusable buffer to do this, to avoid constantly allocating
  1417. and deallocating pool. We try to keep this larger than we will ever need.
  1418. When we find a match on disk, we copy over the name we were called with so
  1419. we have a record of the actual case on the disk. In this way we can
  1420. be case perserving.
  1421. Arguments:
  1422. ParentScb - This is the Scb for the parent directory.
  1423. IgnoreCase - Indicates if we should ignore case while searching through
  1424. the index.
  1425. Name - This is the path component to search for. We will overwrite this
  1426. in place if a match is found.
  1427. FileNameAttr - Address of the buffer we will use to create the file name
  1428. attribute. We will free this buffer and allocate a new buffer
  1429. if needed.
  1430. FileNameAttrLength - This is the length of the FileNameAttr buffer above.
  1431. QuickIndex - If specified, supplies a pointer to a quik lookup structure
  1432. to be updated by this routine.
  1433. IndexEntry - Address to store the cache address of the matching entry.
  1434. IndexEntryBcb - Address to store the Bcb for the IndexEntry above.
  1435. IndexContext - Initialized IndexContext used for the lookup. Can be used
  1436. later when inserting an entry on a miss.
  1437. Return Value:
  1438. BOOLEAN - TRUE if a match was found, FALSE otherwise.
  1439. --*/
  1440. {
  1441. BOOLEAN FoundEntry;
  1442. USHORT Size;
  1443. PAGED_CODE();
  1444. DebugTrace( +1, Dbg, ("NtfsLookupEntry: Entered\n") );
  1445. //
  1446. // We compute the size of the buffer needed to build the filename
  1447. // attribute. If the current buffer is too small we deallocate it
  1448. // and allocate a new one. We always allocate twice the size we
  1449. // need in order to minimize the number of allocations.
  1450. //
  1451. Size = (USHORT)(sizeof( FILE_NAME ) + Name->Length - sizeof(WCHAR));
  1452. if (Size > *FileNameAttrLength) {
  1453. if (*FileNameAttr != NULL) {
  1454. DebugTrace( 0, Dbg, ("Deallocating previous file name attribute buffer\n") );
  1455. NtfsFreePool( *FileNameAttr );
  1456. *FileNameAttr = NULL;
  1457. }
  1458. *FileNameAttr = NtfsAllocatePool(PagedPool, Size << 1 );
  1459. *FileNameAttrLength = Size << 1;
  1460. }
  1461. //
  1462. // We build the filename attribute. If this operation is ignore case,
  1463. // we upcase the expression in the filename attribute.
  1464. //
  1465. NtfsBuildFileNameAttribute( IrpContext,
  1466. &ParentScb->Fcb->FileReference,
  1467. *Name,
  1468. 0,
  1469. *FileNameAttr );
  1470. //
  1471. // Now we call the index routine to perform the search.
  1472. //
  1473. FoundEntry = NtfsFindIndexEntry( IrpContext,
  1474. ParentScb,
  1475. *FileNameAttr,
  1476. IgnoreCase,
  1477. QuickIndex,
  1478. IndexEntryBcb,
  1479. IndexEntry,
  1480. IndexContext );
  1481. //
  1482. // We always restore the name in the filename attribute to the original
  1483. // name in case we upcased it in the lookup.
  1484. //
  1485. if (IgnoreCase) {
  1486. RtlCopyMemory( (*FileNameAttr)->FileName,
  1487. Name->Buffer,
  1488. Name->Length );
  1489. }
  1490. DebugTrace( -1, Dbg, ("NtfsLookupEntry: Exit -> %04x\n", FoundEntry) );
  1491. return FoundEntry;
  1492. }
  1493. VOID
  1494. NtfsCreateAttributeWithValue (
  1495. IN PIRP_CONTEXT IrpContext,
  1496. IN PFCB Fcb,
  1497. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  1498. IN PUNICODE_STRING AttributeName OPTIONAL,
  1499. IN PVOID Value OPTIONAL,
  1500. IN ULONG ValueLength,
  1501. IN USHORT AttributeFlags,
  1502. IN PFILE_REFERENCE WhereIndexed OPTIONAL,
  1503. IN BOOLEAN LogIt,
  1504. OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  1505. )
  1506. /*++
  1507. Routine Description:
  1508. This routine creates the specified attribute with the specified value,
  1509. and returns a description of it via the attribute context. If no
  1510. value is specified, then the attribute is created with the specified
  1511. number of zero bytes.
  1512. On successful return, it is up to the caller to clean up the attribute
  1513. context.
  1514. Arguments:
  1515. Fcb - Current file.
  1516. AttributeTypeCode - Type code of the attribute to create.
  1517. AttributeName - Optional name for attribute.
  1518. Value - Pointer to the buffer containing the desired attribute value,
  1519. or a NULL if zeros are desired.
  1520. ValueLength - Length of value in bytes.
  1521. AttributeFlags - Desired flags for the created attribute.
  1522. WhereIndexed - Optionally supplies the file reference to the file where
  1523. this attribute is indexed.
  1524. LogIt - Most callers should specify TRUE, to have the change logged. However,
  1525. we can specify FALSE if we are creating a new file record, and
  1526. will be logging the entire new file record.
  1527. Context - A handle to the created attribute. This must be cleaned up upon
  1528. return. Callers who may have made an attribute nonresident may
  1529. not count on accessing the created attribute via this context upon
  1530. return.
  1531. Return Value:
  1532. None.
  1533. --*/
  1534. {
  1535. UCHAR AttributeBuffer[SIZEOF_FULL_NONRES_ATTR_HEADER];
  1536. ULONG RecordOffset;
  1537. PATTRIBUTE_RECORD_HEADER Attribute;
  1538. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1539. ULONG SizeNeeded;
  1540. ULONG AttrSizeNeeded;
  1541. PVCB Vcb;
  1542. ULONG Passes = 0;
  1543. ASSERT_IRP_CONTEXT( IrpContext );
  1544. ASSERT_FCB( Fcb );
  1545. PAGED_CODE();
  1546. ASSERT( (AttributeFlags == 0) ||
  1547. (AttributeTypeCode == $INDEX_ROOT) ||
  1548. NtfsIsTypeCodeCompressible( AttributeTypeCode ));
  1549. Vcb = Fcb->Vcb;
  1550. DebugTrace( +1, Dbg, ("NtfsCreateAttributeWithValue\n") );
  1551. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  1552. DebugTrace( 0, Dbg, ("ValueLength = %08lx\n", ValueLength) );
  1553. //
  1554. // Clear out the invalid attribute flags for this volume.
  1555. //
  1556. ClearFlag( AttributeFlags, ~Vcb->AttributeFlagsMask );
  1557. //
  1558. // Calculate the size needed for this attribute
  1559. //
  1560. SizeNeeded = SIZEOF_RESIDENT_ATTRIBUTE_HEADER + QuadAlign( ValueLength ) +
  1561. (ARGUMENT_PRESENT( AttributeName ) ?
  1562. QuadAlign( AttributeName->Length ) : 0);
  1563. //
  1564. // Loop until we find all the space we need.
  1565. //
  1566. do {
  1567. //
  1568. // Reinitialize context if this is not the first pass.
  1569. //
  1570. if (Passes != 0) {
  1571. NtfsCleanupAttributeContext( IrpContext, Context );
  1572. NtfsInitializeAttributeContext( Context );
  1573. }
  1574. Passes += 1;
  1575. //
  1576. // Hope we will never have to loop thru this that many times.
  1577. // If so, we will have to bump up the threshold again or change
  1578. // the algorithm.
  1579. //
  1580. ASSERT( Passes < 6 );
  1581. //
  1582. // If the attribute is not indexed, then we will position to the
  1583. // insertion point by type code and name.
  1584. //
  1585. if (!ARGUMENT_PRESENT( WhereIndexed )) {
  1586. if (NtfsLookupAttributeByName( IrpContext,
  1587. Fcb,
  1588. &Fcb->FileReference,
  1589. AttributeTypeCode,
  1590. AttributeName,
  1591. NULL,
  1592. FALSE,
  1593. Context )) {
  1594. DebugTrace( 0, 0,
  1595. ("Nonindexed attribute already exists, TypeCode = %08lx\n",
  1596. AttributeTypeCode ));
  1597. ASSERTMSG("Nonindexed attribute already exists, About to raise corrupt ", FALSE);
  1598. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1599. }
  1600. //
  1601. // Check here if the attribute needs to be nonresident and if so just
  1602. // pass this off.
  1603. //
  1604. FileRecord = NtfsContainingFileRecord(Context);
  1605. if ((SizeNeeded > (FileRecord->BytesAvailable - FileRecord->FirstFreeByte)) &&
  1606. (SizeNeeded >= Vcb->BigEnoughToMove) &&
  1607. !FlagOn( NtfsGetAttributeDefinition( Vcb,
  1608. AttributeTypeCode)->Flags,
  1609. ATTRIBUTE_DEF_MUST_BE_RESIDENT)) {
  1610. NtfsCreateNonresidentWithValue( IrpContext,
  1611. Fcb,
  1612. AttributeTypeCode,
  1613. AttributeName,
  1614. Value,
  1615. ValueLength,
  1616. AttributeFlags,
  1617. FALSE,
  1618. NULL,
  1619. LogIt,
  1620. Context );
  1621. return;
  1622. }
  1623. //
  1624. // Otherwise, if the attribute is indexed, then we position by the
  1625. // attribute value.
  1626. //
  1627. } else {
  1628. ASSERT(ARGUMENT_PRESENT(Value));
  1629. if (NtfsLookupAttributeByValue( IrpContext,
  1630. Fcb,
  1631. &Fcb->FileReference,
  1632. AttributeTypeCode,
  1633. Value,
  1634. ValueLength,
  1635. Context )) {
  1636. DebugTrace( 0, 0,
  1637. ("Indexed attribute already exists, TypeCode = %08lx\n",
  1638. AttributeTypeCode ));
  1639. ASSERTMSG("Indexed attribute already exists, About to raise corrupt ", FALSE);
  1640. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1641. }
  1642. }
  1643. //
  1644. // If this attribute is being positioned in the base file record and
  1645. // there is an attribute list then we need to ask for enough space
  1646. // for the attribute list entry now.
  1647. //
  1648. FileRecord = NtfsContainingFileRecord( Context );
  1649. Attribute = NtfsFoundAttribute( Context );
  1650. AttrSizeNeeded = SizeNeeded;
  1651. if (Context->AttributeList.Bcb != NULL
  1652. && (ULONG_PTR) FileRecord <= (ULONG_PTR) Context->AttributeList.AttributeList
  1653. && (ULONG_PTR) Attribute >= (ULONG_PTR) Context->AttributeList.AttributeList) {
  1654. //
  1655. // If the attribute list is non-resident then add a fudge factor of
  1656. // 16 bytes for any new retrieval information.
  1657. //
  1658. if (NtfsIsAttributeResident( Context->AttributeList.AttributeList )) {
  1659. AttrSizeNeeded += QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  1660. + (ARGUMENT_PRESENT( AttributeName ) ?
  1661. (ULONG) AttributeName->Length :
  1662. sizeof( WCHAR )));
  1663. } else {
  1664. AttrSizeNeeded += 0x10;
  1665. }
  1666. }
  1667. //
  1668. // Ask for the space we need.
  1669. //
  1670. } while (!NtfsGetSpaceForAttribute( IrpContext, Fcb, AttrSizeNeeded, Context ));
  1671. //
  1672. // Now point to the file record and calculate the record offset where
  1673. // our attribute will go. And point to our local buffer.
  1674. //
  1675. RecordOffset = (ULONG)((PCHAR)NtfsFoundAttribute(Context) - (PCHAR)FileRecord);
  1676. Attribute = (PATTRIBUTE_RECORD_HEADER)AttributeBuffer;
  1677. if (RecordOffset >= Fcb->Vcb->BytesPerFileRecordSegment) {
  1678. ASSERTMSG("RecordOffset beyond FRS size, About to raise corrupt ", FALSE);
  1679. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  1680. }
  1681. RtlZeroMemory( Attribute, SIZEOF_RESIDENT_ATTRIBUTE_HEADER );
  1682. Attribute->TypeCode = AttributeTypeCode;
  1683. Attribute->RecordLength = SizeNeeded;
  1684. Attribute->FormCode = RESIDENT_FORM;
  1685. if (ARGUMENT_PRESENT(AttributeName)) {
  1686. ASSERT( AttributeName->Length <= 0x1FF );
  1687. Attribute->NameLength = (UCHAR)(AttributeName->Length / sizeof(WCHAR));
  1688. Attribute->NameOffset = (USHORT)SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  1689. }
  1690. Attribute->Flags = AttributeFlags;
  1691. Attribute->Instance = FileRecord->NextAttributeInstance;
  1692. //
  1693. // If someone repeatedly adds and removes attributes from a file record we could
  1694. // hit a case where the sequence number will overflow. In this case we
  1695. // want to scan the file record and find an earlier free instance number.
  1696. //
  1697. if (Attribute->Instance > NTFS_CHECK_INSTANCE_ROLLOVER) {
  1698. Attribute->Instance = NtfsScanForFreeInstance( IrpContext, Vcb, FileRecord );
  1699. }
  1700. Attribute->Form.Resident.ValueLength = ValueLength;
  1701. Attribute->Form.Resident.ValueOffset =
  1702. (USHORT)(SIZEOF_RESIDENT_ATTRIBUTE_HEADER +
  1703. QuadAlign( Attribute->NameLength << 1) );
  1704. //
  1705. // If this attribute is indexed, then we have to set the right flag
  1706. // and update the file record reference count.
  1707. //
  1708. if (ARGUMENT_PRESENT(WhereIndexed)) {
  1709. Attribute->Form.Resident.ResidentFlags = RESIDENT_FORM_INDEXED;
  1710. }
  1711. //
  1712. // Now we will actually create the attribute in place, so that we
  1713. // save copying everything twice, and can point to the final image
  1714. // for the log write below.
  1715. //
  1716. NtfsRestartInsertAttribute( IrpContext,
  1717. FileRecord,
  1718. RecordOffset,
  1719. Attribute,
  1720. AttributeName,
  1721. Value,
  1722. ValueLength );
  1723. //
  1724. // Finally, log the creation of this attribute
  1725. //
  1726. if (LogIt) {
  1727. //
  1728. // We have actually created the attribute above, but the write
  1729. // log below could fail. The reason we did the create already
  1730. // was to avoid having to allocate pool and copy everything
  1731. // twice (header, name and value). Our normal error recovery
  1732. // just recovers from the log file. But if we fail to write
  1733. // the log, we have to remove this attribute by hand, and
  1734. // raise the condition again.
  1735. //
  1736. try {
  1737. FileRecord->Lsn =
  1738. NtfsWriteLog( IrpContext,
  1739. Vcb->MftScb,
  1740. NtfsFoundBcb(Context),
  1741. CreateAttribute,
  1742. Add2Ptr(FileRecord, RecordOffset),
  1743. Attribute->RecordLength,
  1744. DeleteAttribute,
  1745. NULL,
  1746. 0,
  1747. NtfsMftOffset( Context ),
  1748. RecordOffset,
  1749. 0,
  1750. Vcb->BytesPerFileRecordSegment );
  1751. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  1752. NtfsRestartRemoveAttribute( IrpContext, FileRecord, RecordOffset );
  1753. NtfsRaiseStatus( IrpContext, GetExceptionCode(), NULL, NULL );
  1754. }
  1755. }
  1756. //
  1757. // Now add it to the attribute list if necessary
  1758. //
  1759. if (Context->AttributeList.Bcb != NULL) {
  1760. MFT_SEGMENT_REFERENCE SegmentReference;
  1761. *(PLONGLONG)&SegmentReference = LlFileRecordsFromBytes( Vcb, NtfsMftOffset( Context ));
  1762. SegmentReference.SequenceNumber = FileRecord->SequenceNumber;
  1763. NtfsAddToAttributeList( IrpContext, Fcb, SegmentReference, Context );
  1764. }
  1765. DebugTrace( -1, Dbg, ("NtfsCreateAttributeWithValue -> VOID\n") );
  1766. return;
  1767. }
  1768. VOID
  1769. NtfsCreateNonresidentWithValue (
  1770. IN PIRP_CONTEXT IrpContext,
  1771. IN PFCB Fcb,
  1772. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  1773. IN PUNICODE_STRING AttributeName OPTIONAL,
  1774. IN PVOID Value OPTIONAL,
  1775. IN ULONG ValueLength,
  1776. IN USHORT AttributeFlags,
  1777. IN BOOLEAN WriteClusters,
  1778. IN PSCB ThisScb OPTIONAL,
  1779. IN BOOLEAN LogIt,
  1780. IN PATTRIBUTE_ENUMERATION_CONTEXT Context
  1781. )
  1782. /*++
  1783. Routine Description:
  1784. This routine creates the specified nonresident attribute with the specified
  1785. value, and returns a description of it via the attribute context. If no
  1786. value is specified, then the attribute is created with the specified
  1787. number of zero bytes.
  1788. On successful return, it is up to the caller to clean up the attribute
  1789. context.
  1790. Arguments:
  1791. Fcb - Current file.
  1792. AttributeTypeCode - Type code of the attribute to create.
  1793. AttributeName - Optional name for attribute.
  1794. Value - Pointer to the buffer containing the desired attribute value,
  1795. or a NULL if zeros are desired.
  1796. ValueLength - Length of value in bytes.
  1797. AttributeFlags - Desired flags for the created attribute.
  1798. WriteClusters - if supplied as TRUE, then we cannot write the data into the
  1799. cache but must write the clusters directly to the disk. The value buffer
  1800. in this case must be quad-aligned and a multiple of cluster size in size.
  1801. If TRUE it also means we are being called during the NtfsConvertToNonresident
  1802. path. We need to set a flag in the Scb in that case.
  1803. ThisScb - If present, this is the Scb to use for the create. It also indicates
  1804. that this call is from convert to non-resident.
  1805. LogIt - Most callers should specify TRUE, to have the change logged. However,
  1806. we can specify FALSE if we are creating a new file record, and
  1807. will be logging the entire new file record.
  1808. Context - This is the location to create the new attribute.
  1809. Return Value:
  1810. None.
  1811. --*/
  1812. {
  1813. PSCB Scb;
  1814. BOOLEAN ReturnedExistingScb;
  1815. UNICODE_STRING LocalName;
  1816. PVCB Vcb = Fcb->Vcb;
  1817. BOOLEAN LogNonresidentToo;
  1818. BOOLEAN AdvanceOnly;
  1819. PAGED_CODE();
  1820. DebugTrace( +1, Dbg, ("NtfsCreateNonresidentWithValue\n") );
  1821. //
  1822. // When we're updating the attribute definition table, we want that operation
  1823. // to be logged, even though it's a $DATA attribute.
  1824. //
  1825. //
  1826. // TODO: post nt5.1 change chkdsk so it can recognize an attrdef table with $EA
  1827. // log non-resident
  1828. //
  1829. AdvanceOnly =
  1830. LogNonresidentToo = (BooleanFlagOn( NtfsGetAttributeDefinition( Vcb, AttributeTypeCode )->Flags,
  1831. ATTRIBUTE_DEF_LOG_NONRESIDENT) ||
  1832. NtfsEqualMftRef( &Fcb->FileReference, &AttrDefFileReference ) ||
  1833. ($EA == AttributeTypeCode) );
  1834. ASSERT( (AttributeFlags == 0) || NtfsIsTypeCodeCompressible( AttributeTypeCode ));
  1835. //
  1836. // Clear out the invalid attribute flags for this volume.
  1837. //
  1838. AttributeFlags &= Vcb->AttributeFlagsMask;
  1839. if (ARGUMENT_PRESENT(AttributeName)) {
  1840. LocalName = *AttributeName;
  1841. } else {
  1842. LocalName.Length = LocalName.MaximumLength = 0;
  1843. LocalName.Buffer = NULL;
  1844. }
  1845. if (ARGUMENT_PRESENT( ThisScb )) {
  1846. Scb = ThisScb;
  1847. ReturnedExistingScb = TRUE;
  1848. } else {
  1849. Scb = NtfsCreateScb( IrpContext,
  1850. Fcb,
  1851. AttributeTypeCode,
  1852. &LocalName,
  1853. FALSE,
  1854. &ReturnedExistingScb );
  1855. //
  1856. // An attribute has gone away but the Scb hasn't left yet.
  1857. // Also mark the header as unitialized.
  1858. //
  1859. ClearFlag( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED |
  1860. SCB_STATE_ATTRIBUTE_RESIDENT |
  1861. SCB_STATE_FILE_SIZE_LOADED );
  1862. //
  1863. // Set a flag in the Scb to indicate that we are converting to non-resident.
  1864. //
  1865. if (WriteClusters) { SetFlag( Scb->ScbState, SCB_STATE_CONVERT_UNDERWAY ); }
  1866. }
  1867. //
  1868. // Allocate the record for the size we need.
  1869. //
  1870. NtfsAllocateAttribute( IrpContext,
  1871. Scb,
  1872. AttributeTypeCode,
  1873. AttributeName,
  1874. AttributeFlags,
  1875. TRUE,
  1876. LogIt,
  1877. (LONGLONG) ValueLength,
  1878. Context );
  1879. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  1880. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  1881. //
  1882. // We need to be careful here, if this call is due to MM creating a
  1883. // section, we don't want to call into the cache manager or we
  1884. // will deadlock on the create section call.
  1885. //
  1886. if (!WriteClusters && !ARGUMENT_PRESENT( ThisScb )) {
  1887. //
  1888. // This call will initialize a stream for use below.
  1889. //
  1890. NtfsCreateInternalAttributeStream( IrpContext,
  1891. Scb,
  1892. TRUE,
  1893. &NtfsInternalUseFile[CREATENONRESIDENTWITHVALUE_FILE_NUMBER] );
  1894. }
  1895. //
  1896. // Now, write in the data.
  1897. //
  1898. Scb->Header.FileSize.QuadPart = ValueLength;
  1899. if ((ARGUMENT_PRESENT( Value )) && (ValueLength != 0)) {
  1900. if (LogNonresidentToo || !WriteClusters) {
  1901. ULONG BytesThisPage;
  1902. PVOID Buffer;
  1903. PBCB Bcb = NULL;
  1904. LONGLONG CurrentFileOffset = 0;
  1905. ULONG RemainingBytes = ValueLength;
  1906. PVOID CurrentValue = Value;
  1907. //
  1908. // While there is more to write, pin the next page and
  1909. // write a log record.
  1910. //
  1911. try {
  1912. CC_FILE_SIZES FileSizes;
  1913. //
  1914. // Call the Cache Manager to truncate and reestablish the FileSize,
  1915. // so that we are guaranteed to get a valid data length call when
  1916. // the data goes out. Otherwise he will likely think he does not
  1917. // have to call us.
  1918. //
  1919. RtlCopyMemory( &FileSizes, &Scb->Header.AllocationSize, sizeof( CC_FILE_SIZES ));
  1920. FileSizes.FileSize.QuadPart = 0;
  1921. CcSetFileSizes( Scb->FileObject, &FileSizes );
  1922. CcSetFileSizes( Scb->FileObject,
  1923. (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
  1924. while (RemainingBytes) {
  1925. BytesThisPage = (RemainingBytes < PAGE_SIZE ? RemainingBytes : PAGE_SIZE);
  1926. NtfsUnpinBcb( IrpContext, &Bcb );
  1927. NtfsPinStream( IrpContext,
  1928. Scb,
  1929. CurrentFileOffset,
  1930. BytesThisPage,
  1931. &Bcb,
  1932. &Buffer );
  1933. if (ARGUMENT_PRESENT(ThisScb)) {
  1934. //
  1935. // Set the address range modified so that the data will get
  1936. // written to its new "home".
  1937. //
  1938. MmSetAddressRangeModified( Buffer, BytesThisPage );
  1939. } else {
  1940. RtlCopyMemory( Buffer, CurrentValue, BytesThisPage );
  1941. }
  1942. if (LogNonresidentToo) {
  1943. (VOID)
  1944. NtfsWriteLog( IrpContext,
  1945. Scb,
  1946. Bcb,
  1947. UpdateNonresidentValue,
  1948. Buffer,
  1949. BytesThisPage,
  1950. Noop,
  1951. NULL,
  1952. 0,
  1953. CurrentFileOffset,
  1954. 0,
  1955. 0,
  1956. BytesThisPage );
  1957. } else {
  1958. CcSetDirtyPinnedData( Bcb, NULL );
  1959. }
  1960. RemainingBytes -= BytesThisPage;
  1961. CurrentValue = (PVOID) Add2Ptr( CurrentValue, BytesThisPage );
  1962. (ULONG)CurrentFileOffset += BytesThisPage;
  1963. }
  1964. } finally {
  1965. NtfsUnpinBcb( IrpContext, &Bcb );
  1966. }
  1967. } else {
  1968. //
  1969. // We are going to write the old data directly to disk.
  1970. //
  1971. NtfsWriteClusters( IrpContext,
  1972. Vcb,
  1973. Scb,
  1974. (LONGLONG)0,
  1975. Value,
  1976. ClustersFromBytes( Vcb, ValueLength ));
  1977. //
  1978. // Be sure to note that the data is actually on disk.
  1979. //
  1980. AdvanceOnly = TRUE;
  1981. }
  1982. }
  1983. //
  1984. // We need to maintain the file size and valid data length in the
  1985. // Scb and attribute record. For this attribute, the valid data
  1986. // size and the file size are now the value length.
  1987. //
  1988. Scb->Header.ValidDataLength = Scb->Header.FileSize;
  1989. NtfsVerifySizes( &Scb->Header );
  1990. NtfsWriteFileSizes( IrpContext,
  1991. Scb,
  1992. &Scb->Header.ValidDataLength.QuadPart,
  1993. AdvanceOnly,
  1994. LogIt,
  1995. FALSE );
  1996. if (!WriteClusters) {
  1997. //
  1998. // Let the cache manager know the new size for this attribute.
  1999. //
  2000. CcSetFileSizes( Scb->FileObject, (PCC_FILE_SIZES)&Scb->Header.AllocationSize );
  2001. }
  2002. //
  2003. // If this is the unnamed data attribute, we need to mark this
  2004. // change in the Fcb.
  2005. //
  2006. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  2007. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  2008. Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
  2009. SetFlag( Fcb->InfoFlags,
  2010. (FCB_INFO_CHANGED_ALLOC_SIZE | FCB_INFO_CHANGED_FILE_SIZE) );
  2011. }
  2012. DebugTrace( -1, Dbg, ("NtfsCreateNonresidentWithValue -> VOID\n") );
  2013. }
  2014. VOID
  2015. NtfsMapAttributeValue (
  2016. IN PIRP_CONTEXT IrpContext,
  2017. IN PFCB Fcb,
  2018. OUT PVOID *Buffer,
  2019. OUT PULONG Length,
  2020. OUT PBCB *Bcb,
  2021. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  2022. )
  2023. /*++
  2024. Routine Description:
  2025. This routine may be called to map an entire attribute value. It works
  2026. whether the attribute is resident or nonresident. It is intended for
  2027. general handling of system-defined attributes which are small to medium
  2028. in size, i.e. 0-64KB. This routine will not work for attributes larger
  2029. than the Cache Manager's virtual address granularity (currently 256KB),
  2030. and this will be detected by the Cache Manager who will raise an error.
  2031. Note that this routine only maps the data for read-only access. To modify
  2032. the data, the caller must call NtfsChangeAttributeValue AFTER UNPINNING
  2033. THE BCB (IF THE SIZE IS CHANGING) returned from this routine.
  2034. Arguments:
  2035. Fcb - Current file.
  2036. Buffer - returns a pointer to the mapped attribute value.
  2037. Length - returns the attribute value length in bytes.
  2038. Bcb - Returns a Bcb which must be unpinned when done with the data, and
  2039. before modifying the attribute value with a size change.
  2040. Context - Attribute Context positioned at the attribute to change.
  2041. Return Value:
  2042. None.
  2043. --*/
  2044. {
  2045. PATTRIBUTE_RECORD_HEADER Attribute;
  2046. PSCB Scb;
  2047. UNICODE_STRING AttributeName;
  2048. BOOLEAN ReturnedExistingScb;
  2049. ASSERT_IRP_CONTEXT( IrpContext );
  2050. ASSERT_FCB( Fcb );
  2051. PAGED_CODE();
  2052. DebugTrace( +1, Dbg, ("NtfsMapAttributeValue\n") );
  2053. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  2054. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  2055. Attribute = NtfsFoundAttribute(Context);
  2056. //
  2057. // For the resident case, everything we need is in the
  2058. // attribute enumeration context.
  2059. //
  2060. if (NtfsIsAttributeResident(Attribute)) {
  2061. *Buffer = NtfsAttributeValue( Attribute );
  2062. *Length = Attribute->Form.Resident.ValueLength;
  2063. *Bcb = NtfsFoundBcb(Context);
  2064. NtfsFoundBcb(Context) = NULL;
  2065. DebugTrace( 0, Dbg, ("Buffer < %08lx\n", *Buffer) );
  2066. DebugTrace( 0, Dbg, ("Length < %08lx\n", *Length) );
  2067. DebugTrace( 0, Dbg, ("Bcb < %08lx\n", *Bcb) );
  2068. DebugTrace( -1, Dbg, ("NtfsMapAttributeValue -> VOID\n") );
  2069. return;
  2070. }
  2071. //
  2072. // Otherwise, this is a nonresident attribute. First create
  2073. // the Scb and stream. Note we do not use any try-finally
  2074. // around this because we currently expect cleanup to get
  2075. // rid of these streams.
  2076. //
  2077. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  2078. Scb = NtfsCreateScb( IrpContext,
  2079. Fcb,
  2080. Attribute->TypeCode,
  2081. &AttributeName,
  2082. FALSE,
  2083. &ReturnedExistingScb );
  2084. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  2085. NtfsUpdateScbFromAttribute( IrpContext, Scb, Attribute );
  2086. }
  2087. NtfsCreateInternalAttributeStream( IrpContext,
  2088. Scb,
  2089. FALSE,
  2090. &NtfsInternalUseFile[MAPATTRIBUTEVALUE_FILE_NUMBER] );
  2091. //
  2092. // Now just try to map the whole thing. Count on the Cache Manager
  2093. // to complain if the attribute is too big to map all at once.
  2094. //
  2095. NtfsMapStream( IrpContext,
  2096. Scb,
  2097. (LONGLONG)0,
  2098. ((ULONG)Attribute->Form.Nonresident.FileSize),
  2099. Bcb,
  2100. Buffer );
  2101. *Length = ((ULONG)Attribute->Form.Nonresident.FileSize);
  2102. DebugTrace( 0, Dbg, ("Buffer < %08lx\n", *Buffer) );
  2103. DebugTrace( 0, Dbg, ("Length < %08lx\n", *Length) );
  2104. DebugTrace( 0, Dbg, ("Bcb < %08lx\n", *Bcb) );
  2105. DebugTrace( -1, Dbg, ("NtfsMapAttributeValue -> VOID\n") );
  2106. }
  2107. VOID
  2108. NtfsChangeAttributeValue (
  2109. IN PIRP_CONTEXT IrpContext,
  2110. IN PFCB Fcb,
  2111. IN ULONG ValueOffset,
  2112. IN PVOID Value OPTIONAL,
  2113. IN ULONG ValueLength,
  2114. IN BOOLEAN SetNewLength,
  2115. IN BOOLEAN LogNonresidentToo,
  2116. IN BOOLEAN CreateSectionUnderway,
  2117. IN BOOLEAN PreserveContext,
  2118. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  2119. )
  2120. /*++
  2121. Routine Description:
  2122. This routine changes the value of the specified attribute, optionally
  2123. changing its size.
  2124. The caller specifies the attribute to be changed via the attribute context,
  2125. and must be prepared to clean up this context no matter how this routine
  2126. returns.
  2127. There are three byte ranges of interest for this routine. The first is
  2128. existing bytes to be perserved at the beginning of the attribute. It
  2129. begins a byte 0 and extends to the point where the attribute is being
  2130. changed or the current end of the attribute, which ever is smaller.
  2131. The second is the range of bytes which needs to be zeroed if the modified
  2132. bytes begin past the current end of the file. This range will be
  2133. of length 0 if the modified range begins within the current range
  2134. of bytes for the attribute. The final range is the modified byte range.
  2135. This is zeroed if no value pointer was specified.
  2136. Ranges of zero bytes at the end of the attribute can be represented in
  2137. non-resident attributes by a valid data length set to the beginning
  2138. of what would be zero bytes.
  2139. The following pictures illustrates these ranges when we writing data
  2140. beyond the current end of the file.
  2141. Current attribute
  2142. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2143. Value
  2144. VVVVVVVVVVVVVVVVVVVVVVVV
  2145. Byte range to save
  2146. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2147. Byte range to zero
  2148. 0000
  2149. Resulting attribute
  2150. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ0000VVVVVVVVVVVVVVVVVVVVVVVV
  2151. The following picture illustrates these ranges when we writing data
  2152. which begins at or before the current end of the file.
  2153. Current attribute
  2154. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2155. Value
  2156. VVVVVVVVVVVVVVVVVVVVVVVV
  2157. Byte range to save
  2158. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2159. Byte range to zero (None)
  2160. Resulting attribute
  2161. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZVVVVVVVVVVVVVVVVVVVVVVVV
  2162. The following picture illustrates these ranges when we writing data
  2163. totally within the current range of the file without setting
  2164. a new size.
  2165. Current attribute
  2166. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2167. Value
  2168. VVVVVVVVVVVVVVVVVVVVVVVV
  2169. Byte range to save (Save the whole range and then write over it)
  2170. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2171. Byte range to zero (None)
  2172. Resulting attribute
  2173. ZZZZVVVVVVVVVVVVVVVVVVVVVVVVZZZZ
  2174. The following picture illustrates these ranges when we writing data
  2175. totally within the current range of the file while setting
  2176. a new size.
  2177. Current attribute
  2178. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
  2179. Value
  2180. VVVVVVVVVVVVVVVVVVVVVVVV
  2181. Byte range to save (Only save the beginning)
  2182. ZZZZ
  2183. Byte range to zero (None)
  2184. Resulting attribute
  2185. ZZZZVVVVVVVVVVVVVVVVVVVVVVVV
  2186. Any of the 'V' values above will be replaced by zeroes if the 'Value'
  2187. parameter is not passed in.
  2188. Arguments:
  2189. Fcb - Current file.
  2190. ValueOffset - Byte offset within the attribute at which the value change is
  2191. to begin.
  2192. Value - Pointer to the buffer containing the new value, if present. Otherwise
  2193. zeroes are desired.
  2194. ValueLength - Length of the value in the above buffer.
  2195. SetNewLength - FALSE if the size of the value is not changing, or TRUE if
  2196. the value length should be changed to ValueOffset + ValueLength.
  2197. LogNonresidentToo - supplies TRUE if the update should be logged even if
  2198. the attribute is nonresident (such as for the
  2199. SECURITY_DESCRIPTOR).
  2200. CreateSectionUnderway - if supplied as TRUE, then to the best of the caller's
  2201. knowledge, an MM Create Section could be underway,
  2202. which means that we cannot initiate caching on
  2203. this attribute, as that could cause deadlock. The
  2204. value buffer in this case must be quad-aligned and
  2205. a multiple of cluster size in size.
  2206. PreserveContext - Indicates if we need to lookup the attribute in case it
  2207. might move.
  2208. Context - Attribute Context positioned at the attribute to change.
  2209. Return Value:
  2210. None.
  2211. --*/
  2212. {
  2213. PATTRIBUTE_RECORD_HEADER Attribute;
  2214. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  2215. ATTRIBUTE_TYPE_CODE AttributeTypeCode;
  2216. UNICODE_STRING AttributeName;
  2217. ULONG NewSize;
  2218. PVCB Vcb;
  2219. BOOLEAN ReturnedExistingScb;
  2220. BOOLEAN GoToNonResident = FALSE;
  2221. PVOID Buffer;
  2222. ULONG CurrentLength;
  2223. LONG SizeChange, QuadSizeChange;
  2224. ULONG RecordOffset;
  2225. ULONG ZeroLength = 0;
  2226. ULONG UnchangedSize = 0;
  2227. PBCB Bcb = NULL;
  2228. PSCB Scb = NULL;
  2229. PVOID SaveBuffer = NULL;
  2230. PVOID CopyInputBuffer = NULL;
  2231. WCHAR NameBuffer[8];
  2232. UNICODE_STRING SavedName;
  2233. ATTRIBUTE_TYPE_CODE TypeCode;
  2234. ASSERT_IRP_CONTEXT( IrpContext );
  2235. ASSERT_FCB( Fcb );
  2236. Vcb = Fcb->Vcb;
  2237. PAGED_CODE();
  2238. DebugTrace( +1, Dbg, ("NtfsChangeAttributeValue\n") );
  2239. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  2240. DebugTrace( 0, Dbg, ("ValueOffset = %08lx\n", ValueOffset) );
  2241. DebugTrace( 0, Dbg, ("Value = %08lx\n", Value) );
  2242. DebugTrace( 0, Dbg, ("ValueLength = %08lx\n", ValueLength) );
  2243. DebugTrace( 0, Dbg, ("SetNewLength = %02lx\n", SetNewLength) );
  2244. DebugTrace( 0, Dbg, ("LogNonresidentToo = %02lx\n", LogNonresidentToo) );
  2245. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  2246. //
  2247. // Get the file record and attribute pointers.
  2248. //
  2249. FileRecord = NtfsContainingFileRecord(Context);
  2250. Attribute = NtfsFoundAttribute(Context);
  2251. TypeCode = Attribute->TypeCode;
  2252. //
  2253. // Set up a pointer to the name buffer in case we have to use it.
  2254. //
  2255. SavedName.Buffer = NameBuffer;
  2256. //
  2257. // Get the current attribute value length.
  2258. //
  2259. if (NtfsIsAttributeResident(Attribute)) {
  2260. CurrentLength = Attribute->Form.Resident.ValueLength;
  2261. } else {
  2262. if (((PLARGE_INTEGER)&Attribute->Form.Nonresident.AllocatedLength)->HighPart != 0) {
  2263. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2264. }
  2265. CurrentLength = ((ULONG)Attribute->Form.Nonresident.AllocatedLength);
  2266. }
  2267. ASSERT( SetNewLength || ((ValueOffset + ValueLength) <= CurrentLength) );
  2268. //
  2269. // Calculate how much the file record is changing by, and its new
  2270. // size. We also compute the size of the range of zero bytes.
  2271. //
  2272. if (SetNewLength) {
  2273. NewSize = ValueOffset + ValueLength;
  2274. SizeChange = NewSize - CurrentLength;
  2275. QuadSizeChange = QuadAlign( NewSize ) - QuadAlign( CurrentLength );
  2276. //
  2277. // If the new size is large enough, the size change may appear to be negative.
  2278. // In this case we go directly to the non-resident path.
  2279. //
  2280. if (NewSize > Vcb->BytesPerFileRecordSegment) {
  2281. GoToNonResident = TRUE;
  2282. }
  2283. } else {
  2284. NewSize = CurrentLength;
  2285. SizeChange = 0;
  2286. QuadSizeChange = 0;
  2287. }
  2288. //
  2289. // If we are zeroing a range in the file and it extends to the
  2290. // end of the file or beyond then make this a single zeroed run.
  2291. //
  2292. if (!ARGUMENT_PRESENT( Value )
  2293. && ValueOffset >= CurrentLength) {
  2294. ZeroLength = ValueOffset + ValueLength - CurrentLength;
  2295. ValueOffset = ValueOffset + ValueLength;
  2296. ValueLength = 0;
  2297. //
  2298. // If we are writing data starting beyond the end of the
  2299. // file then we have a range of bytes to zero.
  2300. //
  2301. } else if (ValueOffset > CurrentLength) {
  2302. ZeroLength = ValueOffset - CurrentLength;
  2303. }
  2304. //
  2305. // At this point we know the following ranges:
  2306. //
  2307. // Range to save: Not needed unless going resident to non-resident
  2308. //
  2309. // Zero range: From Zero offset for length ZeroLength
  2310. //
  2311. // Modified range: From ValueOffset to NewSize, this length may
  2312. // be zero.
  2313. //
  2314. //
  2315. // If the attribute is resident, and it will stay resident, then we will
  2316. // handle that case first, and return.
  2317. //
  2318. if (NtfsIsAttributeResident( Attribute )
  2319. &&
  2320. !GoToNonResident
  2321. &&
  2322. ((QuadSizeChange <= (LONG)(FileRecord->BytesAvailable - FileRecord->FirstFreeByte))
  2323. || ((Attribute->RecordLength + SizeChange) < Vcb->BigEnoughToMove))) {
  2324. PVOID UndoBuffer;
  2325. ULONG UndoLength;
  2326. ULONG AttributeOffset;
  2327. //
  2328. // If the attribute record is growing, then we have to get the new space
  2329. // now.
  2330. //
  2331. if (QuadSizeChange > 0) {
  2332. BOOLEAN FirstPass = TRUE;
  2333. ASSERT( !FlagOn(Attribute->Form.Resident.ResidentFlags, RESIDENT_FORM_INDEXED) );
  2334. //
  2335. // Save a description of the attribute in case we have to look it up
  2336. // again.
  2337. //
  2338. SavedName.Length =
  2339. SavedName.MaximumLength = (USHORT)(Attribute->NameLength * sizeof(WCHAR));
  2340. if (SavedName.Length > sizeof(NameBuffer)) {
  2341. SavedName.Buffer = NtfsAllocatePool( NonPagedPool, SavedName.Length );
  2342. }
  2343. //
  2344. // Copy the name into the buffer.
  2345. //
  2346. if (SavedName.Length != 0) {
  2347. RtlCopyMemory( SavedName.Buffer,
  2348. Add2Ptr( Attribute, Attribute->NameOffset ),
  2349. SavedName.Length );
  2350. }
  2351. //
  2352. // Make sure we deallocate the name buffer.
  2353. //
  2354. try {
  2355. do {
  2356. //
  2357. // If not the first pass, we have to lookup the attribute
  2358. // again.
  2359. //
  2360. if (!FirstPass) {
  2361. NtfsCleanupAttributeContext( IrpContext, Context );
  2362. NtfsInitializeAttributeContext( Context );
  2363. if (!NtfsLookupAttributeByName( IrpContext,
  2364. Fcb,
  2365. &Fcb->FileReference,
  2366. TypeCode,
  2367. &SavedName,
  2368. NULL,
  2369. FALSE,
  2370. Context )) {
  2371. ASSERT(FALSE);
  2372. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2373. }
  2374. //
  2375. // Now we have to reload our attribute pointer
  2376. //
  2377. Attribute = NtfsFoundAttribute( Context );
  2378. }
  2379. FirstPass = FALSE;
  2380. //
  2381. // If FALSE is returned, then the space was not allocated and
  2382. // we have too loop back and try again. Second time must work.
  2383. //
  2384. } while (!NtfsChangeAttributeSize( IrpContext,
  2385. Fcb,
  2386. QuadAlign( Attribute->Form.Resident.ValueOffset + NewSize),
  2387. Context ));
  2388. } finally {
  2389. if (SavedName.Buffer != NameBuffer) {
  2390. NtfsFreePool(SavedName.Buffer);
  2391. }
  2392. }
  2393. //
  2394. // Now we have to reload our attribute pointer
  2395. //
  2396. FileRecord = NtfsContainingFileRecord(Context);
  2397. Attribute = NtfsFoundAttribute(Context);
  2398. } else {
  2399. //
  2400. // Make sure the buffer is pinned if we are not changing size, because
  2401. // we begin to modify it below.
  2402. //
  2403. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  2404. //
  2405. // We can eliminate some/all of the value if it has not changed.
  2406. //
  2407. if (ARGUMENT_PRESENT(Value)) {
  2408. UnchangedSize = (ULONG)RtlCompareMemory( Add2Ptr(Attribute,
  2409. Attribute->Form.Resident.ValueOffset +
  2410. ValueOffset),
  2411. Value,
  2412. ValueLength );
  2413. Value = Add2Ptr(Value, UnchangedSize);
  2414. ValueOffset += UnchangedSize;
  2415. ValueLength -= UnchangedSize;
  2416. }
  2417. }
  2418. RecordOffset = PtrOffset(FileRecord, Attribute);
  2419. //
  2420. // If there is a zero range of bytes, deal with it now.
  2421. // If we are zeroing data then we must be growing the
  2422. // file.
  2423. //
  2424. if (ZeroLength != 0) {
  2425. //
  2426. // We always start zeroing at the zeroing offset.
  2427. //
  2428. AttributeOffset = Attribute->Form.Resident.ValueOffset +
  2429. CurrentLength;
  2430. //
  2431. // If we are starting at the end of the file the undo
  2432. // buffer is NULL and the length is zero.
  2433. //
  2434. FileRecord->Lsn =
  2435. NtfsWriteLog( IrpContext,
  2436. Vcb->MftScb,
  2437. NtfsFoundBcb(Context),
  2438. UpdateResidentValue,
  2439. NULL,
  2440. ZeroLength,
  2441. UpdateResidentValue,
  2442. NULL,
  2443. 0,
  2444. NtfsMftOffset( Context ),
  2445. RecordOffset,
  2446. AttributeOffset,
  2447. Vcb->BytesPerFileRecordSegment );
  2448. //
  2449. // Now zero this data by calling the same routine as restart.
  2450. //
  2451. NtfsRestartChangeValue( IrpContext,
  2452. FileRecord,
  2453. RecordOffset,
  2454. AttributeOffset,
  2455. NULL,
  2456. ZeroLength,
  2457. TRUE );
  2458. #ifdef SYSCACHE_DEBUG
  2459. {
  2460. PSCB TempScb;
  2461. TempScb = CONTAINING_RECORD( Fcb->ScbQueue.Flink, SCB, FcbLinks );
  2462. while (&TempScb->FcbLinks != &Fcb->ScbQueue) {
  2463. if (ScbIsBeingLogged( TempScb )) {
  2464. FsRtlLogSyscacheEvent( TempScb, SCE_ZERO_MF, 0, AttributeOffset, ZeroLength, 0 );
  2465. }
  2466. TempScb = CONTAINING_RECORD( TempScb->FcbLinks.Flink, SCB, FcbLinks );
  2467. }
  2468. }
  2469. #endif
  2470. }
  2471. //
  2472. // Now log the new data for the file. This range will always begin
  2473. // within the current range of bytes for the file. Because of this
  2474. // there is an undo action.
  2475. //
  2476. // Even if there is not a nonzero ValueLength, we still have to
  2477. // execute this code if the attribute is being truncated.
  2478. // The only exception is if we logged some zero data and have
  2479. // nothing left to log.
  2480. //
  2481. if ((ValueLength != 0)
  2482. || (ZeroLength == 0
  2483. && SizeChange != 0)) {
  2484. //
  2485. // The attribute offset is always at the value offset.
  2486. //
  2487. AttributeOffset = Attribute->Form.Resident.ValueOffset + ValueOffset;
  2488. //
  2489. // There are 3 possible cases for the undo action to
  2490. // log.
  2491. //
  2492. //
  2493. // If we are growing the file starting beyond the end of
  2494. // the file then undo buffer is NULL and the length is
  2495. // zero. This will still allow us to shrink the file
  2496. // on abort.
  2497. //
  2498. if (ValueOffset >= CurrentLength) {
  2499. UndoBuffer = NULL;
  2500. UndoLength = 0;
  2501. //
  2502. // For the other cases the undo buffer begins at the
  2503. // point of the change.
  2504. //
  2505. } else {
  2506. UndoBuffer = Add2Ptr( Attribute,
  2507. Attribute->Form.Resident.ValueOffset + ValueOffset );
  2508. //
  2509. // If the size isn't changing then the undo length is the same as
  2510. // the redo length.
  2511. //
  2512. if (SizeChange == 0) {
  2513. UndoLength = ValueLength;
  2514. //
  2515. // Otherwise the length is the range between the end of the
  2516. // file and the start of the new data.
  2517. //
  2518. } else {
  2519. UndoLength = CurrentLength - ValueOffset;
  2520. }
  2521. }
  2522. FileRecord->Lsn =
  2523. NtfsWriteLog( IrpContext,
  2524. Vcb->MftScb,
  2525. NtfsFoundBcb(Context),
  2526. UpdateResidentValue,
  2527. Value,
  2528. ValueLength,
  2529. UpdateResidentValue,
  2530. UndoBuffer,
  2531. UndoLength,
  2532. NtfsMftOffset( Context ),
  2533. RecordOffset,
  2534. AttributeOffset,
  2535. Vcb->BytesPerFileRecordSegment );
  2536. //
  2537. // Now update this data by calling the same routine as restart.
  2538. //
  2539. NtfsRestartChangeValue( IrpContext,
  2540. FileRecord,
  2541. RecordOffset,
  2542. AttributeOffset,
  2543. Value,
  2544. ValueLength,
  2545. (BOOLEAN)(SizeChange != 0) );
  2546. }
  2547. DebugTrace( -1, Dbg, ("NtfsChangeAttributeValue -> VOID\n") );
  2548. return;
  2549. }
  2550. //
  2551. // Nonresident case. Create the Scb and attributestream.
  2552. //
  2553. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  2554. AttributeTypeCode = Attribute->TypeCode;
  2555. Scb = NtfsCreateScb( IrpContext,
  2556. Fcb,
  2557. AttributeTypeCode,
  2558. &AttributeName,
  2559. FALSE,
  2560. &ReturnedExistingScb );
  2561. //
  2562. // Use try-finally for cleanup.
  2563. //
  2564. try {
  2565. BOOLEAN AllocateBufferCopy = FALSE;
  2566. BOOLEAN DeleteAllocation = FALSE;
  2567. BOOLEAN LookupAttribute = FALSE;
  2568. BOOLEAN AdvanceValidData = FALSE;
  2569. LONGLONG NewValidDataLength;
  2570. LONGLONG LargeValueOffset;
  2571. LONGLONG LargeNewSize;
  2572. if (SetNewLength
  2573. && NewSize > Scb->Header.FileSize.LowPart
  2574. && TypeCode == $ATTRIBUTE_LIST) {
  2575. AllocateBufferCopy = TRUE;
  2576. }
  2577. LargeNewSize = NewSize;
  2578. LargeValueOffset = ValueOffset;
  2579. //
  2580. // Well, the attribute is either changing to nonresident, or it is already
  2581. // nonresident. First we will handle the conversion to nonresident case.
  2582. // We can detect this case by whether or not the attribute is currently
  2583. // resident.
  2584. //
  2585. if (NtfsIsAttributeResident(Attribute)) {
  2586. NtfsConvertToNonresident( IrpContext,
  2587. Fcb,
  2588. Attribute,
  2589. CreateSectionUnderway,
  2590. Context );
  2591. //
  2592. // Reload the attribute pointer from the context.
  2593. //
  2594. Attribute = NtfsFoundAttribute( Context );
  2595. //
  2596. // The process of creating a non resident attribute will also create
  2597. // and initialize a stream file for the Scb. If the file is already
  2598. // non-resident we also need a stream file.
  2599. //
  2600. } else {
  2601. NtfsCreateInternalAttributeStream( IrpContext,
  2602. Scb,
  2603. TRUE,
  2604. &NtfsInternalUseFile[CHANGEATTRIBUTEVALUE_FILE_NUMBER] );
  2605. }
  2606. //
  2607. // If the attribute is already nonresident, make sure the allocation
  2608. // is the right size. We grow it before we log the data to be sure
  2609. // we have the space for the new data. We shrink it after we log the
  2610. // new data so we have the old data available for the undo.
  2611. //
  2612. if (((PLARGE_INTEGER)&Attribute->Form.Nonresident.AllocatedLength)->HighPart != 0) {
  2613. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  2614. }
  2615. if (NewSize > ((ULONG)Attribute->Form.Nonresident.AllocatedLength)) {
  2616. LONGLONG NewAllocation;
  2617. if (PreserveContext) {
  2618. //
  2619. // Save a description of the attribute in case we have to look it up
  2620. // again.
  2621. //
  2622. SavedName.Length =
  2623. SavedName.MaximumLength = (USHORT)(Attribute->NameLength * sizeof(WCHAR));
  2624. if (SavedName.Length > sizeof(NameBuffer)) {
  2625. SavedName.Buffer = NtfsAllocatePool( NonPagedPool, SavedName.Length );
  2626. }
  2627. //
  2628. // Copy the name into the buffer.
  2629. //
  2630. if (SavedName.Length != 0) {
  2631. RtlCopyMemory( SavedName.Buffer,
  2632. Add2Ptr( Attribute, Attribute->NameOffset ),
  2633. SavedName.Length );
  2634. }
  2635. LookupAttribute = TRUE;
  2636. }
  2637. //
  2638. // If this is the attribute list then check if we want to allocate a larger block.
  2639. // This way the attribute list doesn't get too fragmented.
  2640. //
  2641. NewAllocation = NewSize - ((ULONG)Attribute->Form.Nonresident.AllocatedLength);
  2642. if (Scb->AttributeTypeCode == $ATTRIBUTE_LIST) {
  2643. if ((ULONG) Attribute->Form.Nonresident.AllocatedLength > (4 * PAGE_SIZE)) {
  2644. NewAllocation += (2 * PAGE_SIZE);
  2645. } else if ((ULONG) Attribute->Form.Nonresident.AllocatedLength > PAGE_SIZE) {
  2646. NewAllocation += PAGE_SIZE;
  2647. }
  2648. }
  2649. NtfsAddAllocation( IrpContext,
  2650. Scb->FileObject,
  2651. Scb,
  2652. LlClustersFromBytes( Vcb, Attribute->Form.Nonresident.AllocatedLength ),
  2653. LlClustersFromBytes( Vcb, NewAllocation ),
  2654. FALSE,
  2655. NULL );
  2656. //
  2657. // AddAllocation will adjust the sizes in the Scb and report
  2658. // the new size to the cache manager. We need to remember if
  2659. // we changed the sizes for the unnamed data attribute.
  2660. //
  2661. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  2662. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  2663. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  2664. }
  2665. } else if (Vcb->BytesPerCluster <=
  2666. ((ULONG)Attribute->Form.Nonresident.AllocatedLength) - NewSize) {
  2667. if ((Scb->AttributeTypeCode != $ATTRIBUTE_LIST) ||
  2668. ((NewSize + Vcb->BytesPerCluster) * 2 < ((ULONG) Attribute->Form.Nonresident.AllocatedLength))) {
  2669. DeleteAllocation = TRUE;
  2670. }
  2671. }
  2672. //
  2673. // Now, write in the data.
  2674. //
  2675. if ((ValueLength != 0
  2676. && ARGUMENT_PRESENT( Value ))
  2677. || (LogNonresidentToo
  2678. && SetNewLength)) {
  2679. BOOLEAN BytesToUndo;
  2680. //
  2681. // We have to compute the amount of data to zero in a different
  2682. // way than we did for the resident case. For the non-resident
  2683. // case we need to zero the data between the old valid data
  2684. // length and the offset in the file for the new data.
  2685. //
  2686. if (LargeValueOffset >= Scb->Header.ValidDataLength.QuadPart) {
  2687. ZeroLength = (ULONG)(LargeValueOffset - Scb->Header.ValidDataLength.QuadPart);
  2688. BytesToUndo = FALSE;
  2689. } else {
  2690. ZeroLength = 0;
  2691. BytesToUndo = TRUE;
  2692. }
  2693. //
  2694. // Update existing nonresident attribute. (We may have just created it
  2695. // above.)
  2696. //
  2697. // If we are supposed to log it, then pin, log and do the update here.
  2698. //
  2699. if (LogNonresidentToo) {
  2700. //
  2701. // At this point the attribute is non-resident and contains
  2702. // its previous value. If the new data lies beyond the
  2703. // previous valid data, we need to zero this data. This
  2704. // action won't require any undo. Otherwise the new data
  2705. // lies within the existing data. In this case we need to
  2706. // log the previous data for possible undo. Finally, the
  2707. // tail of the new data may extend beyond the end of the
  2708. // previous data. There is no undo requirement for these
  2709. // bytes.
  2710. //
  2711. // We do the logging operation in three steps:
  2712. //
  2713. // 1 - We find the all the pages in the attribute that
  2714. // we need to zero any bytes for. There is no
  2715. // undo for these bytes.
  2716. //
  2717. // 2 - Find all pages where we have to perform undo and
  2718. // log the changes to those pages. Note only
  2719. // step 1 or step 2 will be performed as they
  2720. // are mutually exclusive.
  2721. //
  2722. // 3 - Finally, we may have pages where the new data
  2723. // extends beyond the current final page in the
  2724. // attribute. We log the new data but there is
  2725. // no undo.
  2726. //
  2727. // 4 - We may have pages where the old data extends
  2728. // beyond the new data. We will log this old
  2729. // data in the event that we grow and shrink
  2730. // this attribute several times in the same
  2731. // transaction (changes to the attribute list).
  2732. // In this case there is redo but no undo.
  2733. //
  2734. LONGLONG CurrentPage;
  2735. ULONG PageOffset;
  2736. ULONG ByteCountToUndo;
  2737. ULONG NewBytesRemaining;
  2738. //
  2739. // Find the starting page for this operation. It is the
  2740. // ValidDataLength rounded down to a page boundary.
  2741. //
  2742. CurrentPage = Scb->Header.ValidDataLength.QuadPart;
  2743. PageOffset = (ULONG)CurrentPage & (PAGE_SIZE - 1);
  2744. (ULONG)CurrentPage = ((ULONG)CurrentPage & ~(PAGE_SIZE - 1));
  2745. //
  2746. // Loop until there are no more bytes to zero.
  2747. //
  2748. while (ZeroLength != 0) {
  2749. ULONG ZeroBytesThisPage;
  2750. ZeroBytesThisPage = PAGE_SIZE - PageOffset;
  2751. if (ZeroBytesThisPage > ZeroLength) {
  2752. ZeroBytesThisPage = ZeroLength;
  2753. }
  2754. //
  2755. // Pin the desired page and compute a buffer into the
  2756. // page. Also compute how many bytes we we zero on
  2757. // this page.
  2758. //
  2759. NtfsUnpinBcb( IrpContext, &Bcb );
  2760. NtfsPinStream( IrpContext,
  2761. Scb,
  2762. CurrentPage,
  2763. ZeroBytesThisPage + PageOffset,
  2764. &Bcb,
  2765. &Buffer );
  2766. Buffer = Add2Ptr( Buffer, PageOffset );
  2767. //
  2768. // Now write the zeros into the log.
  2769. //
  2770. (VOID)
  2771. NtfsWriteLog( IrpContext,
  2772. Scb,
  2773. Bcb,
  2774. UpdateNonresidentValue,
  2775. NULL,
  2776. ZeroBytesThisPage,
  2777. Noop,
  2778. NULL,
  2779. 0,
  2780. CurrentPage,
  2781. PageOffset,
  2782. 0,
  2783. ZeroBytesThisPage + PageOffset );
  2784. //
  2785. // Zero any data necessary.
  2786. //
  2787. RtlZeroMemory( Buffer, ZeroBytesThisPage );
  2788. #ifdef SYSCACHE_DEBUG
  2789. if (ScbIsBeingLogged( Scb )) {
  2790. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_MF, 0, CurrentPage, ZeroBytesThisPage, 1 );
  2791. }
  2792. #endif
  2793. //
  2794. // Now move through the file.
  2795. //
  2796. ZeroLength -= ZeroBytesThisPage;
  2797. CurrentPage = CurrentPage + PAGE_SIZE;
  2798. PageOffset = 0;
  2799. }
  2800. //
  2801. // Find the starting page for this operation. It is the
  2802. // ValueOffset rounded down to a page boundary.
  2803. //
  2804. CurrentPage = LargeValueOffset;
  2805. (ULONG)CurrentPage = ((ULONG)CurrentPage & ~(PAGE_SIZE - 1));
  2806. PageOffset = (ULONG)LargeValueOffset & (PAGE_SIZE - 1);
  2807. //
  2808. // Now loop until there are no more pages with undo
  2809. // bytes to log.
  2810. //
  2811. NewBytesRemaining = ValueLength;
  2812. if (BytesToUndo) {
  2813. ByteCountToUndo = (ULONG)(Scb->Header.ValidDataLength.QuadPart - LargeValueOffset);
  2814. //
  2815. // If we are spanning pages, growing the file and the
  2816. // input buffer points into the cache, we could lose
  2817. // data as we cross a page boundary. In that case
  2818. // we need to allocate a separate buffer.
  2819. //
  2820. if (AllocateBufferCopy
  2821. && NewBytesRemaining + PageOffset > PAGE_SIZE) {
  2822. CopyInputBuffer = NtfsAllocatePool(PagedPool, NewBytesRemaining );
  2823. RtlCopyMemory( CopyInputBuffer,
  2824. Value,
  2825. NewBytesRemaining );
  2826. Value = CopyInputBuffer;
  2827. AllocateBufferCopy = FALSE;
  2828. }
  2829. //
  2830. // If we aren't setting a new length then limit the
  2831. // undo bytes to those being overwritten.
  2832. //
  2833. if (!SetNewLength
  2834. && ByteCountToUndo > NewBytesRemaining) {
  2835. ByteCountToUndo = NewBytesRemaining;
  2836. }
  2837. while (ByteCountToUndo != 0) {
  2838. ULONG UndoBytesThisPage;
  2839. ULONG RedoBytesThisPage;
  2840. ULONG BytesThisPage;
  2841. NTFS_LOG_OPERATION RedoOperation;
  2842. PVOID RedoBuffer;
  2843. //
  2844. // Also compute the number of bytes of undo and
  2845. // redo on this page.
  2846. //
  2847. RedoBytesThisPage = UndoBytesThisPage = PAGE_SIZE - PageOffset;
  2848. if (RedoBytesThisPage > NewBytesRemaining) {
  2849. RedoBytesThisPage = NewBytesRemaining;
  2850. }
  2851. if (UndoBytesThisPage >= ByteCountToUndo) {
  2852. UndoBytesThisPage = ByteCountToUndo;
  2853. }
  2854. //
  2855. // We pin enough bytes on this page to cover both the
  2856. // redo and undo bytes.
  2857. //
  2858. if (UndoBytesThisPage > RedoBytesThisPage) {
  2859. BytesThisPage = PageOffset + UndoBytesThisPage;
  2860. } else {
  2861. BytesThisPage = PageOffset + RedoBytesThisPage;
  2862. }
  2863. //
  2864. // If there is no redo (we are shrinking the data),
  2865. // then make the redo a noop.
  2866. //
  2867. if (RedoBytesThisPage == 0) {
  2868. RedoOperation = Noop;
  2869. RedoBuffer = NULL;
  2870. } else {
  2871. RedoOperation = UpdateNonresidentValue;
  2872. RedoBuffer = Value;
  2873. }
  2874. //
  2875. // Now we pin the page and calculate the beginning
  2876. // buffer in the page.
  2877. //
  2878. NtfsUnpinBcb( IrpContext, &Bcb );
  2879. NtfsPinStream( IrpContext,
  2880. Scb,
  2881. CurrentPage,
  2882. BytesThisPage,
  2883. &Bcb,
  2884. &Buffer );
  2885. Buffer = Add2Ptr( Buffer, PageOffset );
  2886. //
  2887. // Now log the changes to this page.
  2888. //
  2889. (VOID)
  2890. NtfsWriteLog( IrpContext,
  2891. Scb,
  2892. Bcb,
  2893. RedoOperation,
  2894. RedoBuffer,
  2895. RedoBytesThisPage,
  2896. UpdateNonresidentValue,
  2897. Buffer,
  2898. UndoBytesThisPage,
  2899. CurrentPage,
  2900. PageOffset,
  2901. 0,
  2902. BytesThisPage );
  2903. //
  2904. // Move the data into place if we have new data.
  2905. //
  2906. if (RedoBytesThisPage != 0) {
  2907. RtlMoveMemory( Buffer, Value, RedoBytesThisPage );
  2908. }
  2909. //
  2910. // Now decrement the counts and move through the
  2911. // caller's buffer.
  2912. //
  2913. ByteCountToUndo -= UndoBytesThisPage;
  2914. NewBytesRemaining -= RedoBytesThisPage;
  2915. CurrentPage = PAGE_SIZE + CurrentPage;
  2916. PageOffset = 0;
  2917. Value = Add2Ptr( Value, RedoBytesThisPage );
  2918. }
  2919. }
  2920. //
  2921. // Now loop until there are no more pages with new data
  2922. // to log.
  2923. //
  2924. while (NewBytesRemaining != 0) {
  2925. ULONG RedoBytesThisPage;
  2926. //
  2927. // Also compute the number of bytes of redo on this page.
  2928. //
  2929. RedoBytesThisPage = PAGE_SIZE - PageOffset;
  2930. if (RedoBytesThisPage > NewBytesRemaining) {
  2931. RedoBytesThisPage = NewBytesRemaining;
  2932. }
  2933. //
  2934. // Now we pin the page and calculate the beginning
  2935. // buffer in the page.
  2936. //
  2937. NtfsUnpinBcb( IrpContext, &Bcb );
  2938. NtfsPinStream( IrpContext,
  2939. Scb,
  2940. CurrentPage,
  2941. RedoBytesThisPage,
  2942. &Bcb,
  2943. &Buffer );
  2944. Buffer = Add2Ptr( Buffer, PageOffset );
  2945. //
  2946. // Now log the changes to this page.
  2947. //
  2948. (VOID)
  2949. NtfsWriteLog( IrpContext,
  2950. Scb,
  2951. Bcb,
  2952. UpdateNonresidentValue,
  2953. Value,
  2954. RedoBytesThisPage,
  2955. Noop,
  2956. NULL,
  2957. 0,
  2958. CurrentPage,
  2959. PageOffset,
  2960. 0,
  2961. PageOffset + RedoBytesThisPage );
  2962. //
  2963. // Move the data into place.
  2964. //
  2965. RtlMoveMemory( Buffer, Value, RedoBytesThisPage );
  2966. //
  2967. // Now decrement the counts and move through the
  2968. // caller's buffer.
  2969. //
  2970. NewBytesRemaining -= RedoBytesThisPage;
  2971. CurrentPage = PAGE_SIZE + CurrentPage;
  2972. PageOffset = 0;
  2973. Value = Add2Ptr( Value, RedoBytesThisPage );
  2974. }
  2975. //
  2976. // If we have values to write, we write them to the cache now.
  2977. //
  2978. } else {
  2979. //
  2980. // If we have data to zero, we do no now.
  2981. //
  2982. #ifdef SYSCACHE_DEBUG
  2983. if (ScbIsBeingLogged( Scb )) {
  2984. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_MF, 0, Scb->Header.ValidDataLength.QuadPart, ZeroLength, 2 );
  2985. }
  2986. #endif
  2987. if (ZeroLength != 0) {
  2988. if (!NtfsZeroData( IrpContext,
  2989. Scb,
  2990. Scb->FileObject,
  2991. Scb->Header.ValidDataLength.QuadPart,
  2992. (LONGLONG)ZeroLength,
  2993. NULL )) {
  2994. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2995. }
  2996. }
  2997. if (!CcCopyWrite( Scb->FileObject,
  2998. (PLARGE_INTEGER)&LargeValueOffset,
  2999. ValueLength,
  3000. (BOOLEAN) FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ),
  3001. Value )) {
  3002. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3003. }
  3004. }
  3005. //
  3006. // We need to remember the new valid data length in the
  3007. // Scb if it is greater than the existing.
  3008. //
  3009. NewValidDataLength = LargeValueOffset + ValueLength;
  3010. if (NewValidDataLength > Scb->Header.ValidDataLength.QuadPart) {
  3011. #ifdef SYSCACHE_DEBUG
  3012. if (ScbIsBeingLogged( Scb )) {
  3013. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_MF, SCE_FLAG_SET_VDL, Scb->Header.ValidDataLength.QuadPart, NewValidDataLength, 0 );
  3014. }
  3015. #endif
  3016. Scb->Header.ValidDataLength.QuadPart = NewValidDataLength;
  3017. //
  3018. // If we took the log non-resident path, then we
  3019. // want to advance this on the disk as well.
  3020. //
  3021. if (LogNonresidentToo) {
  3022. AdvanceValidData = TRUE;
  3023. }
  3024. SetFlag( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE );
  3025. }
  3026. //
  3027. // We need to maintain the file size in the Scb. If we grow the
  3028. // file, we extend the cache file size. We always set the
  3029. // valid data length in the Scb to the new file size. The
  3030. // 'AdvanceValidData' boolean and the current size on the
  3031. // disk will determine if it changes on disk.
  3032. //
  3033. if (SetNewLength) {
  3034. Scb->Header.ValidDataLength.QuadPart = NewValidDataLength;
  3035. }
  3036. }
  3037. if (SetNewLength) {
  3038. Scb->Header.FileSize.QuadPart = LargeNewSize;
  3039. if (LogNonresidentToo) {
  3040. Scb->Header.ValidDataLength.QuadPart = LargeNewSize;
  3041. }
  3042. }
  3043. //
  3044. // Note VDD is nonzero only for compressed files
  3045. //
  3046. if (Scb->Header.ValidDataLength.QuadPart < Scb->ValidDataToDisk) {
  3047. Scb->ValidDataToDisk = Scb->Header.ValidDataLength.QuadPart;
  3048. }
  3049. //
  3050. // If there is allocation to delete, we do so now.
  3051. //
  3052. if (DeleteAllocation) {
  3053. //
  3054. // If this is an attribute list then leave at least one full cluster at the
  3055. // end. We don't want to trim off a cluster and then try to regrow the attribute
  3056. // list within the same transaction.
  3057. //
  3058. if (Scb->AttributeTypeCode == $ATTRIBUTE_LIST) {
  3059. LargeNewSize += Vcb->BytesPerCluster;
  3060. ASSERT( LargeNewSize <= Scb->Header.AllocationSize.QuadPart );
  3061. }
  3062. NtfsDeleteAllocation( IrpContext,
  3063. Scb->FileObject,
  3064. Scb,
  3065. LlClustersFromBytes( Vcb, LargeNewSize ),
  3066. MAXLONGLONG,
  3067. TRUE,
  3068. FALSE );
  3069. //
  3070. // DeleteAllocation will adjust the sizes in the Scb and report
  3071. // the new size to the cache manager. We need to remember if
  3072. // we changed the sizes for the unnamed data attribute.
  3073. //
  3074. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  3075. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  3076. Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
  3077. SetFlag( Fcb->InfoFlags,
  3078. (FCB_INFO_CHANGED_ALLOC_SIZE | FCB_INFO_CHANGED_FILE_SIZE) );
  3079. }
  3080. if (AdvanceValidData) {
  3081. NtfsWriteFileSizes( IrpContext,
  3082. Scb,
  3083. &Scb->Header.ValidDataLength.QuadPart,
  3084. TRUE,
  3085. TRUE,
  3086. TRUE );
  3087. }
  3088. } else if (SetNewLength) {
  3089. PFILE_OBJECT CacheFileObject = NULL;
  3090. //
  3091. // If there is no file object, we will create a stream file
  3092. // now,
  3093. //
  3094. if (Scb->FileObject != NULL) {
  3095. CacheFileObject = Scb->FileObject;
  3096. } else if (!CreateSectionUnderway) {
  3097. NtfsCreateInternalAttributeStream( IrpContext,
  3098. Scb,
  3099. FALSE,
  3100. &NtfsInternalUseFile[CHANGEATTRIBUTEVALUE2_FILE_NUMBER] );
  3101. CacheFileObject = Scb->FileObject;
  3102. } else {
  3103. PIO_STACK_LOCATION IrpSp;
  3104. IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
  3105. if (IrpSp->FileObject->SectionObjectPointer == &Scb->NonpagedScb->SegmentObject) {
  3106. CacheFileObject = IrpSp->FileObject;
  3107. }
  3108. }
  3109. ASSERT( CacheFileObject != NULL );
  3110. NtfsSetBothCacheSizes( CacheFileObject,
  3111. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  3112. Scb );
  3113. //
  3114. // If this is the unnamed data attribute, we need to mark this
  3115. // change in the Fcb.
  3116. //
  3117. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  3118. Fcb->Info.FileSize = Scb->Header.FileSize.QuadPart;
  3119. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_SIZE );
  3120. }
  3121. //
  3122. // Now update the sizes on the disk.
  3123. // The new sizes will already be in the Scb.
  3124. //
  3125. NtfsWriteFileSizes( IrpContext,
  3126. Scb,
  3127. &Scb->Header.ValidDataLength.QuadPart,
  3128. AdvanceValidData,
  3129. TRUE,
  3130. TRUE );
  3131. } else if (AdvanceValidData) {
  3132. NtfsWriteFileSizes( IrpContext,
  3133. Scb,
  3134. &Scb->Header.ValidDataLength.QuadPart,
  3135. TRUE,
  3136. TRUE,
  3137. TRUE );
  3138. }
  3139. //
  3140. // Look up the attribute again in case it moved.
  3141. //
  3142. if (LookupAttribute) {
  3143. NtfsCleanupAttributeContext( IrpContext, Context );
  3144. NtfsInitializeAttributeContext( Context );
  3145. if (!NtfsLookupAttributeByName( IrpContext,
  3146. Fcb,
  3147. &Fcb->FileReference,
  3148. TypeCode,
  3149. &SavedName,
  3150. NULL,
  3151. FALSE,
  3152. Context )) {
  3153. ASSERT( FALSE );
  3154. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3155. }
  3156. }
  3157. } finally {
  3158. DebugUnwind( NtfsChangeAttributeValue );
  3159. if (CopyInputBuffer != NULL) {
  3160. NtfsFreePool( CopyInputBuffer );
  3161. }
  3162. if (SaveBuffer != NULL) {
  3163. NtfsFreePool( SaveBuffer );
  3164. }
  3165. NtfsUnpinBcb( IrpContext, &Bcb );
  3166. DebugTrace( -1, Dbg, ("NtfsChangeAttributeValue -> VOID\n") );
  3167. }
  3168. }
  3169. VOID
  3170. NtfsConvertToNonresident (
  3171. IN PIRP_CONTEXT IrpContext,
  3172. IN PFCB Fcb,
  3173. IN OUT PATTRIBUTE_RECORD_HEADER Attribute,
  3174. IN BOOLEAN CreateSectionUnderway,
  3175. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context OPTIONAL
  3176. )
  3177. /*++
  3178. Routine Description:
  3179. This routine converts a resident attribute to nonresident. It does so
  3180. by allocating a buffer and copying the data and attribute name away,
  3181. deleting the attribute, allocating a new attribute of the right size,
  3182. and then copying the data back out again.
  3183. Arguments:
  3184. Fcb - Requested file.
  3185. Attribute - Supplies a pointer to the attribute to convert.
  3186. CreateSectionUnderway - if supplied as TRUE, then to the best of the caller's
  3187. knowledge, an MM Create Section could be underway,
  3188. which means that we cannot initiate caching on
  3189. this attribute, as that could cause deadlock. The
  3190. value buffer in this case must be quad-aligned and
  3191. a multiple of cluster size in size.
  3192. Context - An attribute context to look up another attribute in the same
  3193. file record. If supplied, we insure that the context is valid
  3194. for converted attribute.
  3195. Return Value:
  3196. None
  3197. --*/
  3198. {
  3199. PVOID Buffer;
  3200. PVOID AllocatedBuffer = NULL;
  3201. ULONG AllocatedLength;
  3202. ULONG AttributeNameOffset;
  3203. ATTRIBUTE_ENUMERATION_CONTEXT LocalContext;
  3204. BOOLEAN CleanupLocalContext = FALSE;
  3205. BOOLEAN ReturnedExistingScb;
  3206. ATTRIBUTE_TYPE_CODE AttributeTypeCode = Attribute->TypeCode;
  3207. USHORT AttributeFlags = Attribute->Flags;
  3208. PVOID AttributeValue = NULL;
  3209. ULONG ValueLength;
  3210. UNICODE_STRING AttributeName;
  3211. WCHAR AttributeNameBuffer[16];
  3212. BOOLEAN WriteClusters = CreateSectionUnderway;
  3213. PBCB ResidentBcb = NULL;
  3214. PSCB Scb = NULL;
  3215. PAGED_CODE();
  3216. //
  3217. // Use a try-finally to facilitate cleanup.
  3218. //
  3219. try {
  3220. //
  3221. // Build a temporary copy of the name out of the attribute.
  3222. //
  3223. AttributeName.MaximumLength =
  3224. AttributeName.Length = Attribute->NameLength * sizeof( WCHAR );
  3225. AttributeName.Buffer = Add2Ptr( Attribute, Attribute->NameOffset );
  3226. //
  3227. // If we don't have an attribute context for this attribute then look it
  3228. // up now.
  3229. //
  3230. if (!ARGUMENT_PRESENT( Context )) {
  3231. Context = &LocalContext;
  3232. NtfsInitializeAttributeContext( Context );
  3233. CleanupLocalContext = TRUE;
  3234. //
  3235. // Lookup the first occurence of this attribute.
  3236. //
  3237. if (!NtfsLookupAttributeByName( IrpContext,
  3238. Fcb,
  3239. &Fcb->FileReference,
  3240. AttributeTypeCode,
  3241. &AttributeName,
  3242. NULL,
  3243. FALSE,
  3244. Context )) {
  3245. DebugTrace( 0, 0, ("Could not find attribute being converted\n") );
  3246. ASSERTMSG("Could not find attribute being converted, About to raise corrupt ", FALSE);
  3247. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3248. }
  3249. }
  3250. //
  3251. // We need to figure out how much pool to allocate. If there is a mapped
  3252. // view of this section or a section is being created we will allocate a buffer
  3253. // and copy the data into the buffer. Otherwise we will pin the data in
  3254. // the cache, mark it dirty and use that buffer to perform the conversion.
  3255. //
  3256. AllocatedLength = AttributeName.Length;
  3257. Scb = NtfsCreateScb( IrpContext,
  3258. Fcb,
  3259. AttributeTypeCode,
  3260. &AttributeName,
  3261. FALSE,
  3262. &ReturnedExistingScb );
  3263. //
  3264. // Clear the file size loaded flag for resident attributes non-user data because these
  3265. // values are not kept current in the scb and must be loaded off the attribute
  3266. // This situation only occurs when the user has opened the attribute explicitly
  3267. //
  3268. if (ReturnedExistingScb &&
  3269. FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT ) &&
  3270. !NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
  3271. ClearFlag( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED | SCB_STATE_HEADER_INITIALIZED );
  3272. }
  3273. //
  3274. // Make sure the Scb is up-to-date.
  3275. //
  3276. NtfsUpdateScbFromAttribute( IrpContext,
  3277. Scb,
  3278. Attribute );
  3279. //
  3280. // Set the flag in the Scb to indicate that we are converting this to
  3281. // non resident.
  3282. //
  3283. SetFlag( Scb->ScbState, SCB_STATE_CONVERT_UNDERWAY );
  3284. if (Scb->ScbSnapshot) {
  3285. ASSERT( (NULL == Scb->ScbSnapshot->OwnerIrpContext) || (IrpContext == Scb->ScbSnapshot->OwnerIrpContext) );
  3286. Scb->ScbSnapshot->OwnerIrpContext = IrpContext;
  3287. }
  3288. //
  3289. // Now check if the file is mapped by a user.
  3290. //
  3291. if (NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) &&
  3292. (CreateSectionUnderway ||
  3293. !MmCanFileBeTruncated( &Scb->NonpagedScb->SegmentObject, NULL ))) {
  3294. AttributeNameOffset = ClusterAlign( Fcb->Vcb,
  3295. Attribute->Form.Resident.ValueLength );
  3296. AllocatedLength += AttributeNameOffset;
  3297. ValueLength = Attribute->Form.Resident.ValueLength;
  3298. WriteClusters = TRUE;
  3299. if ((ValueLength != 0) &&
  3300. (IrpContext->MajorFunction == IRP_MJ_WRITE) &&
  3301. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX ) &&
  3302. (IrpContext->OriginatingIrp != NULL) &&
  3303. !FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ) &&
  3304. (Scb->Header.PagingIoResource != NULL)) {
  3305. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  3306. //
  3307. // If we fault the data into the section then we better have
  3308. // the paging io resource exclusive. Otherwise we could hit
  3309. // a collided page fault.
  3310. //
  3311. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3312. }
  3313. Scb = NULL;
  3314. } else {
  3315. volatile UCHAR VolatileUchar;
  3316. AttributeNameOffset = 0;
  3317. NtfsCreateInternalAttributeStream( IrpContext,
  3318. Scb,
  3319. TRUE,
  3320. &NtfsInternalUseFile[CONVERTTONONRESIDENT_FILE_NUMBER] );
  3321. //
  3322. // Make sure the cache is up-to-date.
  3323. //
  3324. NtfsSetBothCacheSizes( Scb->FileObject,
  3325. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  3326. Scb );
  3327. ValueLength = Scb->Header.ValidDataLength.LowPart;
  3328. if (ValueLength != 0) {
  3329. ULONG WaitState;
  3330. //
  3331. // There is a deadlock possibility if there is already a Bcb for
  3332. // this page. If the lazy writer has acquire the Bcb to flush the
  3333. // page then he can be blocked behind the current request which is
  3334. // trying to perform the convert. This thread will complete the
  3335. // deadlock by trying to acquire the Bcb to pin the page.
  3336. //
  3337. // If there is a possible deadlock then we will pin in two stages:
  3338. // First map the page (while waiting) to bring the page into memory,
  3339. // then pin it without waiting. If we are unable to acquire the
  3340. // Bcb then mark the Irp Context to acquire the paging io resource
  3341. // exclusively on the retry.
  3342. //
  3343. // We only do this for ConvertToNonResident which come from a user
  3344. // write. Otherwise the correct synchronization should already be done.
  3345. //
  3346. // Either the top level already has the paging io resource or there
  3347. // is no paging io resource.
  3348. //
  3349. // We might hit this point in the Hotfix path if we need to convert
  3350. // the bad cluster attribute list to non-resident. It that case
  3351. // we won't have an originating Irp.
  3352. //
  3353. if ((IrpContext->MajorFunction == IRP_MJ_WRITE) &&
  3354. !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX ) &&
  3355. (IrpContext->OriginatingIrp != NULL) &&
  3356. !FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ) &&
  3357. (Scb->Header.PagingIoResource != NULL)) {
  3358. LONGLONG FileOffset = 0;
  3359. //
  3360. // Now capture the wait state and set the IrpContext flag
  3361. // to handle a failure when mapping or pinning.
  3362. //
  3363. WaitState = FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  3364. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
  3365. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_ACQUIRE_EX );
  3366. //
  3367. // If we fault the data into the section then we better have
  3368. // the paging io resource exclusive. Otherwise we could hit
  3369. // a collided page fault.
  3370. //
  3371. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3372. } else {
  3373. NtfsPinStream( IrpContext,
  3374. Scb,
  3375. (LONGLONG)0,
  3376. ValueLength,
  3377. &ResidentBcb,
  3378. &AttributeValue );
  3379. }
  3380. //
  3381. // Close the window where this page can leave memory before we
  3382. // have the new attribute initialized. The result will be that
  3383. // we may fault in this page again and read uninitialized data
  3384. // out of the newly allocated sectors.
  3385. //
  3386. // Make the page dirty so that the cache manager will write it out
  3387. // and update the valid data length.
  3388. //
  3389. VolatileUchar = *((PUCHAR) AttributeValue);
  3390. *((PUCHAR) AttributeValue) = VolatileUchar;
  3391. }
  3392. }
  3393. if (AllocatedLength > 8) {
  3394. Buffer = AllocatedBuffer = NtfsAllocatePool(PagedPool, AllocatedLength );
  3395. } else {
  3396. Buffer = AttributeNameBuffer;
  3397. }
  3398. //
  3399. // Now update the attribute name in the buffer.
  3400. //
  3401. AttributeName.Buffer = Add2Ptr( Buffer, AttributeNameOffset );
  3402. RtlCopyMemory( AttributeName.Buffer,
  3403. Add2Ptr( Attribute, Attribute->NameOffset ),
  3404. AttributeName.Length );
  3405. //
  3406. // If we are going to write the clusters directly to the disk then copy
  3407. // the bytes into the buffer.
  3408. //
  3409. if (WriteClusters) {
  3410. AttributeValue = Buffer;
  3411. RtlCopyMemory( AttributeValue, NtfsAttributeValue( Attribute ), ValueLength );
  3412. }
  3413. //
  3414. // Now just delete the current record and create it nonresident.
  3415. // Create nonresident with attribute does the right thing if we
  3416. // are being called by MM. Preserve the file record but release
  3417. // any and all allocation.
  3418. //
  3419. NtfsDeleteAttributeRecord( IrpContext,
  3420. Fcb,
  3421. DELETE_LOG_OPERATION | DELETE_RELEASE_ALLOCATION,
  3422. Context );
  3423. NtfsCreateNonresidentWithValue( IrpContext,
  3424. Fcb,
  3425. AttributeTypeCode,
  3426. &AttributeName,
  3427. AttributeValue,
  3428. ValueLength,
  3429. AttributeFlags,
  3430. WriteClusters,
  3431. Scb,
  3432. TRUE,
  3433. Context );
  3434. //
  3435. // If we were passed an attribute context, then we want to
  3436. // reload the context with the new location of the file.
  3437. //
  3438. if (!CleanupLocalContext) {
  3439. NtfsCleanupAttributeContext( IrpContext, Context );
  3440. NtfsInitializeAttributeContext( Context );
  3441. if (!NtfsLookupAttributeByName( IrpContext,
  3442. Fcb,
  3443. &Fcb->FileReference,
  3444. AttributeTypeCode,
  3445. &AttributeName,
  3446. NULL,
  3447. FALSE,
  3448. Context )) {
  3449. DebugTrace( 0, 0, ("Could not find attribute being converted\n") );
  3450. ASSERTMSG("Could not find attribute being converted, About to raise corrupt ", FALSE);
  3451. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3452. }
  3453. }
  3454. } finally {
  3455. DebugUnwind( NtfsConvertToNonresident );
  3456. if (AllocatedBuffer != NULL) {
  3457. NtfsFreePool( AllocatedBuffer );
  3458. }
  3459. if (CleanupLocalContext) {
  3460. NtfsCleanupAttributeContext( IrpContext, Context );
  3461. }
  3462. NtfsUnpinBcb( IrpContext, &ResidentBcb );
  3463. }
  3464. }
  3465. VOID
  3466. NtfsDeleteAttributeRecord (
  3467. IN PIRP_CONTEXT IrpContext,
  3468. IN PFCB Fcb,
  3469. IN ULONG Flags,
  3470. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  3471. )
  3472. /*++
  3473. Routine Description:
  3474. This routine deletes an existing attribute removing it from the file record.
  3475. The caller specifies the attribute to be deleted via the attribute context,
  3476. and must be prepared to clean up this context no matter how this routine
  3477. returns.
  3478. Note that currently this routine does not deallocate any clusters allocated
  3479. to a nonresident attribute; it expects the caller to already have done so.
  3480. Arguments:
  3481. Fcb - Current file.
  3482. Flags - Bitmask that modifies behaviour:
  3483. DELETE_LOG_OPERATION Most callers should specify this, to have the
  3484. change logged. However, we can omit it if we are deleting an entire
  3485. file record, and will be logging that.
  3486. DELETE_RELEASE_FILE_RECORD Indicates that we should release the file record.
  3487. Most callers will not specify this. (Convert to non-resident will omit).
  3488. DELETE_RELEASE_ALLOCATION Indicates that we should free up any allocation.
  3489. Most callers will specify this.
  3490. Context - Attribute Context positioned at the attribute to delete.
  3491. Return Value:
  3492. None.
  3493. --*/
  3494. {
  3495. PATTRIBUTE_RECORD_HEADER Attribute;
  3496. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  3497. PVCB Vcb;
  3498. ATTRIBUTE_TYPE_CODE AttributeTypeCode;
  3499. ASSERT_IRP_CONTEXT( IrpContext );
  3500. ASSERT_FCB( Fcb );
  3501. Vcb = Fcb->Vcb;
  3502. PAGED_CODE();
  3503. DebugTrace( +1, Dbg, ("NtfsDeleteAttribute\n") );
  3504. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  3505. DebugTrace( 0, Dbg, ("Context =%08lx\n", Context) );
  3506. //
  3507. // Get the pointers we need.
  3508. //
  3509. Attribute = NtfsFoundAttribute(Context);
  3510. AttributeTypeCode = Attribute->TypeCode;
  3511. FileRecord = NtfsContainingFileRecord(Context);
  3512. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  3513. if (!NtfsIsAttributeResident( Attribute ) &&
  3514. FlagOn( Flags, DELETE_RELEASE_ALLOCATION)) {
  3515. ASSERT( (NULL == IrpContext->CleanupStructure) || (Fcb == IrpContext->CleanupStructure) );
  3516. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, Context, TRUE, FALSE );
  3517. //
  3518. // Reload our local pointers.
  3519. //
  3520. Attribute = NtfsFoundAttribute(Context);
  3521. FileRecord = NtfsContainingFileRecord(Context);
  3522. }
  3523. //
  3524. // If this is a resident stream then release the quota. Quota for
  3525. // non-resident streams is handled by NtfsDeleteAllocaiton.
  3526. //
  3527. if (NtfsIsTypeCodeSubjectToQuota( Attribute->TypeCode) &&
  3528. (NtfsIsAttributeResident( Attribute ) ||
  3529. (Attribute->Form.Nonresident.LowestVcn == 0))) {
  3530. LONGLONG Delta = -NtfsResidentStreamQuota( Vcb );
  3531. NtfsConditionallyUpdateQuota( IrpContext,
  3532. Fcb,
  3533. &Delta,
  3534. FlagOn( Flags, DELETE_LOG_OPERATION ),
  3535. FALSE );
  3536. }
  3537. //
  3538. // Be sure the attribute is pinned.
  3539. //
  3540. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  3541. //
  3542. // Log the change.
  3543. //
  3544. if (FlagOn( Flags, DELETE_LOG_OPERATION )) {
  3545. FileRecord->Lsn =
  3546. NtfsWriteLog( IrpContext,
  3547. Vcb->MftScb,
  3548. NtfsFoundBcb(Context),
  3549. DeleteAttribute,
  3550. NULL,
  3551. 0,
  3552. CreateAttribute,
  3553. Attribute,
  3554. Attribute->RecordLength,
  3555. NtfsMftOffset( Context ),
  3556. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord),
  3557. 0,
  3558. Vcb->BytesPerFileRecordSegment );
  3559. }
  3560. NtfsRestartRemoveAttribute( IrpContext,
  3561. FileRecord,
  3562. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord) );
  3563. Context->FoundAttribute.AttributeDeleted = TRUE;
  3564. if (FlagOn( Flags, DELETE_LOG_OPERATION ) &&
  3565. (Context->AttributeList.Bcb != NULL)) {
  3566. //
  3567. // Now delete the attribute list entry, if there is one. Do it
  3568. // after freeing space above, because we assume the list has not moved.
  3569. // Note we only do this if DELETE_LOG_OPERATION was specified, assuming
  3570. // that otherwise the entire file is going away anyway, so there is no
  3571. // need to fix up the list.
  3572. //
  3573. NtfsDeleteFromAttributeList( IrpContext, Fcb, Context );
  3574. }
  3575. //
  3576. // Delete the file record if it happened to go empty. (Note that
  3577. // delete file does not call this routine and deletes its own file
  3578. // records.)
  3579. //
  3580. if (FlagOn( Flags, DELETE_RELEASE_FILE_RECORD ) &&
  3581. FileRecord->FirstFreeByte == ((ULONG)FileRecord->FirstAttributeOffset +
  3582. QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE )))) {
  3583. ASSERT( NtfsFullSegmentNumber( &Fcb->FileReference ) ==
  3584. NtfsUnsafeSegmentNumber( &Fcb->FileReference ) );
  3585. NtfsDeallocateMftRecord( IrpContext,
  3586. Vcb,
  3587. (ULONG) LlFileRecordsFromBytes( Vcb, Context->FoundAttribute.MftFileOffset ));
  3588. }
  3589. DebugTrace( -1, Dbg, ("NtfsDeleteAttributeRecord -> VOID\n") );
  3590. return;
  3591. }
  3592. VOID
  3593. NtfsDeleteAllocationFromRecord (
  3594. PIRP_CONTEXT IrpContext,
  3595. IN PFCB Fcb,
  3596. IN PATTRIBUTE_ENUMERATION_CONTEXT Context,
  3597. IN BOOLEAN BreakupAllowed,
  3598. IN BOOLEAN LogIt
  3599. )
  3600. /*++
  3601. Routine Description:
  3602. This routine may be called to delete the allocation of an attribute
  3603. from its attribute record. It does nothing to the attribute record
  3604. itself - the caller must deal with that.
  3605. Arguments:
  3606. Fcb - Current file.
  3607. Context - Attribute enumeration context positioned to the attribute
  3608. whose allocation is to be deleted.
  3609. BreakupAllowed - TRUE if the caller can tolerate breaking up the deletion of
  3610. allocation into multiple transactions, if there are a large
  3611. number of runs.
  3612. LogIt - Indicates if we need to log the change to the mapping pairs.
  3613. Return Value:
  3614. None
  3615. --*/
  3616. {
  3617. PATTRIBUTE_RECORD_HEADER Attribute;
  3618. PSCB Scb;
  3619. UNICODE_STRING AttributeName;
  3620. PFILE_OBJECT TempFileObject;
  3621. BOOLEAN ScbExisted;
  3622. BOOLEAN ScbAcquired = FALSE;
  3623. BOOLEAN ReinitializeContext = FALSE;
  3624. BOOLEAN FcbHadPaging;
  3625. PAGED_CODE();
  3626. //
  3627. // Point to the current attribute.
  3628. //
  3629. Attribute = NtfsFoundAttribute( Context );
  3630. //
  3631. // If the attribute is nonresident, then delete its allocation.
  3632. //
  3633. ASSERT(Attribute->FormCode == NONRESIDENT_FORM);
  3634. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  3635. if (Fcb->PagingIoResource != NULL) {
  3636. FcbHadPaging = TRUE;
  3637. } else {
  3638. FcbHadPaging = FALSE;
  3639. }
  3640. //
  3641. // Decode the file object
  3642. //
  3643. Scb = NtfsCreateScb( IrpContext,
  3644. Fcb,
  3645. Attribute->TypeCode,
  3646. &AttributeName,
  3647. FALSE,
  3648. &ScbExisted );
  3649. try {
  3650. //
  3651. // If the scb is new and that caused a paging resource to be created
  3652. // E.g. a named data stream in a directory raise because our state is now
  3653. // incosistent. We need to acquire that paging resource first
  3654. //
  3655. if (!FcbHadPaging && (Fcb->PagingIoResource != NULL)) {
  3656. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  3657. }
  3658. //
  3659. // Acquire the Scb Exclusive
  3660. //
  3661. NtfsAcquireExclusiveScb( IrpContext, Scb );
  3662. ScbAcquired = TRUE;
  3663. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  3664. NtfsUpdateScbFromAttribute( IrpContext, Scb, Attribute );
  3665. }
  3666. //
  3667. // If we created the Scb, then this is the only case where
  3668. // it is legal for us to omit the File Object in the delete
  3669. // allocation call, because there cannot possibly be a section.
  3670. //
  3671. // Also if there is not a section and this thread owns everything
  3672. // for this file then we can neglect the file object.
  3673. //
  3674. if (!ScbExisted ||
  3675. ((Scb->NonpagedScb->SegmentObject.DataSectionObject == NULL) &&
  3676. ((Scb->Header.PagingIoResource == NULL) ||
  3677. (NtfsIsExclusiveScbPagingIo( Scb ))))) {
  3678. TempFileObject = NULL;
  3679. //
  3680. // Else, if there is already a stream file object, we can just
  3681. // use it.
  3682. //
  3683. } else if (Scb->FileObject != NULL) {
  3684. TempFileObject = Scb->FileObject;
  3685. //
  3686. // Else the Scb existed and we did not already have a stream,
  3687. // so we have to create one and delete it on the way out.
  3688. //
  3689. } else {
  3690. NtfsCreateInternalAttributeStream( IrpContext,
  3691. Scb,
  3692. TRUE,
  3693. &NtfsInternalUseFile[DELETEALLOCATIONFROMRECORD_FILE_NUMBER] );
  3694. TempFileObject = Scb->FileObject;
  3695. }
  3696. //
  3697. // Before we make this call, we need to check if we will have to
  3698. // reread the current attribute. This could be necessary if
  3699. // we remove any records for this attribute in the delete case.
  3700. //
  3701. // We only do this under the following conditions.
  3702. //
  3703. // 1 - There is an attribute list present.
  3704. // 2 - There is an entry following the current entry in
  3705. // the attribute list.
  3706. // 3 - The lowest Vcn for that following entry is non-zero.
  3707. //
  3708. if (Context->AttributeList.Bcb != NULL) {
  3709. PATTRIBUTE_LIST_ENTRY NextEntry;
  3710. NextEntry = (PATTRIBUTE_LIST_ENTRY) NtfsGetNextRecord( Context->AttributeList.Entry );
  3711. if (NextEntry < Context->AttributeList.BeyondFinalEntry) {
  3712. if ( NextEntry->LowestVcn != 0) {
  3713. ReinitializeContext = TRUE;
  3714. }
  3715. }
  3716. }
  3717. //
  3718. // Before we delete the allocation and purge the cache - flush any metadata in case
  3719. // we fail at some point later so we don't lose anything due to the purge. This
  3720. // is extra i/o when the delete works as expected but the amount of dirty metadata
  3721. // is limited by both metadata size and the fact its aggressively flushed by cc anyway
  3722. // the only case when this results in a real flush would be when an attribute like
  3723. // a reparse point is very quickly created and deleted
  3724. //
  3725. if (TempFileObject && (!NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) || FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE ))) {
  3726. IO_STATUS_BLOCK Iosb;
  3727. CcFlushCache( TempFileObject->SectionObjectPointer, NULL, 0, &Iosb );
  3728. if (Iosb.Status != STATUS_SUCCESS) {
  3729. NtfsRaiseStatus( IrpContext, Iosb.Status, &Scb->Fcb->FileReference, Scb->Fcb );
  3730. }
  3731. }
  3732. NtfsDeleteAllocation( IrpContext,
  3733. TempFileObject,
  3734. Scb,
  3735. *(PVCN)&Li0,
  3736. MAXLONGLONG,
  3737. LogIt,
  3738. BreakupAllowed );
  3739. //
  3740. // Purge all the data - if any is left in case the cache manager didn't
  3741. // due to the attribute being accessed with the pin interface
  3742. //
  3743. if (TempFileObject) {
  3744. CcPurgeCacheSection( TempFileObject->SectionObjectPointer, NULL, 0, FALSE );
  3745. }
  3746. //
  3747. // Reread the attribute if we need to.
  3748. //
  3749. if (ReinitializeContext) {
  3750. NtfsCleanupAttributeContext( IrpContext, Context );
  3751. NtfsInitializeAttributeContext( Context );
  3752. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, Context );
  3753. }
  3754. } finally {
  3755. DebugUnwind( NtfsDeleteAllocationFromRecord );
  3756. if (ScbAcquired) {
  3757. NtfsReleaseScb( IrpContext, Scb );
  3758. }
  3759. }
  3760. return;
  3761. }
  3762. //
  3763. // This routine is intended for use by allocsup.c. Other callers should use
  3764. // the routines in allocsup.
  3765. //
  3766. BOOLEAN
  3767. NtfsCreateAttributeWithAllocation (
  3768. IN PIRP_CONTEXT IrpContext,
  3769. IN PSCB Scb,
  3770. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  3771. IN PUNICODE_STRING AttributeName OPTIONAL,
  3772. IN USHORT AttributeFlags,
  3773. IN BOOLEAN LogIt,
  3774. IN BOOLEAN UseContext,
  3775. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  3776. )
  3777. /*++
  3778. Routine Description:
  3779. This routine creates the specified attribute with allocation, and returns a
  3780. description of it via the attribute context. If the amount of space being
  3781. created is small enough, we do all of the work here. Otherwise we create the
  3782. initial attribute and call NtfsAddAttributeAllocation to add the rest (in order
  3783. to keep the more complex logic in one place).
  3784. On successful return, it is up to the caller to clean up the attribute
  3785. context.
  3786. Arguments:
  3787. Scb - Current stream.
  3788. AttributeTypeCode - Type code of the attribute to create.
  3789. AttributeName - Optional name for attribute.
  3790. AttributeFlags - Desired flags for the created attribute.
  3791. WhereIndexed - Optionally supplies the file reference to the file where
  3792. this attribute is indexed.
  3793. LogIt - Most callers should specify TRUE, to have the change logged. However,
  3794. we can specify FALSE if we are creating a new file record, and
  3795. will be logging the entire new file record.
  3796. UseContext - Indicates if the context is pointing at the location for the attribute.
  3797. Context - A handle to the created attribute. This context is in a indeterminate
  3798. state on return.
  3799. Return Value:
  3800. BOOLEAN - TRUE if we created the attribute with all the allocation. FALSE
  3801. otherwise. We should only return FALSE if we are creating a file
  3802. and don't want to log any of the changes to the file record.
  3803. --*/
  3804. {
  3805. UCHAR AttributeBuffer[SIZEOF_FULL_NONRES_ATTR_HEADER];
  3806. UCHAR MappingPairsBuffer[64];
  3807. ULONG RecordOffset;
  3808. PATTRIBUTE_RECORD_HEADER Attribute;
  3809. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  3810. ULONG SizeNeeded;
  3811. ULONG AttrSizeNeeded;
  3812. PCHAR MappingPairs;
  3813. ULONG MappingPairsLength;
  3814. LCN Lcn;
  3815. VCN LastVcn;
  3816. VCN HighestVcn;
  3817. PVCB Vcb;
  3818. ULONG Passes = 0;
  3819. PFCB Fcb = Scb->Fcb;
  3820. PNTFS_MCB Mcb = &Scb->Mcb;
  3821. ULONG AttributeHeaderSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  3822. BOOLEAN AllocateAll = TRUE;
  3823. UCHAR CompressionShift = 0;
  3824. ASSERT_IRP_CONTEXT( IrpContext );
  3825. ASSERT_FCB( Fcb );
  3826. PAGED_CODE();
  3827. ASSERT( (AttributeFlags == 0) ||
  3828. NtfsIsTypeCodeCompressible( AttributeTypeCode ));
  3829. Vcb = Fcb->Vcb;
  3830. //
  3831. // Clear out the invalid attribute flags for this volume.
  3832. //
  3833. AttributeFlags &= Vcb->AttributeFlagsMask;
  3834. DebugTrace( +1, Dbg, ("NtfsCreateAttributeWithAllocation\n") );
  3835. DebugTrace( 0, Dbg, ("Mcb = %08lx\n", Mcb) );
  3836. //
  3837. // Calculate the size needed for this attribute. (We say we have
  3838. // Vcb->BigEnoughToMove bytes available as a short cut, since we
  3839. // will extend later as required anyway. It should be extremely
  3840. // unusual that we would really have to extend.)
  3841. //
  3842. MappingPairsLength = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  3843. Vcb->BigEnoughToMove,
  3844. (LONGLONG)0,
  3845. NULL,
  3846. &LastVcn ));
  3847. //
  3848. // Extra work for compressed / sparse files
  3849. //
  3850. if (FlagOn( AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  3851. LONGLONG ClustersInCompressionUnit;
  3852. //
  3853. // Calculate the compression unit size.
  3854. //
  3855. CompressionShift = NTFS_CLUSTERS_PER_COMPRESSION;
  3856. //
  3857. // If this generates a compression unit past 64K then we need to shrink
  3858. // the shift value. This can only happen for sparse files.
  3859. //
  3860. if (!FlagOn( AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  3861. while (Vcb->SparseFileClusters < (ULONG) (1 << CompressionShift)) {
  3862. CompressionShift -= 1;
  3863. }
  3864. }
  3865. ClustersInCompressionUnit = 1 << CompressionShift;
  3866. //
  3867. // Round the LastVcn down to a compression unit and recalc the size
  3868. // needed for the mapping pairs if it was truncated. Note LastVcn = 1 + actual stop pt
  3869. // if we didn't allocate everything in which case it == maxlonglong
  3870. //
  3871. if (LastVcn != MAXLONGLONG) {
  3872. VCN RoundedLastVcn;
  3873. //
  3874. // LastVcn is the cluster beyond allocation or the allocation size i.e stop at 0 we have 1 cluster
  3875. // we want the new allocation to be a compression unit mult so the stop point should be
  3876. // a compression unit rounded allocation - 1
  3877. // Note LastVcn will == RoundedLastVcn + 1 on exit from GetSizeForMappingPairs
  3878. //
  3879. RoundedLastVcn = (LastVcn & ~(ClustersInCompressionUnit - 1)) - 1;
  3880. MappingPairsLength = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  3881. Vcb->BigEnoughToMove,
  3882. (LONGLONG)0,
  3883. &RoundedLastVcn,
  3884. &LastVcn ));
  3885. ASSERT( (LastVcn & (ClustersInCompressionUnit - 1)) == 0 );
  3886. }
  3887. //
  3888. // Remember the size of the attribute header needed for this file.
  3889. //
  3890. AttributeHeaderSize = SIZEOF_FULL_NONRES_ATTR_HEADER;
  3891. }
  3892. SizeNeeded = AttributeHeaderSize +
  3893. MappingPairsLength +
  3894. (ARGUMENT_PRESENT(AttributeName) ?
  3895. QuadAlign( AttributeName->Length ) : 0);
  3896. AttrSizeNeeded = SizeNeeded;
  3897. //
  3898. // Loop until we find all the space we need.
  3899. //
  3900. do {
  3901. //
  3902. // Reinitialize context if this is not the first pass.
  3903. //
  3904. if (Passes != 0) {
  3905. NtfsCleanupAttributeContext( IrpContext, Context );
  3906. NtfsInitializeAttributeContext( Context );
  3907. }
  3908. Passes += 1;
  3909. //
  3910. // Hope we will never have to loop thru this that many times.
  3911. // If so, we will have to bump up the threshold again or change
  3912. // the algorithm.
  3913. //
  3914. ASSERT( Passes < 6 );
  3915. //
  3916. // If the attribute is not indexed, then we will position to the
  3917. // insertion point by type code and name.
  3918. //
  3919. if (!UseContext &&
  3920. NtfsLookupAttributeByName( IrpContext,
  3921. Fcb,
  3922. &Fcb->FileReference,
  3923. AttributeTypeCode,
  3924. AttributeName,
  3925. NULL,
  3926. FALSE,
  3927. Context )) {
  3928. DebugTrace( 0, 0,
  3929. ("Nonresident attribute already exists, TypeCode = %08lx\n",
  3930. AttributeTypeCode) );
  3931. ASSERTMSG("Nonresident attribute already exists, About to raise corrupt ", FALSE);
  3932. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  3933. }
  3934. //
  3935. // If this attribute is being positioned in the base file record and
  3936. // there is an attribute list then we need to ask for enough space
  3937. // for the attribute list entry now.
  3938. //
  3939. FileRecord = NtfsContainingFileRecord( Context );
  3940. Attribute = NtfsFoundAttribute( Context );
  3941. AttrSizeNeeded = SizeNeeded;
  3942. if (Context->AttributeList.Bcb != NULL
  3943. && (ULONG_PTR) FileRecord <= (ULONG_PTR) Context->AttributeList.AttributeList
  3944. && (ULONG_PTR) Attribute >= (ULONG_PTR) Context->AttributeList.AttributeList) {
  3945. //
  3946. // If the attribute list is non-resident then add a fudge factor of
  3947. // 16 bytes for any new retrieval information.
  3948. //
  3949. if (NtfsIsAttributeResident( Context->AttributeList.AttributeList )) {
  3950. AttrSizeNeeded += QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  3951. + (ARGUMENT_PRESENT( AttributeName ) ?
  3952. (ULONG) AttributeName->Length :
  3953. sizeof( WCHAR )));
  3954. } else {
  3955. AttrSizeNeeded += 0x10;
  3956. }
  3957. }
  3958. UseContext = FALSE;
  3959. //
  3960. // Ask for the space we need.
  3961. //
  3962. } while (!NtfsGetSpaceForAttribute( IrpContext, Fcb, AttrSizeNeeded, Context ));
  3963. //
  3964. // Now get the attribute pointer and fill it in.
  3965. //
  3966. FileRecord = NtfsContainingFileRecord(Context);
  3967. RecordOffset = (ULONG)((PCHAR)NtfsFoundAttribute(Context) - (PCHAR)FileRecord);
  3968. Attribute = (PATTRIBUTE_RECORD_HEADER)AttributeBuffer;
  3969. RtlZeroMemory( Attribute, SIZEOF_FULL_NONRES_ATTR_HEADER );
  3970. Attribute->TypeCode = AttributeTypeCode;
  3971. Attribute->RecordLength = SizeNeeded;
  3972. Attribute->FormCode = NONRESIDENT_FORM;
  3973. //
  3974. // Assume no attribute name, and calculate where the Mapping Pairs
  3975. // will go. (Update below if we are wrong.)
  3976. //
  3977. MappingPairs = Add2Ptr( Attribute, AttributeHeaderSize );
  3978. //
  3979. // If the attribute has a name, take care of that now.
  3980. //
  3981. if (ARGUMENT_PRESENT(AttributeName)
  3982. && AttributeName->Length != 0) {
  3983. ASSERT( AttributeName->Length <= 0x1FF );
  3984. Attribute->NameLength = (UCHAR)(AttributeName->Length / sizeof(WCHAR));
  3985. Attribute->NameOffset = (USHORT)AttributeHeaderSize;
  3986. MappingPairs += QuadAlign( AttributeName->Length );
  3987. }
  3988. Attribute->Flags = AttributeFlags;
  3989. Attribute->Instance = FileRecord->NextAttributeInstance;
  3990. //
  3991. // If someone repeatedly adds and removes attributes from a file record we could
  3992. // hit a case where the sequence number will overflow. In this case we
  3993. // want to scan the file record and find an earlier free instance number.
  3994. //
  3995. if (Attribute->Instance > NTFS_CHECK_INSTANCE_ROLLOVER) {
  3996. Attribute->Instance = NtfsScanForFreeInstance( IrpContext, Vcb, FileRecord );
  3997. }
  3998. //
  3999. // We always need the mapping pairs offset.
  4000. //
  4001. Attribute->Form.Nonresident.MappingPairsOffset = (USHORT)(MappingPairs -
  4002. (PCHAR)Attribute);
  4003. //
  4004. // Set up the compression unit size.
  4005. //
  4006. Attribute->Form.Nonresident.CompressionUnit = CompressionShift;
  4007. //
  4008. // Now we need to point to the real place to build the mapping pairs buffer.
  4009. // If they will not be too big we can use our internal buffer.
  4010. //
  4011. MappingPairs = MappingPairsBuffer;
  4012. if (MappingPairsLength > 64) {
  4013. MappingPairs = NtfsAllocatePool( NonPagedPool, MappingPairsLength );
  4014. }
  4015. *MappingPairs = 0;
  4016. //
  4017. // Find how much space is allocated by finding the last Mcb entry and
  4018. // looking it up. If there are no entries, all of the subsequent
  4019. // fields are already zeroed.
  4020. //
  4021. Attribute->Form.Nonresident.HighestVcn =
  4022. HighestVcn = -1;
  4023. if (NtfsLookupLastNtfsMcbEntry( Mcb, &HighestVcn, &Lcn )) {
  4024. ASSERT_LCN_RANGE_CHECKING( Vcb, Lcn );
  4025. //
  4026. // Now build the mapping pairs in place.
  4027. //
  4028. NtfsBuildMappingPairs( Mcb,
  4029. 0,
  4030. &LastVcn,
  4031. MappingPairs );
  4032. Attribute->Form.Nonresident.HighestVcn = LastVcn;
  4033. //
  4034. // Fill in the nonresident-specific fields. We set the allocation
  4035. // size to only include the Vcn's we included in the mapping pairs.
  4036. //
  4037. Attribute->Form.Nonresident.AllocatedLength =
  4038. Int64ShllMod32((LastVcn + 1 ), Vcb->ClusterShift);
  4039. //
  4040. // The totally allocated field in the Scb will contain the current allocated
  4041. // value for this stream.
  4042. //
  4043. if (FlagOn( AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  4044. ASSERT( Scb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA );
  4045. Attribute->Form.Nonresident.TotalAllocated = Scb->TotalAllocated;
  4046. ASSERT( ((LastVcn + 1) & ((1 << CompressionShift) - 1)) == 0 );
  4047. }
  4048. //
  4049. // We are creating a attribute with zero allocation. Make the Vcn sizes match
  4050. // so we don't make the call below to AddAttributeAllocation.
  4051. //
  4052. } else {
  4053. LastVcn = HighestVcn;
  4054. }
  4055. //
  4056. // Now we will actually create the attribute in place, so that we
  4057. // save copying everything twice, and can point to the final image
  4058. // for the log write below.
  4059. //
  4060. NtfsRestartInsertAttribute( IrpContext,
  4061. FileRecord,
  4062. RecordOffset,
  4063. Attribute,
  4064. AttributeName,
  4065. MappingPairs,
  4066. MappingPairsLength );
  4067. //
  4068. // Finally, log the creation of this attribute
  4069. //
  4070. if (LogIt) {
  4071. //
  4072. // We have actually created the attribute above, but the write
  4073. // log below could fail. The reason we did the create already
  4074. // was to avoid having to allocate pool and copy everything
  4075. // twice (header, name and value). Our normal error recovery
  4076. // just recovers from the log file. But if we fail to write
  4077. // the log, we have to remove this attribute by hand, and
  4078. // raise the condition again.
  4079. //
  4080. try {
  4081. FileRecord->Lsn =
  4082. NtfsWriteLog( IrpContext,
  4083. Vcb->MftScb,
  4084. NtfsFoundBcb(Context),
  4085. CreateAttribute,
  4086. Add2Ptr(FileRecord, RecordOffset),
  4087. Attribute->RecordLength,
  4088. DeleteAttribute,
  4089. NULL,
  4090. 0,
  4091. NtfsMftOffset( Context ),
  4092. RecordOffset,
  4093. 0,
  4094. Vcb->BytesPerFileRecordSegment );
  4095. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  4096. NtfsRestartRemoveAttribute( IrpContext, FileRecord, RecordOffset );
  4097. if (MappingPairs != MappingPairsBuffer) {
  4098. NtfsFreePool( MappingPairs );
  4099. }
  4100. NtfsRaiseStatus( IrpContext, GetExceptionCode(), NULL, NULL );
  4101. }
  4102. }
  4103. //
  4104. // Free the mapping pairs buffer if we allocated one.
  4105. //
  4106. if (MappingPairs != MappingPairsBuffer) {
  4107. NtfsFreePool( MappingPairs );
  4108. }
  4109. //
  4110. // Now add it to the attribute list if necessary
  4111. //
  4112. if (Context->AttributeList.Bcb != NULL) {
  4113. MFT_SEGMENT_REFERENCE SegmentReference;
  4114. *(PLONGLONG)&SegmentReference = LlFileRecordsFromBytes( Vcb, NtfsMftOffset( Context ));
  4115. SegmentReference.SequenceNumber = FileRecord->SequenceNumber;
  4116. NtfsAddToAttributeList( IrpContext, Fcb, SegmentReference, Context );
  4117. }
  4118. //
  4119. // Reflect the current allocation in the scb - in case we take the path below
  4120. //
  4121. Scb->Header.AllocationSize.QuadPart = Attribute->Form.Nonresident.AllocatedLength;
  4122. //
  4123. // We couldn't create all of the mapping for the allocation above. If
  4124. // this is a create then we want to truncate the allocation to what we
  4125. // have already allocated. Otherwise we want to call
  4126. // NtfsAddAttributeAllocation to map the remaining allocation.
  4127. //
  4128. if (LastVcn != HighestVcn) {
  4129. if (LogIt ||
  4130. !NtfsIsTypeCodeUserData( AttributeTypeCode ) ||
  4131. IrpContext->MajorFunction != IRP_MJ_CREATE) {
  4132. NtfsAddAttributeAllocation( IrpContext, Scb, Context, NULL, NULL );
  4133. } else {
  4134. //
  4135. // Truncate away the clusters beyond the last Vcn and set the
  4136. // flag in the IrpContext indicating there is more allocation
  4137. // to do.
  4138. //
  4139. NtfsDeallocateClusters( IrpContext,
  4140. Fcb->Vcb,
  4141. Scb,
  4142. LastVcn + 1,
  4143. MAXLONGLONG,
  4144. NULL );
  4145. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  4146. LastVcn + 1,
  4147. MAXLONGLONG,
  4148. TRUE,
  4149. FALSE );
  4150. if (FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA )) {
  4151. LONGLONG Delta = LlBytesFromClusters( Fcb->Vcb, LastVcn - HighestVcn );
  4152. ASSERT( NtfsIsTypeCodeSubjectToQuota( AttributeTypeCode ));
  4153. ASSERT( NtfsIsTypeCodeSubjectToQuota( Scb->AttributeTypeCode ));
  4154. //
  4155. // Return any quota charged.
  4156. //
  4157. NtfsConditionallyUpdateQuota( IrpContext,
  4158. Fcb,
  4159. &Delta,
  4160. LogIt,
  4161. TRUE );
  4162. }
  4163. AllocateAll = FALSE;
  4164. }
  4165. }
  4166. DebugTrace( -1, Dbg, ("NtfsCreateAttributeWithAllocation -> VOID\n") );
  4167. return AllocateAll;
  4168. }
  4169. //
  4170. // This routine is intended for use by allocsup.c. Other callers should use
  4171. // the routines in allocsup.
  4172. //
  4173. VOID
  4174. NtfsAddAttributeAllocation (
  4175. IN PIRP_CONTEXT IrpContext,
  4176. IN PSCB Scb,
  4177. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context,
  4178. IN PVCN StartingVcn OPTIONAL,
  4179. IN PVCN ClusterCount OPTIONAL
  4180. )
  4181. /*++
  4182. Routine Description:
  4183. This routine adds space to an existing nonresident attribute.
  4184. The caller specifies the attribute to be changed via the attribute context,
  4185. and must be prepared to clean up this context no matter how this routine
  4186. returns.
  4187. This routine procedes in the following steps, whose numbers correspond
  4188. to the numbers in comments below:
  4189. 1. Save a description of the current attribute.
  4190. 2. Figure out how big the attribute would have to be to store all
  4191. of the new run information.
  4192. 3. Find the last occurrence of the attribute, to which the new
  4193. allocation is to be appended.
  4194. 4. If the attribute is getting very large and will not fit, then
  4195. move it to its own file record. In any case grow the attribute
  4196. enough to fit either all of the new allocation, or as much as
  4197. possible.
  4198. 5. Construct the new mapping pairs in place, and log the change.
  4199. 6. If there is still more allocation to describe, then loop to
  4200. create new file records and initialize them to describe additional
  4201. allocation until all of the allocation is described.
  4202. Arguments:
  4203. Scb - Current stream.
  4204. Context - Attribute Context positioned at the attribute to change. Note
  4205. that unlike other routines, this parameter is left in an
  4206. indeterminate state upon return. The caller should plan on
  4207. doing nothing other than cleaning it up.
  4208. StartingVcn - Supplies Vcn to start on, if not the new highest vcn
  4209. ClusterCount - Supplies count of clusters being added, if not the new highest vcn
  4210. Return Value:
  4211. None.
  4212. --*/
  4213. {
  4214. PATTRIBUTE_RECORD_HEADER Attribute;
  4215. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  4216. ULONG NewSize, MappingPairsSize;
  4217. LONG SizeChange;
  4218. PCHAR MappingPairs;
  4219. ULONG SizeAvailable;
  4220. PVCB Vcb;
  4221. VCN LowestVcnRemapped;
  4222. LONGLONG LocalClusterCount;
  4223. VCN OldHighestVcn;
  4224. VCN NewHighestVcn;
  4225. VCN LastVcn;
  4226. BOOLEAN IsHotFixScb;
  4227. PBCB NewBcb = NULL;
  4228. LONGLONG MftReferenceNumber;
  4229. PFCB Fcb = Scb->Fcb;
  4230. PNTFS_MCB Mcb = &Scb->Mcb;
  4231. ULONG AttributeHeaderSize;
  4232. BOOLEAN SingleHole;
  4233. ASSERT_IRP_CONTEXT( IrpContext );
  4234. PAGED_CODE();
  4235. Vcb = Fcb->Vcb;
  4236. DebugTrace( +1, Dbg, ("NtfsAddAttributeAllocation\n") );
  4237. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  4238. DebugTrace( 0, Dbg, ("Mcb = %08lx\n", Mcb) );
  4239. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  4240. //
  4241. // Make a local copy of cluster count, if given. We will use this local
  4242. // copy to determine the shrinking range if we move to a previous file
  4243. // record on a second pass through this loop.
  4244. //
  4245. if (ARGUMENT_PRESENT( ClusterCount )) {
  4246. LocalClusterCount = *ClusterCount;
  4247. }
  4248. while (TRUE) {
  4249. //
  4250. // Make sure the buffer is pinned.
  4251. //
  4252. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  4253. //
  4254. // Make sure we cleanup on the way out
  4255. //
  4256. try {
  4257. //
  4258. // Step 1.
  4259. //
  4260. // Save a description of the attribute to help us look it up
  4261. // again, and to make clones if necessary.
  4262. //
  4263. Attribute = NtfsFoundAttribute(Context);
  4264. //
  4265. // Do some basic verification of the on disk and in memory filesizes
  4266. // If they're disjoint - usually due to a failed abort raise corrupt again
  4267. //
  4268. if ((Attribute->FormCode != NONRESIDENT_FORM) ||
  4269. (Attribute->Form.Nonresident.AllocatedLength != Scb->Header.AllocationSize.QuadPart)) {
  4270. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4271. }
  4272. ASSERT(Attribute->Form.Nonresident.LowestVcn == 0);
  4273. OldHighestVcn = LlClustersFromBytes(Vcb, Attribute->Form.Nonresident.AllocatedLength) - 1;
  4274. //
  4275. // Get the file record pointer.
  4276. //
  4277. FileRecord = NtfsContainingFileRecord( Context );
  4278. //
  4279. // Step 2.
  4280. //
  4281. // Come up with the Vcn we will stop on. If a StartingVcn and ClusterCount
  4282. // were specified, then use them to calculate where we will stop. Otherwise
  4283. // lookup the largest Vcn in this Mcb, so that we will know when we are done.
  4284. // We will also write the new allocation size here.
  4285. //
  4286. {
  4287. LCN TempLcn;
  4288. BOOLEAN UpdateFileSizes = FALSE;
  4289. NewHighestVcn = -1;
  4290. //
  4291. // If a StartingVcn and ClusterCount were specified, then use them.
  4292. //
  4293. if (ARGUMENT_PRESENT(StartingVcn)) {
  4294. ASSERT(ARGUMENT_PRESENT(ClusterCount));
  4295. NewHighestVcn = (*StartingVcn + LocalClusterCount) - 1;
  4296. //
  4297. // If there are no entries in the file record then we have no new
  4298. // sizes to report.
  4299. //
  4300. } else if (NtfsLookupLastNtfsMcbEntry(Mcb, &NewHighestVcn, &TempLcn)) {
  4301. //
  4302. // For compressed files, make sure we are not shrinking allocation
  4303. // size (OldHighestVcn) due to a compression unit that was all zeros
  4304. // and has no allocation. Note, truncates are done in
  4305. // NtfsDeleteAttributeAllocation, so we should not be shrinking the
  4306. // file here.
  4307. //
  4308. // If this is an attribute being written compressed, then always
  4309. // insure that we keep the allocation size on a compression unit
  4310. // boundary, by pushing NewHighestVcn to a boundary - 1.
  4311. //
  4312. if (Scb->CompressionUnit != 0) {
  4313. //
  4314. // Don't shrink the file on this path.
  4315. //
  4316. if (OldHighestVcn > NewHighestVcn) {
  4317. NewHighestVcn = OldHighestVcn;
  4318. }
  4319. ((PLARGE_INTEGER) &NewHighestVcn)->LowPart |= ClustersFromBytes(Vcb, Scb->CompressionUnit) - 1;
  4320. //
  4321. // Make sure we didn't push a hole into the next compression
  4322. // unit. If so then truncate to the current NewHighestVcn. We
  4323. // know this will be on a compression unit boundary.
  4324. //
  4325. if (NewHighestVcn < Scb->Mcb.NtfsMcbArray[Scb->Mcb.NtfsMcbArraySizeInUse - 1].EndingVcn) {
  4326. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  4327. NewHighestVcn + 1,
  4328. MAXLONGLONG,
  4329. TRUE,
  4330. FALSE );
  4331. }
  4332. }
  4333. }
  4334. //
  4335. // Copy the new allocation size into our size structure and
  4336. // update the attribute.
  4337. //
  4338. ASSERT( Scb->Header.AllocationSize.QuadPart != 0 || NewHighestVcn > OldHighestVcn );
  4339. if (NewHighestVcn > OldHighestVcn) {
  4340. Scb->Header.AllocationSize.QuadPart = LlBytesFromClusters(Fcb->Vcb, NewHighestVcn + 1);
  4341. UpdateFileSizes = TRUE;
  4342. }
  4343. //
  4344. // If we moved the allocation size up or the totally allocated does
  4345. // not match the value on the disk (only for compressed files,
  4346. // then update the file sizes.
  4347. //
  4348. if (UpdateFileSizes ||
  4349. (FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ) &&
  4350. (Attribute->Form.Nonresident.TotalAllocated != Scb->TotalAllocated))) {
  4351. NtfsWriteFileSizes( IrpContext,
  4352. Scb,
  4353. &Scb->Header.ValidDataLength.QuadPart,
  4354. FALSE,
  4355. TRUE,
  4356. TRUE );
  4357. }
  4358. }
  4359. //
  4360. // Step 3.
  4361. //
  4362. // Lookup the attribute record at which the change begins, if it is not
  4363. // the first file record that we are looking at.
  4364. //
  4365. if ((Attribute->Form.Nonresident.HighestVcn != OldHighestVcn) &&
  4366. (NewHighestVcn > Attribute->Form.Nonresident.HighestVcn)) {
  4367. NtfsCleanupAttributeContext( IrpContext, Context );
  4368. NtfsInitializeAttributeContext( Context );
  4369. NtfsLookupAttributeForScb( IrpContext, Scb, &NewHighestVcn, Context );
  4370. Attribute = NtfsFoundAttribute(Context);
  4371. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  4372. FileRecord = NtfsContainingFileRecord(Context);
  4373. }
  4374. //
  4375. // Make sure we nuke this range if we get an error, by expanding
  4376. // the error recovery range.
  4377. //
  4378. if (Scb->Mcb.PoolType == PagedPool) {
  4379. if (Scb->ScbSnapshot != NULL) {
  4380. if (Attribute->Form.Nonresident.LowestVcn < Scb->ScbSnapshot->LowestModifiedVcn) {
  4381. Scb->ScbSnapshot->LowestModifiedVcn = Attribute->Form.Nonresident.LowestVcn;
  4382. }
  4383. if (NewHighestVcn > Scb->ScbSnapshot->HighestModifiedVcn) {
  4384. Scb->ScbSnapshot->HighestModifiedVcn = NewHighestVcn;
  4385. }
  4386. if (Attribute->Form.Nonresident.HighestVcn > Scb->ScbSnapshot->HighestModifiedVcn) {
  4387. Scb->ScbSnapshot->HighestModifiedVcn = Attribute->Form.Nonresident.HighestVcn;
  4388. }
  4389. }
  4390. }
  4391. //
  4392. // Remember the last Vcn we will need to create mapping pairs
  4393. // for. We use either NewHighestVcn or the highest Vcn in this
  4394. // file record in the case that we are just inserting a run into
  4395. // an existing record.
  4396. //
  4397. if (ARGUMENT_PRESENT(StartingVcn)) {
  4398. if (Attribute->Form.Nonresident.HighestVcn > NewHighestVcn) {
  4399. NewHighestVcn = Attribute->Form.Nonresident.HighestVcn;
  4400. }
  4401. }
  4402. //
  4403. // Remember the lowest Vcn for this attribute. We will use this to
  4404. // decide whether to loop back and look for an earlier file record.
  4405. //
  4406. LowestVcnRemapped = Attribute->Form.Nonresident.LowestVcn;
  4407. //
  4408. // Remember the header size for this attribute. This will be the
  4409. // mapping pairs offset except for attributes with names.
  4410. //
  4411. AttributeHeaderSize = Attribute->Form.Nonresident.MappingPairsOffset;
  4412. if (Attribute->NameOffset != 0) {
  4413. AttributeHeaderSize = Attribute->NameOffset;
  4414. }
  4415. //
  4416. // If we are making space for a totally allocated field then we
  4417. // want to add space to the non-resident header for these entries.
  4418. // To detect this we know that a starting Vcn was specified and
  4419. // we specified exactly the entire file record. Also the major
  4420. // and minor Irp codes are exactly that for a compression operation.
  4421. //
  4422. if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
  4423. (IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
  4424. (IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp)->Parameters.FileSystemControl.FsControlCode == FSCTL_SET_COMPRESSION) &&
  4425. ARGUMENT_PRESENT( StartingVcn ) &&
  4426. (*StartingVcn == 0) &&
  4427. (LocalClusterCount == Attribute->Form.Nonresident.HighestVcn + 1)) {
  4428. AttributeHeaderSize += sizeof( LONGLONG );
  4429. }
  4430. //
  4431. // Now we must make sure that we never ask for more than can fit in
  4432. // one file record with our attribute and a $END record.
  4433. //
  4434. SizeAvailable = NtfsMaximumAttributeSize(Vcb->BytesPerFileRecordSegment) -
  4435. AttributeHeaderSize -
  4436. QuadAlign( Scb->AttributeName.Length );
  4437. //
  4438. // For the Mft, we will leave a "fudge factor" of 1/8th a file record
  4439. // free to make sure that possible hot fixes do not cause us to
  4440. // break the bootstrap process to finding the mapping for the Mft.
  4441. // Only take this action if we already have an attribute list for
  4442. // the Mft, otherwise we may not detect when we need to move to own
  4443. // record.
  4444. //
  4445. IsHotFixScb = NtfsIsTopLevelHotFixScb( Scb );
  4446. if ((Scb == Vcb->MftScb) &&
  4447. (Context->AttributeList.Bcb != NULL) &&
  4448. !IsHotFixScb &&
  4449. !ARGUMENT_PRESENT( StartingVcn )) {
  4450. SizeAvailable -= Vcb->MftCushion;
  4451. }
  4452. //
  4453. // Calculate how much space is actually needed, independent of whether it will
  4454. // fit.
  4455. //
  4456. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4457. SizeAvailable,
  4458. Attribute->Form.Nonresident.LowestVcn,
  4459. &NewHighestVcn,
  4460. &LastVcn ));
  4461. NewSize = AttributeHeaderSize + QuadAlign( Scb->AttributeName.Length ) + MappingPairsSize;
  4462. SizeChange = (LONG)NewSize - (LONG)Attribute->RecordLength;
  4463. //
  4464. // Step 4.
  4465. //
  4466. // Here we decide if we need to move the attribute to its own record,
  4467. // or whether there is enough room to grow it in place.
  4468. //
  4469. {
  4470. VCN LowestVcn;
  4471. ULONG Pass = 0;
  4472. //
  4473. // It is important to note that at this point, if we will need an
  4474. // attribute list attribute, then we will already have it. This is
  4475. // because we calculated the size needed for the attribute, and moved
  4476. // to a our own record if we were not going to fit and we were not
  4477. // already in a separate record. Later on we assume that the attribute
  4478. // list exists, and just add to it as required. If we didn't move to
  4479. // own record because this is the Mft and this is not file record 0,
  4480. // then we already have an attribute list from a previous split.
  4481. //
  4482. do {
  4483. //
  4484. // If not the first pass, we have to lookup the attribute
  4485. // again. (It looks terrible to have to refind an attribute
  4486. // record other than the first one, but this should never
  4487. // happen, since subsequent attributes should always be in
  4488. // their own record.)
  4489. //
  4490. if (Pass != 0) {
  4491. NtfsCleanupAttributeContext( IrpContext, Context );
  4492. NtfsInitializeAttributeContext( Context );
  4493. if (!NtfsLookupAttributeByName( IrpContext,
  4494. Fcb,
  4495. &Fcb->FileReference,
  4496. Scb->AttributeTypeCode,
  4497. &Scb->AttributeName,
  4498. &LowestVcn,
  4499. FALSE,
  4500. Context )) {
  4501. ASSERT( FALSE );
  4502. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  4503. }
  4504. }
  4505. Pass += 1;
  4506. //
  4507. // Now we have to reload our pointers
  4508. //
  4509. Attribute = NtfsFoundAttribute(Context);
  4510. FileRecord = NtfsContainingFileRecord(Context);
  4511. //
  4512. // If the attribute doesn't fit, and it is not alone in this file
  4513. // record, and the attribute is big enough to move, then we will
  4514. // have to take some special action. Note that if we do not already
  4515. // have an attribute list, then we will only do the move if we are
  4516. // currently big enough to move, otherwise there may not be enough
  4517. // space in MoveAttributeToOwnRecord to create the attribute list,
  4518. // and that could cause us to recursively try to create the attribute
  4519. // list in Create Attribute With Value.
  4520. //
  4521. // We won't make this move if we are dealing with the Mft and it
  4522. // is not file record 0.
  4523. //
  4524. // Also we never move an attribute list to its own record.
  4525. //
  4526. if ((Attribute->TypeCode != $ATTRIBUTE_LIST)
  4527. &&
  4528. (SizeChange > (LONG)(FileRecord->BytesAvailable - FileRecord->FirstFreeByte))
  4529. &&
  4530. ((NtfsFirstAttribute(FileRecord) != Attribute) ||
  4531. (((PATTRIBUTE_RECORD_HEADER)NtfsGetNextRecord(Attribute))->TypeCode != $END))
  4532. &&
  4533. (((NewSize >= Vcb->BigEnoughToMove) && (Context->AttributeList.Bcb != NULL)) ||
  4534. (Attribute->RecordLength >= Vcb->BigEnoughToMove))
  4535. &&
  4536. ((Scb != Vcb->MftScb)
  4537. ||
  4538. (*(PLONGLONG)&FileRecord->BaseFileRecordSegment == 0))) {
  4539. //
  4540. // If we are moving the Mft $DATA out of the base file record, the
  4541. // attribute context will point to the split portion on return.
  4542. // The attribute will only contain previously existing mapping, none
  4543. // of the additional clusters which exist in the Mcb.
  4544. //
  4545. ASSERT( NewBcb == NULL ); // in case we were looping without unpinning
  4546. MftReferenceNumber = MoveAttributeToOwnRecord( IrpContext,
  4547. Fcb,
  4548. Attribute,
  4549. Context,
  4550. &NewBcb,
  4551. &FileRecord );
  4552. Attribute = NtfsFirstAttribute(FileRecord);
  4553. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  4554. FileRecord = NtfsContainingFileRecord(Context);
  4555. //
  4556. // If this is the MftScb then we need to recheck the size needed for the
  4557. // mapping pairs. The test for the Mft above guarantees that we
  4558. // were dealing with the base file record.
  4559. //
  4560. if (Scb == Vcb->MftScb) {
  4561. LastVcn = LastVcn - 1;
  4562. //
  4563. // Calculate how much space is now needed given our new LastVcn.
  4564. //
  4565. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4566. SizeAvailable,
  4567. Attribute->Form.Nonresident.LowestVcn,
  4568. &LastVcn,
  4569. &LastVcn ));
  4570. }
  4571. }
  4572. //
  4573. // Remember the lowest Vcn so that we can find this record again
  4574. // if we have to. We capture the value now, after the move attribute
  4575. // in case this is the Mft doing a split and the entire attribute
  4576. // didn't move. We depend on MoveAttributeToOwnRecord to return
  4577. // the new file record for the Mft split.
  4578. //
  4579. LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  4580. //
  4581. // If FALSE is returned, then the space was not allocated and
  4582. // we have to loop back and try again. Second time must work.
  4583. //
  4584. } while (!NtfsChangeAttributeSize( IrpContext,
  4585. Fcb,
  4586. NewSize,
  4587. Context ));
  4588. //
  4589. // Now we have to reload our pointers
  4590. //
  4591. Attribute = NtfsFoundAttribute(Context);
  4592. FileRecord = NtfsContainingFileRecord(Context);
  4593. }
  4594. //
  4595. // Step 5.
  4596. //
  4597. // Get pointer to mapping pairs
  4598. //
  4599. {
  4600. ULONG AttributeOffset;
  4601. ULONG MappingPairsOffset;
  4602. CHAR MappingPairsBuffer[64];
  4603. ULONG RecordOffset = PtrOffset(FileRecord, Attribute);
  4604. //
  4605. // See if it is the case that all mapping pairs will not fit into
  4606. // the current file record, as we may wish to split in the middle
  4607. // rather than at the end as we are currently set up to do.
  4608. // We don't want to take this path if we are splitting the file record
  4609. // because of our limit on the range size due to maximum clusters per
  4610. // range.
  4611. //
  4612. if (LastVcn < NewHighestVcn) {
  4613. if (ARGUMENT_PRESENT( StartingVcn ) &&
  4614. (Scb != Vcb->MftScb)) {
  4615. LONGLONG TempCount;
  4616. //
  4617. // There are two cases to deal with. If the existing file record
  4618. // was a large hole then we may need to limit the size if we
  4619. // are adding allocation. In this case we don't want to simply
  4620. // split at the run being inserted. Otherwise we might end up
  4621. // creating a large number of file records containing only one
  4622. // run (the case where a user fills a large hole by working
  4623. // backwards). Pad the new file record with a portion of the hole.
  4624. //
  4625. if (LastVcn - Attribute->Form.Nonresident.LowestVcn > MAX_CLUSTERS_PER_RANGE) {
  4626. //
  4627. // We don't start within our maximum range from the beginning of the
  4628. // range. If we are within our limit from the end of the range
  4629. // then extend the new range backwards to reach our limit.
  4630. //
  4631. if ((NewHighestVcn - LastVcn + 1) < MAX_CLUSTERS_PER_RANGE) {
  4632. LastVcn = NewHighestVcn - MAX_CLUSTERS_PER_RANGE;
  4633. //
  4634. // Calculate how much space is now needed given our new LastVcn.
  4635. //
  4636. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4637. SizeAvailable,
  4638. Attribute->Form.Nonresident.LowestVcn,
  4639. &LastVcn,
  4640. &LastVcn ));
  4641. }
  4642. //
  4643. //
  4644. // In this case we have run out of room for mapping pairs via
  4645. // an overwrite somewhere in the middle of the file. To avoid
  4646. // shoving a couple mapping pairs off the end over and over, we
  4647. // will arbitrarily split this attribute in the middle. We do
  4648. // so by looking up the lowest and highest Vcns that we are working
  4649. // with and get their indices, then split in the middle.
  4650. //
  4651. } else if (MappingPairsSize > (SizeAvailable >> 1)) {
  4652. LCN TempLcn;
  4653. PVOID RangeLow, RangeHigh;
  4654. ULONG IndexLow, IndexHigh;
  4655. //
  4656. // Get the low and high Mcb indices for these runs.
  4657. //
  4658. if (!NtfsLookupNtfsMcbEntry( Mcb,
  4659. Attribute->Form.Nonresident.LowestVcn,
  4660. NULL,
  4661. NULL,
  4662. NULL,
  4663. NULL,
  4664. &RangeLow,
  4665. &IndexLow )) {
  4666. ASSERT( FALSE );
  4667. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  4668. }
  4669. //
  4670. // Point to the last Vcn we know is actually in the Mcb...
  4671. //
  4672. LastVcn = LastVcn - 1;
  4673. if (!NtfsLookupNtfsMcbEntry( Mcb,
  4674. LastVcn,
  4675. NULL,
  4676. NULL,
  4677. NULL,
  4678. NULL,
  4679. &RangeHigh,
  4680. &IndexHigh )) {
  4681. ASSERT( FALSE );
  4682. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4683. }
  4684. ASSERT(RangeLow == RangeHigh);
  4685. //
  4686. // Calculate the index in the middle.
  4687. //
  4688. IndexLow += (IndexHigh - IndexLow) /2;
  4689. //
  4690. // If we are inserting past the ValidDataToDisk (SplitMcb case),
  4691. // then the allocation behind us may be relatively static, so
  4692. // let's just move with our preallocated space to the new buffer.
  4693. //
  4694. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
  4695. (*StartingVcn >= LlClustersFromBytes(Vcb, Scb->ValidDataToDisk))) {
  4696. //
  4697. // Calculate the index at about 7/8 the way. Hopefully this will
  4698. // move over all of the unallocated piece, while still leaving
  4699. // some small amount of expansion space behind for overwrites.
  4700. //
  4701. IndexLow += (IndexHigh - IndexLow) /2;
  4702. IndexLow += (IndexHigh - IndexLow) /2;
  4703. }
  4704. //
  4705. // Lookup the middle run and use the Last Vcn in that run.
  4706. //
  4707. if (!NtfsGetNextNtfsMcbEntry( Mcb,
  4708. &RangeLow,
  4709. IndexLow,
  4710. &LastVcn,
  4711. &TempLcn,
  4712. &TempCount )) {
  4713. ASSERT( FALSE );
  4714. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  4715. }
  4716. LastVcn = (LastVcn + TempCount) - 1;
  4717. //
  4718. // Calculate how much space is now needed given our new LastVcn.
  4719. //
  4720. MappingPairsSize = QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4721. SizeAvailable,
  4722. Attribute->Form.Nonresident.LowestVcn,
  4723. &LastVcn,
  4724. &LastVcn ));
  4725. }
  4726. }
  4727. }
  4728. //
  4729. // If we are growing this range, then we need to make sure we fix
  4730. // its definition.
  4731. //
  4732. if ((LastVcn - 1) != Attribute->Form.Nonresident.HighestVcn) {
  4733. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  4734. Attribute->Form.Nonresident.LowestVcn,
  4735. LastVcn - 1,
  4736. FALSE );
  4737. }
  4738. //
  4739. // Point to our local mapping pairs buffer, or allocate one if it is not
  4740. // big enough.
  4741. //
  4742. MappingPairs = MappingPairsBuffer;
  4743. if (MappingPairsSize > 64) {
  4744. MappingPairs = NtfsAllocatePool( NonPagedPool, MappingPairsSize + 8 );
  4745. }
  4746. //
  4747. // Use try-finally to insure we free any pool on the way out.
  4748. //
  4749. try {
  4750. DebugDoit(
  4751. VCN TempVcn;
  4752. TempVcn = LastVcn - 1;
  4753. ASSERT(MappingPairsSize >=
  4754. QuadAlign( NtfsGetSizeForMappingPairs( Mcb, SizeAvailable,
  4755. Attribute->Form.Nonresident.LowestVcn,
  4756. &TempVcn, &LastVcn )));
  4757. );
  4758. //
  4759. // Now add the space in the file record.
  4760. //
  4761. *MappingPairs = 0;
  4762. SingleHole = NtfsBuildMappingPairs( Mcb,
  4763. Attribute->Form.Nonresident.LowestVcn,
  4764. &LastVcn,
  4765. MappingPairs );
  4766. //
  4767. // Now find the first different byte. (Most of the time the
  4768. // cost to do this is probably more than paid for by less
  4769. // logging.)
  4770. //
  4771. AttributeOffset = Attribute->Form.Nonresident.MappingPairsOffset;
  4772. MappingPairsOffset = (ULONG)
  4773. RtlCompareMemory( MappingPairs,
  4774. Add2Ptr(Attribute, AttributeOffset),
  4775. ((Attribute->RecordLength - AttributeOffset) > MappingPairsSize ?
  4776. MappingPairsSize :
  4777. (Attribute->RecordLength - AttributeOffset)));
  4778. AttributeOffset += MappingPairsOffset;
  4779. //
  4780. // Log the change.
  4781. //
  4782. {
  4783. LONGLONG LogOffset;
  4784. if (NewBcb != NULL) {
  4785. //
  4786. // We know the file record number of the new file
  4787. // record. Convert it to a file offset.
  4788. //
  4789. LogOffset = LlBytesFromFileRecords( Vcb, MftReferenceNumber );
  4790. } else {
  4791. LogOffset = NtfsMftOffset( Context );
  4792. }
  4793. FileRecord->Lsn =
  4794. NtfsWriteLog( IrpContext,
  4795. Vcb->MftScb,
  4796. NewBcb != NULL ? NewBcb : NtfsFoundBcb(Context),
  4797. UpdateMappingPairs,
  4798. Add2Ptr(MappingPairs, MappingPairsOffset),
  4799. MappingPairsSize - MappingPairsOffset,
  4800. UpdateMappingPairs,
  4801. Add2Ptr(Attribute, AttributeOffset),
  4802. Attribute->RecordLength - AttributeOffset,
  4803. LogOffset,
  4804. RecordOffset,
  4805. AttributeOffset,
  4806. Vcb->BytesPerFileRecordSegment );
  4807. }
  4808. //
  4809. // Now do the mapping pairs update by calling the same
  4810. // routine called at restart.
  4811. //
  4812. NtfsRestartChangeMapping( IrpContext,
  4813. Vcb,
  4814. FileRecord,
  4815. RecordOffset,
  4816. AttributeOffset,
  4817. Add2Ptr(MappingPairs, MappingPairsOffset),
  4818. MappingPairsSize - MappingPairsOffset );
  4819. } finally {
  4820. if (MappingPairs != MappingPairsBuffer) {
  4821. NtfsFreePool(MappingPairs);
  4822. }
  4823. }
  4824. }
  4825. ASSERT( Attribute->Form.Nonresident.HighestVcn == LastVcn );
  4826. //
  4827. // Check if have spilled into the reserved area of an Mft file record.
  4828. //
  4829. if ((Scb == Vcb->MftScb) &&
  4830. (Context->AttributeList.Bcb != NULL)) {
  4831. if (FileRecord->BytesAvailable - FileRecord->FirstFreeByte < Vcb->MftReserved
  4832. && (*(PLONGLONG)&FileRecord->BaseFileRecordSegment != 0)) {
  4833. NtfsAcquireCheckpoint( IrpContext, Vcb );
  4834. SetFlag( Vcb->MftDefragState,
  4835. VCB_MFT_DEFRAG_EXCESS_MAP | VCB_MFT_DEFRAG_ENABLED );
  4836. NtfsReleaseCheckpoint( IrpContext, Vcb );
  4837. }
  4838. }
  4839. //
  4840. // It is possible that we have a file record which contains nothing but a
  4841. // hole, if that is the case see if we can merge this with either the
  4842. // preceding or following attribute (merge the holes). We will then
  4843. // need to rewrite the mapping for the merged record.
  4844. //
  4845. if (SingleHole &&
  4846. ARGUMENT_PRESENT( StartingVcn ) &&
  4847. (Context->AttributeList.Bcb != NULL) &&
  4848. (Scb != Vcb->MftScb) &&
  4849. ((Attribute->Form.Nonresident.LowestVcn != 0) ||
  4850. (LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart ) !=
  4851. (Attribute->Form.Nonresident.HighestVcn + 1)))) {
  4852. //
  4853. // Call our worker routine to perform the actual work if necessary.
  4854. //
  4855. NtfsMergeFileRecords( IrpContext,
  4856. Scb,
  4857. (BOOLEAN) (LastVcn < NewHighestVcn),
  4858. Context );
  4859. }
  4860. //
  4861. // Step 6.
  4862. //
  4863. // Now loop to create new file records if we have more allocation to
  4864. // describe. We use the highest Vcn of the file record we began with
  4865. // as our stopping point or the last Vcn we are adding.
  4866. //
  4867. // NOTE - The record merge code above uses the same test to see if there is more
  4868. // work to do. If this test changes then the body of the IF statement above also
  4869. // needs to be updated.
  4870. //
  4871. while (LastVcn < NewHighestVcn) {
  4872. MFT_SEGMENT_REFERENCE Reference;
  4873. LONGLONG FileRecordNumber;
  4874. PATTRIBUTE_TYPE_CODE NewEnd;
  4875. //
  4876. // If we get here as the result of a hot fix in the Mft, bail
  4877. // out. We could cause a disconnect in the Mft.
  4878. //
  4879. if (IsHotFixScb && (Scb == Vcb->MftScb)) {
  4880. ExRaiseStatus( STATUS_INTERNAL_ERROR );
  4881. }
  4882. //
  4883. // If we have a large sparse range then we may find that the limit
  4884. // in the base file record is range of clusters in the
  4885. // attribute not the number of runs. In that case the base
  4886. // file record may not have been moved to its own file record
  4887. // and there is no attribute list. We need to create the attribute
  4888. // list before cloning the file record.
  4889. //
  4890. if (Context->AttributeList.Bcb == NULL) {
  4891. NtfsCleanupAttributeContext( IrpContext, Context );
  4892. NtfsInitializeAttributeContext( Context );
  4893. //
  4894. // We don't use the second file reference in this case so
  4895. // it is safe to pass the value in the Fcb.
  4896. //
  4897. CreateAttributeList( IrpContext,
  4898. Fcb,
  4899. FileRecord,
  4900. NULL,
  4901. Fcb->FileReference,
  4902. NULL,
  4903. GetSizeForAttributeList( FileRecord ),
  4904. Context );
  4905. //
  4906. // Now look up the previous attribute again.
  4907. //
  4908. NtfsCleanupAttributeContext( IrpContext, Context );
  4909. NtfsInitializeAttributeContext( Context );
  4910. NtfsLookupAttributeForScb( IrpContext, Scb, &LastVcn, Context );
  4911. }
  4912. //
  4913. // Clone our current file record, and point to our new attribute.
  4914. //
  4915. NtfsUnpinBcb( IrpContext, &NewBcb );
  4916. FileRecord = NtfsCloneFileRecord( IrpContext,
  4917. Fcb,
  4918. (BOOLEAN)(Scb == Vcb->MftScb),
  4919. &NewBcb,
  4920. &Reference );
  4921. Attribute = Add2Ptr( FileRecord, FileRecord->FirstAttributeOffset );
  4922. //
  4923. // Next LowestVcn is the LastVcn + 1
  4924. //
  4925. LastVcn = LastVcn + 1;
  4926. Attribute->Form.Nonresident.LowestVcn = LastVcn;
  4927. //
  4928. // Consistency check for MFT defragging. An mft segment can never
  4929. // describe itself or any piece of the mft before it
  4930. //
  4931. if (Scb == Vcb->MftScb) {
  4932. VCN NewFileVcn;
  4933. if (Vcb->FileRecordsPerCluster == 0) {
  4934. //
  4935. // For small cluster systems the file record will take 2 clusters
  4936. // use the 2nd cluster in our check for self describing segments
  4937. //
  4938. NewFileVcn = (NtfsFullSegmentNumber( &Reference ) << Vcb->MftToClusterShift) + (Vcb->ClustersPerFileRecordSegment - 1);
  4939. } else {
  4940. NewFileVcn = NtfsFullSegmentNumber( &Reference ) >> Vcb->MftToClusterShift;
  4941. }
  4942. if (LastVcn <= NewFileVcn) {
  4943. #ifdef BENL_DBG
  4944. KdPrint(( "NTFS: selfdescribing mft segment vcn: 0x%I64x, Ref: 0x%I64x\n", LastVcn, NtfsFullSegmentNumber( &Reference ) ));
  4945. #endif
  4946. NtfsRaiseStatus( IrpContext, STATUS_MFT_TOO_FRAGMENTED, NULL, NULL );
  4947. }
  4948. }
  4949. //
  4950. // Calculate the size of the attribute record we will need.
  4951. //
  4952. NewSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER
  4953. + QuadAlign( Scb->AttributeName.Length )
  4954. + QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  4955. SizeAvailable,
  4956. LastVcn,
  4957. &NewHighestVcn,
  4958. &LastVcn ));
  4959. //
  4960. // Define the new range.
  4961. //
  4962. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  4963. Attribute->Form.Nonresident.LowestVcn,
  4964. LastVcn - 1,
  4965. FALSE );
  4966. //
  4967. // Initialize the new attribute from the old one.
  4968. //
  4969. Attribute->TypeCode = Scb->AttributeTypeCode;
  4970. Attribute->RecordLength = NewSize;
  4971. Attribute->FormCode = NONRESIDENT_FORM;
  4972. //
  4973. // Assume no attribute name, and calculate where the Mapping Pairs
  4974. // will go. (Update below if we are wrong.)
  4975. //
  4976. MappingPairs = (PCHAR)Attribute + SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  4977. //
  4978. // If the attribute has a name, take care of that now.
  4979. //
  4980. if (Scb->AttributeName.Length != 0) {
  4981. Attribute->NameLength = (UCHAR)(Scb->AttributeName.Length / sizeof(WCHAR));
  4982. Attribute->NameOffset = (USHORT)PtrOffset(Attribute, MappingPairs);
  4983. RtlCopyMemory( MappingPairs,
  4984. Scb->AttributeName.Buffer,
  4985. Scb->AttributeName.Length );
  4986. MappingPairs += QuadAlign( Scb->AttributeName.Length );
  4987. }
  4988. Attribute->Flags = Scb->AttributeFlags;
  4989. Attribute->Instance = FileRecord->NextAttributeInstance++;
  4990. //
  4991. // We always need the mapping pairs offset.
  4992. //
  4993. Attribute->Form.Nonresident.MappingPairsOffset = (USHORT)(MappingPairs -
  4994. (PCHAR)Attribute);
  4995. NewEnd = Add2Ptr( Attribute, Attribute->RecordLength );
  4996. *NewEnd = $END;
  4997. FileRecord->FirstFreeByte = PtrOffset( FileRecord, NewEnd )
  4998. + QuadAlign( sizeof(ATTRIBUTE_TYPE_CODE ));
  4999. //
  5000. // Now add the space in the file record.
  5001. //
  5002. *MappingPairs = 0;
  5003. NtfsBuildMappingPairs( Mcb,
  5004. Attribute->Form.Nonresident.LowestVcn,
  5005. &LastVcn,
  5006. MappingPairs );
  5007. Attribute->Form.Nonresident.HighestVcn = LastVcn;
  5008. //
  5009. // Now log these changes and fix up the first file record.
  5010. //
  5011. FileRecordNumber = NtfsFullSegmentNumber(&Reference);
  5012. //
  5013. // Now log these changes and fix up the first file record.
  5014. //
  5015. FileRecord->Lsn =
  5016. NtfsWriteLog( IrpContext,
  5017. Vcb->MftScb,
  5018. NewBcb,
  5019. InitializeFileRecordSegment,
  5020. FileRecord,
  5021. FileRecord->FirstFreeByte,
  5022. Noop,
  5023. NULL,
  5024. 0,
  5025. LlBytesFromFileRecords( Vcb, FileRecordNumber ),
  5026. 0,
  5027. 0,
  5028. Vcb->BytesPerFileRecordSegment );
  5029. //
  5030. // Finally, we have to add the entry to the attribute list.
  5031. // The routine we have to do this gets most of its inputs
  5032. // out of an attribute context. Our context at this point
  5033. // does not have quite the right information, so we have to
  5034. // update it here before calling AddToAttributeList.
  5035. //
  5036. Context->FoundAttribute.FileRecord = FileRecord;
  5037. Context->FoundAttribute.Attribute = Attribute;
  5038. Context->AttributeList.Entry =
  5039. NtfsGetNextRecord(Context->AttributeList.Entry);
  5040. NtfsAddToAttributeList( IrpContext, Fcb, Reference, Context );
  5041. }
  5042. } finally {
  5043. NtfsUnpinBcb( IrpContext, &NewBcb );
  5044. }
  5045. if (!ARGUMENT_PRESENT( StartingVcn) ||
  5046. (LowestVcnRemapped <= *StartingVcn)) {
  5047. break;
  5048. }
  5049. //
  5050. // Move the range to be remapped down.
  5051. //
  5052. LocalClusterCount = LowestVcnRemapped - *StartingVcn;
  5053. NtfsCleanupAttributeContext( IrpContext, Context );
  5054. NtfsInitializeAttributeContext( Context );
  5055. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, Context );
  5056. }
  5057. DebugTrace( -1, Dbg, ("NtfsAddAttributeAllocation -> VOID\n") );
  5058. }
  5059. //
  5060. // This routine is intended for use by allocsup.c. Other callers should use
  5061. // the routines in allocsup.
  5062. //
  5063. VOID
  5064. NtfsDeleteAttributeAllocation (
  5065. IN PIRP_CONTEXT IrpContext,
  5066. IN PSCB Scb,
  5067. IN BOOLEAN LogIt,
  5068. IN PVCN StopOnVcn,
  5069. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context,
  5070. IN BOOLEAN TruncateToVcn
  5071. )
  5072. /*++
  5073. Routine Description:
  5074. This routine deletes an existing nonresident attribute, removing the
  5075. deleted clusters only from the allocation description in the file
  5076. record.
  5077. The caller specifies the attribute to be changed via the attribute context,
  5078. and must be prepared to clean up this context no matter how this routine
  5079. returns. The Scb must already have deleted the clusters in question.
  5080. Arguments:
  5081. Scb - Current attribute, with the clusters in question already deleted from
  5082. the Mcb.
  5083. LogIt - Most callers should specify TRUE, to have the change logged. However,
  5084. we can specify FALSE if we are deleting an entire file record, and
  5085. will be logging that.
  5086. StopOnVcn - Vcn to stop on for regerating mapping
  5087. Context - Attribute Context positioned at the attribute to change.
  5088. TruncateToVcn - Truncate file sizes as appropriate to the Vcn
  5089. Return Value:
  5090. None.
  5091. --*/
  5092. {
  5093. ULONG AttributeOffset;
  5094. ULONG MappingPairsOffset, MappingPairsSize;
  5095. CHAR MappingPairsBuffer[64];
  5096. ULONG RecordOffset;
  5097. PATTRIBUTE_RECORD_HEADER Attribute;
  5098. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  5099. PCHAR MappingPairs;
  5100. VCN LastVcn;
  5101. ULONG NewSize;
  5102. PVCB Vcb;
  5103. ASSERT_IRP_CONTEXT( IrpContext );
  5104. ASSERT_SCB( Scb );
  5105. PAGED_CODE();
  5106. Vcb = Scb->Vcb;
  5107. //
  5108. // For now we only support truncation.
  5109. //
  5110. DebugTrace( +1, Dbg, ("NtfsDeleteAttributeAllocation\n") );
  5111. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  5112. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  5113. //
  5114. // Make sure the buffer is pinned.
  5115. //
  5116. NtfsPinMappedAttribute( IrpContext, Vcb, Context );
  5117. Attribute = NtfsFoundAttribute(Context);
  5118. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  5119. //
  5120. // Get the file record pointer.
  5121. //
  5122. FileRecord = NtfsContainingFileRecord(Context);
  5123. RecordOffset = PtrOffset(FileRecord, Attribute);
  5124. //
  5125. // Calculate how much space is actually needed.
  5126. //
  5127. MappingPairsSize = QuadAlign(NtfsGetSizeForMappingPairs( &Scb->Mcb,
  5128. MAXULONG,
  5129. Attribute->Form.Nonresident.LowestVcn,
  5130. StopOnVcn,
  5131. &LastVcn ));
  5132. //
  5133. // Don't assume we understand everything about the size of the current header.
  5134. // Find the offset of the name or the mapping pairs to use as the size
  5135. // of the header.
  5136. //
  5137. NewSize = Attribute->Form.Nonresident.MappingPairsOffset;
  5138. if (Attribute->NameLength != 0) {
  5139. NewSize = Attribute->NameOffset + QuadAlign( Attribute->NameLength << 1 );
  5140. }
  5141. NewSize += MappingPairsSize;
  5142. //
  5143. // If the record could somehow grow by deleting allocation, then
  5144. // NtfsChangeAttributeSize could fail and we would have to copy the
  5145. // loop from NtfsAddAttributeAllocation.
  5146. //
  5147. ASSERT( NewSize <= Attribute->RecordLength );
  5148. MappingPairs = MappingPairsBuffer;
  5149. if (MappingPairsSize > 64) {
  5150. MappingPairs = NtfsAllocatePool( NonPagedPool, MappingPairsSize + 8 );
  5151. }
  5152. //
  5153. // Use try-finally to insure we free any pool on the way out.
  5154. //
  5155. try {
  5156. //
  5157. // Now build up the mapping pairs in the buffer.
  5158. //
  5159. *MappingPairs = 0;
  5160. NtfsBuildMappingPairs( &Scb->Mcb,
  5161. Attribute->Form.Nonresident.LowestVcn,
  5162. &LastVcn,
  5163. MappingPairs );
  5164. //
  5165. // Now find the first different byte. (Most of the time the
  5166. // cost to do this is probably more than paid for by less
  5167. // logging.)
  5168. //
  5169. AttributeOffset = Attribute->Form.Nonresident.MappingPairsOffset;
  5170. MappingPairsOffset = (ULONG)
  5171. RtlCompareMemory( MappingPairs,
  5172. Add2Ptr(Attribute, AttributeOffset),
  5173. MappingPairsSize );
  5174. AttributeOffset += MappingPairsOffset;
  5175. //
  5176. // Log the change.
  5177. //
  5178. if (LogIt) {
  5179. FileRecord->Lsn =
  5180. NtfsWriteLog( IrpContext,
  5181. Vcb->MftScb,
  5182. NtfsFoundBcb(Context),
  5183. UpdateMappingPairs,
  5184. Add2Ptr(MappingPairs, MappingPairsOffset),
  5185. MappingPairsSize - MappingPairsOffset,
  5186. UpdateMappingPairs,
  5187. Add2Ptr(Attribute, AttributeOffset),
  5188. Attribute->RecordLength - AttributeOffset,
  5189. NtfsMftOffset( Context ),
  5190. RecordOffset,
  5191. AttributeOffset,
  5192. Vcb->BytesPerFileRecordSegment );
  5193. }
  5194. //
  5195. // Now do the mapping pairs update by calling the same
  5196. // routine called at restart.
  5197. //
  5198. NtfsRestartChangeMapping( IrpContext,
  5199. Vcb,
  5200. FileRecord,
  5201. RecordOffset,
  5202. AttributeOffset,
  5203. Add2Ptr(MappingPairs, MappingPairsOffset),
  5204. MappingPairsSize - MappingPairsOffset );
  5205. //
  5206. // If we were asked to stop on a Vcn, then the caller does not wish
  5207. // us to modify the Scb. (Currently this is only done one time when
  5208. // the Mft Data attribute no longer fits in the first file record.)
  5209. //
  5210. if (TruncateToVcn) {
  5211. LONGLONG Size;
  5212. //
  5213. // We add one cluster to calculate the allocation size.
  5214. //
  5215. LastVcn = LastVcn + 1;
  5216. Size = LlBytesFromClusters( Vcb, LastVcn );
  5217. Scb->Header.AllocationSize.QuadPart = Size;
  5218. if (Scb->Header.ValidDataLength.QuadPart > Size) {
  5219. Scb->Header.ValidDataLength.QuadPart = Size;
  5220. }
  5221. if (Scb->Header.FileSize.QuadPart > Size) {
  5222. Scb->Header.FileSize.QuadPart = Size;
  5223. }
  5224. //
  5225. // Possibly update ValidDataToDisk which is only nonzero for compressed file
  5226. //
  5227. if (Size < Scb->ValidDataToDisk) {
  5228. Scb->ValidDataToDisk = Size;
  5229. }
  5230. }
  5231. } finally {
  5232. if (MappingPairs != MappingPairsBuffer) {
  5233. NtfsFreePool(MappingPairs);
  5234. }
  5235. }
  5236. DebugTrace( -1, Dbg, ("NtfsDeleteAttributeAllocation -> VOID\n") );
  5237. }
  5238. BOOLEAN
  5239. NtfsIsFileDeleteable (
  5240. IN PIRP_CONTEXT IrpContext,
  5241. IN PFCB Fcb,
  5242. OUT PBOOLEAN NonEmptyIndex
  5243. )
  5244. /*++
  5245. Routine Description:
  5246. This look checks if a file may be deleted by examing all of the index
  5247. attributes to check that they have no children.
  5248. Note that once a file is marked for delete, we must insure
  5249. that none of the conditions checked by this routine are allowed to
  5250. change. For example, once the file is marked for delete, no links
  5251. may be added, and no files may be created in any indices of this
  5252. file.
  5253. Arguments:
  5254. Fcb - Fcb for the file.
  5255. NonEmptyIndex - Address to store TRUE if the file is not deleteable because
  5256. it contains an non-empty indexed attribute.
  5257. Return Value:
  5258. FALSE - If it is not ok to delete the specified file.
  5259. TRUE - If it is ok to delete the specified file.
  5260. --*/
  5261. {
  5262. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  5263. PATTRIBUTE_RECORD_HEADER Attribute;
  5264. BOOLEAN MoreToGo;
  5265. PAGED_CODE();
  5266. NtfsInitializeAttributeContext( &Context );
  5267. try {
  5268. //
  5269. // Enumerate all of the attributes to check whether they may be deleted.
  5270. //
  5271. MoreToGo = NtfsLookupAttributeByCode( IrpContext,
  5272. Fcb,
  5273. &Fcb->FileReference,
  5274. $INDEX_ROOT,
  5275. &Context );
  5276. while (MoreToGo) {
  5277. //
  5278. // Point to the current attribute.
  5279. //
  5280. Attribute = NtfsFoundAttribute( &Context );
  5281. //
  5282. // If the attribute is an index, then it must be empty.
  5283. //
  5284. if (!NtfsIsIndexEmpty( IrpContext, Attribute )) {
  5285. *NonEmptyIndex = TRUE;
  5286. break;
  5287. }
  5288. //
  5289. // Go to the next attribute.
  5290. //
  5291. MoreToGo = NtfsLookupNextAttributeByCode( IrpContext,
  5292. Fcb,
  5293. $INDEX_ROOT,
  5294. &Context );
  5295. }
  5296. } finally {
  5297. DebugUnwind( NtfsIsFileDeleteable );
  5298. NtfsCleanupAttributeContext( IrpContext, &Context );
  5299. }
  5300. //
  5301. // The File is deleteable if scanned the entire file record
  5302. // and found no reasons we could not delete the file.
  5303. //
  5304. return (BOOLEAN)(!MoreToGo);
  5305. }
  5306. VOID
  5307. NtfsDeleteFile (
  5308. IN PIRP_CONTEXT IrpContext,
  5309. IN PFCB Fcb,
  5310. IN PSCB ParentScb,
  5311. IN OUT PBOOLEAN AcquiredParentScb,
  5312. IN OUT PNAME_PAIR NamePair OPTIONAL,
  5313. IN OUT PNTFS_TUNNELED_DATA TunneledData OPTIONAL
  5314. )
  5315. /*++
  5316. Routine Description:
  5317. This routine may be called to see if it is the specified file may
  5318. be deleted from the specified parent (i.e., if the specified parent
  5319. were to be acquired exclusive). This routine should be called from
  5320. fileinfo, to see whether it is ok to mark an open file for delete.
  5321. NamePair will capture the names of the file being deleted if supplied.
  5322. Note that once a file is marked for delete, none of we must insure
  5323. that none of the conditions checked by this routine are allowed to
  5324. change. For example, once the file is marked for delete, no links
  5325. may be added, and no files may be created in any indices of this
  5326. file.
  5327. This routine does NOT do the following other delete related actions
  5328. 1) remove the fcb from the fcbtable
  5329. 2) tunneling
  5330. 3) Link count adjustments in the fcb
  5331. 4) Directory notification
  5332. NOTE: The caller must have the Fcb and ParentScb exclusive to call
  5333. this routine,
  5334. Arguments:
  5335. Fcb - Fcb for the file.
  5336. ParentScb - Parent Scb via which the file was opened, and which would
  5337. be acquired exclusive to perform the delete.
  5338. AcquiredParentScb - On input indicates whether the ParentScb has
  5339. already been acquired. Set to TRUE here if this routine
  5340. acquires the parent.
  5341. TunneledData - Optionally provided to capture the name pair and
  5342. object id of a file so they can be tunneled.
  5343. Return Value:
  5344. None
  5345. --*/
  5346. {
  5347. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  5348. LONGLONG Delta;
  5349. PATTRIBUTE_RECORD_HEADER Attribute;
  5350. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  5351. PVCB Vcb;
  5352. PLIST_ENTRY Links;
  5353. ULONG RecordNumber;
  5354. NUKEM LocalNuke;
  5355. ULONG Pass;
  5356. ULONG i;
  5357. PNUKEM TempNukem;
  5358. PNUKEM Nukem = &LocalNuke;
  5359. ULONG NukemIndex = 0;
  5360. UCHAR *ObjectId;
  5361. MAP_HANDLE MapHandle;
  5362. ULONG ForceCheckpointCount;
  5363. ULONG IncomingFileAttributes = 0; // invalid value
  5364. ULONG IncomingReparsePointTag = IO_REPARSE_TAG_RESERVED_ZERO; // invalid value
  5365. BOOLEAN MoreToGo;
  5366. BOOLEAN NonresidentAttributeList = FALSE;
  5367. BOOLEAN InitializedMapHandle = FALSE;
  5368. BOOLEAN ReparsePointIsPresent = FALSE;
  5369. BOOLEAN ObjectIdIsPresent = FALSE;
  5370. BOOLEAN LogIt;
  5371. BOOLEAN AcquiredReparseIndex = FALSE;
  5372. BOOLEAN AcquiredObjectIdIndex = FALSE;
  5373. PAGED_CODE();
  5374. ASSERT_EXCLUSIVE_FCB( Fcb );
  5375. RtlZeroMemory( &LocalNuke, sizeof(NUKEM) );
  5376. Vcb = Fcb->Vcb;
  5377. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_QUOTA_DISABLE );
  5378. //
  5379. // Remember the values of the file attribute flags and of the reparse tag
  5380. // for abnormal termination recovery.
  5381. //
  5382. IncomingFileAttributes = Fcb->Info.FileAttributes;
  5383. IncomingReparsePointTag = Fcb->Info.ReparsePointTag;
  5384. try {
  5385. //
  5386. // We perform the delete in multiple passes. We need to break this up carefully so that
  5387. // if the delete aborts for any reason that the file is left in a consistent state. The
  5388. // operations are broken up into the following stages.
  5389. //
  5390. //
  5391. // First stage - free any allocation possible
  5392. //
  5393. // - Truncate all user streams to length zero
  5394. //
  5395. // Middle stage - this is required for files with a large number of attributes. Otherwise
  5396. // we can't delete the file records and stay within the log file. Skip this pass
  5397. // for smaller files.
  5398. //
  5399. // - Remove data attributes except unnamed
  5400. //
  5401. // Final stage - no checkpoints allowed until the end of this stage.
  5402. //
  5403. // - Acquire the quota resource if needed.
  5404. // - Remove filenames from Index (for any filename attributes still present)
  5405. // - Remove entry from ObjectId Index
  5406. // - Delete allocation for reparse point and 4.0 style security descriptor
  5407. // - Remove entry from Reparse Index
  5408. // - Delete AttributeList
  5409. // - Log deallocate of file records
  5410. //
  5411. for (Pass = 1; Pass <= 3; Pass += 1) {
  5412. ForceCheckpointCount = 0;
  5413. NtfsInitializeAttributeContext( &Context );
  5414. //
  5415. // Enumerate all of the attributes to check whether they may be deleted.
  5416. //
  5417. MoreToGo = NtfsLookupAttribute( IrpContext,
  5418. Fcb,
  5419. &Fcb->FileReference,
  5420. &Context );
  5421. //
  5422. // Log the change to the mapping pairs if there is an attribute list.
  5423. //
  5424. LogIt = FALSE;
  5425. if (Context.AttributeList.Bcb != NULL) {
  5426. LogIt = TRUE;
  5427. }
  5428. //
  5429. // Remember if we want to log the changes to non-resident attributes.
  5430. //
  5431. while (MoreToGo) {
  5432. //
  5433. // Point to the current attribute.
  5434. //
  5435. Attribute = NtfsFoundAttribute( &Context );
  5436. //
  5437. // All indices must be empty.
  5438. //
  5439. ASSERT( (Attribute->TypeCode != $INDEX_ROOT) ||
  5440. NtfsIsIndexEmpty( IrpContext, Attribute ));
  5441. //
  5442. // Remember when the $REPARSE_POINT attribute is present.
  5443. // When it is non-resident we delete it in pass 3.
  5444. // The entry in the $Reparse index always gets deleted in pass 3.
  5445. // We have to delete the index entry before deleting the allocation.
  5446. //
  5447. if (Attribute->TypeCode == $REPARSE_POINT) {
  5448. ReparsePointIsPresent = TRUE;
  5449. if (Pass == 3) {
  5450. //
  5451. // If this is the $REPARSE_POINT attribute, delete now the appropriate
  5452. // entry from the $Reparse index.
  5453. //
  5454. NTSTATUS Status = STATUS_SUCCESS;
  5455. INDEX_KEY IndexKey;
  5456. INDEX_ROW IndexRow;
  5457. REPARSE_INDEX_KEY KeyValue;
  5458. PREPARSE_DATA_BUFFER ReparseBuffer = NULL;
  5459. PVOID AttributeData = NULL;
  5460. PBCB Bcb = NULL;
  5461. ULONG Length = 0;
  5462. //
  5463. // Point to the attribute data.
  5464. //
  5465. if (NtfsIsAttributeResident( Attribute )) {
  5466. //
  5467. // Point to the value of the arribute.
  5468. //
  5469. AttributeData = NtfsAttributeValue( Attribute );
  5470. DebugTrace( 0, Dbg, ("Existing attribute is resident.\n") );
  5471. } else {
  5472. //
  5473. // Map the attribute list if the attribute is non-resident.
  5474. // Otherwise the attribute is already mapped and we have a Bcb
  5475. // in the attribute context.
  5476. //
  5477. DebugTrace( 0, Dbg, ("Existing attribute is non-resident.\n") );
  5478. if (Attribute->Form.Nonresident.FileSize > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) {
  5479. NtfsRaiseStatus( IrpContext,STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  5480. }
  5481. NtfsMapAttributeValue( IrpContext,
  5482. Fcb,
  5483. &AttributeData, // point to the value
  5484. &Length,
  5485. &Bcb,
  5486. &Context );
  5487. //
  5488. // Unpin the Bcb. The unpin routine checks for NULL.
  5489. //
  5490. NtfsUnpinBcb( IrpContext, &Bcb );
  5491. }
  5492. //
  5493. // Set the pointer to extract the reparse point tag.
  5494. //
  5495. ReparseBuffer = (PREPARSE_DATA_BUFFER)AttributeData;
  5496. //
  5497. // Verify that this file is in the reparse point index and delete it.
  5498. //
  5499. KeyValue.FileReparseTag = ReparseBuffer->ReparseTag;
  5500. KeyValue.FileId = *(PLARGE_INTEGER)&Fcb->FileReference;
  5501. IndexKey.Key = (PVOID)&KeyValue;
  5502. IndexKey.KeyLength = sizeof(KeyValue);
  5503. NtOfsInitializeMapHandle( &MapHandle );
  5504. InitializedMapHandle = TRUE;
  5505. //
  5506. // All of the resources should have been acquired.
  5507. //
  5508. ASSERT( *AcquiredParentScb );
  5509. ASSERT( AcquiredReparseIndex );
  5510. //
  5511. // NtOfsFindRecord will return an error status if the key is not found.
  5512. //
  5513. Status = NtOfsFindRecord( IrpContext,
  5514. Vcb->ReparsePointTableScb,
  5515. &IndexKey,
  5516. &IndexRow,
  5517. &MapHandle,
  5518. NULL );
  5519. if (!NT_SUCCESS(Status)) {
  5520. //
  5521. // Should not happen. The reparse point should be in the index.
  5522. //
  5523. DebugTrace( 0, Dbg, ("Record not found in the reparse point index.\n") );
  5524. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  5525. }
  5526. //
  5527. // Remove the entry from the reparse point index.
  5528. //
  5529. NtOfsDeleteRecords( IrpContext,
  5530. Vcb->ReparsePointTableScb,
  5531. 1, // deleting one record from the index
  5532. &IndexKey );
  5533. }
  5534. }
  5535. //
  5536. // If the attribute is nonresident, then delete its allocation.
  5537. // We only need to make the NtfsDeleteAllocation from record for
  5538. // the attribute with lowest Vcn of zero. This will deallocate
  5539. // all of the clusters for the file.
  5540. //
  5541. if (Attribute->FormCode == NONRESIDENT_FORM) {
  5542. if ((Attribute->Form.Nonresident.LowestVcn == 0) &&
  5543. (Attribute->Form.Nonresident.AllocatedLength != 0)) {
  5544. if (Pass == 1) {
  5545. //
  5546. // Postpone till pass 3 the deletion of the non-resident attribute in
  5547. // the case of a security descriptor and a reparse point.
  5548. //
  5549. if ((Attribute->TypeCode != $SECURITY_DESCRIPTOR) &&
  5550. (Attribute->TypeCode != $REPARSE_POINT)) {
  5551. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, &Context, TRUE, LogIt );
  5552. //
  5553. // Make sure we count the number of these calls we make. Force a
  5554. // periodic checkpoint on a file with a lot of streams. We might have
  5555. // thousands of streams whose allocations won't force a checkpoint. we
  5556. // could spin indefinitely trying to delete this file. Lets force
  5557. // a checkpoint on a regular basis.
  5558. //
  5559. ForceCheckpointCount += 1;
  5560. if (ForceCheckpointCount > 10) {
  5561. NtfsCheckpointCurrentTransaction( IrpContext );
  5562. ForceCheckpointCount = 0;
  5563. }
  5564. //
  5565. // Reload the attribute pointer in the event it
  5566. // was remapped.
  5567. //
  5568. Attribute = NtfsFoundAttribute( &Context );
  5569. }
  5570. } else if (Pass == 3) {
  5571. //
  5572. // Now, in pass 3, delete the security descriptor and the reparse
  5573. // point attributes when they are non-residents.
  5574. //
  5575. if ((Attribute->TypeCode == $SECURITY_DESCRIPTOR) ||
  5576. (Attribute->TypeCode == $REPARSE_POINT)) {
  5577. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, &Context, FALSE, LogIt );
  5578. //
  5579. // Reload the attribute pointer in the event it
  5580. // was remapped.
  5581. //
  5582. Attribute = NtfsFoundAttribute( &Context );
  5583. }
  5584. }
  5585. }
  5586. } else {
  5587. //
  5588. // If we are at the start of Pass 3 then make sure we have the parent
  5589. // acquired and can perform any necessary quota operations.
  5590. //
  5591. if ((Attribute->TypeCode == $STANDARD_INFORMATION) &&
  5592. (Pass == 3)) {
  5593. if (!*AcquiredParentScb ||
  5594. NtfsPerformQuotaOperation( Fcb ) ||
  5595. ReparsePointIsPresent ||
  5596. ObjectIdIsPresent) {
  5597. //
  5598. // See if we need to acquire any resources, and if so, get
  5599. // them in the right order. We need to do this carefully.
  5600. // If the Mft is acquired by this thread then checkpoint
  5601. // the transaction and release the Mft before we go any
  5602. // further.
  5603. //
  5604. if (Vcb->MftScb->Fcb->ExclusiveFcbLinks.Flink != NULL &&
  5605. NtfsIsExclusiveScb( Vcb->MftScb )) {
  5606. NtfsCheckpointCurrentTransaction( IrpContext );
  5607. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  5608. }
  5609. ASSERT(!NtfsIsExclusiveScb( Vcb->MftScb ));
  5610. //
  5611. // Now acquire the parent if not already acquired.
  5612. //
  5613. if (!*AcquiredParentScb) {
  5614. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  5615. *AcquiredParentScb = TRUE;
  5616. }
  5617. if (ObjectIdIsPresent) {
  5618. NtfsAcquireExclusiveScb( IrpContext, Vcb->ObjectIdTableScb );
  5619. AcquiredObjectIdIndex = TRUE;
  5620. }
  5621. //
  5622. // Also acquire reparse & object id if necessary in
  5623. // the correct bottom-up Vcb order.
  5624. //
  5625. if (ReparsePointIsPresent && !AcquiredReparseIndex) {
  5626. NtfsAcquireExclusiveScb( IrpContext, Vcb->ReparsePointTableScb );
  5627. AcquiredReparseIndex = TRUE;
  5628. }
  5629. //
  5630. // We may acquire the quota index in here.
  5631. //
  5632. if (Attribute->Form.Resident.ValueLength == sizeof( STANDARD_INFORMATION )) {
  5633. //
  5634. // Capture all of the user's quota for this file.
  5635. //
  5636. //
  5637. // The quota resource cannot be acquired before the streams
  5638. // are deleted because we can deadlock with the mapped page
  5639. // writer when CcSetFileSizes is called.
  5640. //
  5641. if (NtfsPerformQuotaOperation( Fcb )) {
  5642. ASSERT(!NtfsIsExclusiveScb( Vcb->MftScb ));
  5643. Delta = -(LONGLONG) ((PSTANDARD_INFORMATION)
  5644. NtfsAttributeValue( Attribute ))->QuotaCharged;
  5645. NtfsUpdateFileQuota( IrpContext,
  5646. Fcb,
  5647. &Delta,
  5648. TRUE,
  5649. FALSE );
  5650. }
  5651. }
  5652. }
  5653. }
  5654. }
  5655. //
  5656. // If we are deleting the object id attribute, we need to
  5657. // update the object id index as well.
  5658. //
  5659. if (Attribute->TypeCode == $OBJECT_ID) {
  5660. if (Pass == 1) {
  5661. //
  5662. // On pass 1, it is only necessary to remember we have
  5663. // an object id so we remember to acquire the oid index
  5664. // on pass 3.
  5665. //
  5666. ObjectIdIsPresent = TRUE;
  5667. } else if (Pass == 3) {
  5668. //
  5669. // We'd better be holding the object id index and parent
  5670. // directory already, or else there is a potential for
  5671. // deadlock.
  5672. //
  5673. ASSERT(NtfsIsExclusiveScb( Vcb->ObjectIdTableScb ));
  5674. ASSERT(*AcquiredParentScb);
  5675. if (ARGUMENT_PRESENT(TunneledData)) {
  5676. //
  5677. // We need to lookup the object id so we can tunnel it.
  5678. //
  5679. TunneledData->HasObjectId = TRUE;
  5680. ObjectId = (UCHAR *) NtfsAttributeValue( Attribute );
  5681. RtlCopyMemory( TunneledData->ObjectIdBuffer.ObjectId,
  5682. ObjectId,
  5683. sizeof(TunneledData->ObjectIdBuffer.ObjectId) );
  5684. NtfsGetObjectIdExtendedInfo( IrpContext,
  5685. Fcb->Vcb,
  5686. ObjectId,
  5687. TunneledData->ObjectIdBuffer.ExtendedInfo );
  5688. }
  5689. //
  5690. // We need to delete the object id from the index
  5691. // to keep everything consistent. The FALSE means
  5692. // don't delete the attribute itself, that would
  5693. // lead to some ugly recursion.
  5694. //
  5695. NtfsDeleteObjectIdInternal( IrpContext,
  5696. Fcb,
  5697. Vcb,
  5698. FALSE );
  5699. }
  5700. }
  5701. //
  5702. // If we are in the second pass then remove extra named data streams.
  5703. //
  5704. if (Pass == 2) {
  5705. //
  5706. // The record is large enough to consider. Only do this for named
  5707. // data streams
  5708. //
  5709. if ((Attribute->TypeCode == $DATA) &&
  5710. (Attribute->NameLength != 0)) {
  5711. PSCB DeleteScb;
  5712. UNICODE_STRING AttributeName;
  5713. //
  5714. // Get the Scb so we can mark it as deleted.
  5715. //
  5716. AttributeName.Buffer = Add2Ptr( Attribute, Attribute->NameOffset );
  5717. AttributeName.Length = Attribute->NameLength * sizeof( WCHAR );
  5718. DeleteScb = NtfsCreateScb( IrpContext,
  5719. Fcb,
  5720. Attribute->TypeCode,
  5721. &AttributeName,
  5722. TRUE,
  5723. NULL );
  5724. NtfsDeleteAttributeRecord( IrpContext,
  5725. Fcb,
  5726. (DELETE_LOG_OPERATION |
  5727. DELETE_RELEASE_FILE_RECORD),
  5728. &Context );
  5729. if (DeleteScb != NULL) {
  5730. SetFlag( DeleteScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  5731. DeleteScb->AttributeTypeCode = $UNUSED;
  5732. }
  5733. //
  5734. // Let's checkpoint periodically after each attribute. Since
  5735. // the attribute list is large and we are removing and
  5736. // entry from the beginning of the list the log records
  5737. // can be very large.
  5738. //
  5739. NtfsCheckpointCurrentTransaction( IrpContext );
  5740. }
  5741. } else if (Pass == 3) {
  5742. //
  5743. // If the attribute is a file name, then it must be from our
  5744. // caller's parent directory, or else we cannot delete.
  5745. //
  5746. if (Attribute->TypeCode == $FILE_NAME) {
  5747. PFILE_NAME FileName;
  5748. FileName = (PFILE_NAME)NtfsAttributeValue( Attribute );
  5749. ASSERT( ARGUMENT_PRESENT( ParentScb ));
  5750. ASSERT(NtfsEqualMftRef(&FileName->ParentDirectory,
  5751. &ParentScb->Fcb->FileReference));
  5752. if (ARGUMENT_PRESENT(NamePair)) {
  5753. //
  5754. // Squirrel away names
  5755. //
  5756. NtfsCopyNameToNamePair( NamePair,
  5757. FileName->FileName,
  5758. FileName->FileNameLength,
  5759. FileName->Flags );
  5760. }
  5761. NtfsDeleteIndexEntry( IrpContext,
  5762. ParentScb,
  5763. (PVOID)FileName,
  5764. &Fcb->FileReference );
  5765. }
  5766. //
  5767. // If this file record is not already deleted, then do it now.
  5768. // Note, we are counting on its contents not to change.
  5769. //
  5770. FileRecord = NtfsContainingFileRecord( &Context );
  5771. //
  5772. // See if this is the same as the last one we remembered, else remember it.
  5773. //
  5774. if (Context.AttributeList.Bcb != NULL) {
  5775. RecordNumber = NtfsUnsafeSegmentNumber( &Context.AttributeList.Entry->SegmentReference );
  5776. } else {
  5777. RecordNumber = NtfsUnsafeSegmentNumber( &Fcb->FileReference );
  5778. }
  5779. //
  5780. // Now loop to see if we already remembered this record.
  5781. // This reduces our pool allocation and also prevents us
  5782. // from deleting file records twice.
  5783. //
  5784. TempNukem = Nukem;
  5785. while (TempNukem != NULL) {
  5786. for (i = 0; i < 4; i++) {
  5787. if (TempNukem->RecordNumbers[i] == RecordNumber) {
  5788. RecordNumber = 0;
  5789. break;
  5790. }
  5791. }
  5792. TempNukem = TempNukem->Next;
  5793. }
  5794. if (RecordNumber != 0) {
  5795. //
  5796. // Is the list full? If so allocate and initialize a new one.
  5797. //
  5798. if (NukemIndex > 3) {
  5799. TempNukem = (PNUKEM)ExAllocateFromPagedLookasideList( &NtfsNukemLookasideList );
  5800. RtlZeroMemory( TempNukem, sizeof(NUKEM) );
  5801. TempNukem->Next = Nukem;
  5802. Nukem = TempNukem;
  5803. NukemIndex = 0;
  5804. }
  5805. //
  5806. // Remember to delete this guy. (Note we can possibly list someone
  5807. // more than once, but NtfsDeleteFileRecord handles that.)
  5808. //
  5809. Nukem->RecordNumbers[NukemIndex] = RecordNumber;
  5810. NukemIndex += 1;
  5811. }
  5812. //
  5813. // When we have the first attribute, check for the existance of
  5814. // a non-resident attribute list.
  5815. //
  5816. } else if ((Attribute->TypeCode == $STANDARD_INFORMATION) &&
  5817. (Context.AttributeList.Bcb != NULL) &&
  5818. (!NtfsIsAttributeResident( Context.AttributeList.AttributeList ))) {
  5819. NonresidentAttributeList = TRUE;
  5820. }
  5821. //
  5822. // Go to the next attribute.
  5823. //
  5824. MoreToGo = NtfsLookupNextAttribute( IrpContext,
  5825. Fcb,
  5826. &Context );
  5827. }
  5828. NtfsCleanupAttributeContext( IrpContext, &Context );
  5829. //
  5830. // Skip pass 2 unless there is a large attribute list.
  5831. //
  5832. if (Pass == 1) {
  5833. if (RtlPointerToOffset( Context.AttributeList.FirstEntry,
  5834. Context.AttributeList.BeyondFinalEntry ) > 0x1000) {
  5835. //
  5836. // Go ahead and checkpoint now so we will make progress in Pass 2.
  5837. //
  5838. NtfsCheckpointCurrentTransaction( IrpContext );
  5839. } else {
  5840. //
  5841. // Skip pass 2.
  5842. //
  5843. Pass += 1;
  5844. }
  5845. }
  5846. }
  5847. //
  5848. // Handle the unusual nonresident attribute list case
  5849. //
  5850. if (NonresidentAttributeList) {
  5851. NtfsInitializeAttributeContext( &Context );
  5852. NtfsLookupAttributeByCode( IrpContext,
  5853. Fcb,
  5854. &Fcb->FileReference,
  5855. $ATTRIBUTE_LIST,
  5856. &Context );
  5857. NtfsDeleteAllocationFromRecord( IrpContext, Fcb, &Context, FALSE, FALSE );
  5858. NtfsCleanupAttributeContext( IrpContext, &Context );
  5859. }
  5860. //
  5861. // Post the delete to the Usn Journal.
  5862. //
  5863. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_FILE_DELETE | USN_REASON_CLOSE );
  5864. //
  5865. // Now loop to delete the file records.
  5866. //
  5867. while (Nukem != NULL) {
  5868. for (i = 0; i < 4; i++) {
  5869. if (Nukem->RecordNumbers[i] != 0) {
  5870. NtfsDeallocateMftRecord( IrpContext,
  5871. Vcb,
  5872. Nukem->RecordNumbers[i] );
  5873. }
  5874. }
  5875. TempNukem = Nukem->Next;
  5876. if (Nukem != &LocalNuke) {
  5877. ExFreeToPagedLookasideList( &NtfsNukemLookasideList, Nukem );
  5878. }
  5879. Nukem = TempNukem;
  5880. }
  5881. //
  5882. // Commit the delete - this has the nice effect of writing out all usn journal records for the
  5883. // delete after this we can safely remove the in memory structures (esp. the fcbtable)
  5884. // and not worry about retrying the request due to a logfilefull
  5885. //
  5886. NtfsCheckpointCurrentTransaction( IrpContext );
  5887. if (ParentScb != NULL) {
  5888. NtfsUpdateFcb( ParentScb->Fcb,
  5889. (FCB_INFO_CHANGED_LAST_CHANGE |
  5890. FCB_INFO_CHANGED_LAST_MOD |
  5891. FCB_INFO_UPDATE_LAST_ACCESS) );
  5892. }
  5893. SetFlag( Fcb->FcbState, FCB_STATE_FILE_DELETED );
  5894. //
  5895. // We need to mark all of the links on the file as gone.
  5896. //
  5897. for (Links = Fcb->LcbQueue.Flink;
  5898. Links != &Fcb->LcbQueue;
  5899. Links = Links->Flink) {
  5900. PLCB ThisLcb;
  5901. ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
  5902. if (ThisLcb->Scb == ParentScb) {
  5903. //
  5904. // Remove all remaining prefixes on this link.
  5905. // Make sure the resource is acquired.
  5906. //
  5907. if (!(*AcquiredParentScb)) {
  5908. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  5909. *AcquiredParentScb = TRUE;
  5910. }
  5911. NtfsRemovePrefix( ThisLcb );
  5912. //
  5913. // Remove any hash table entries for this Lcb.
  5914. //
  5915. NtfsRemoveHashEntriesForLcb( ThisLcb );
  5916. SetFlag( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE );
  5917. //
  5918. // We don't need to report any changes on this link.
  5919. //
  5920. ThisLcb->InfoFlags = 0;
  5921. }
  5922. }
  5923. //
  5924. // We need to mark all of the Scbs as gone.
  5925. //
  5926. for (Links = Fcb->ScbQueue.Flink;
  5927. Links != &Fcb->ScbQueue;
  5928. Links = Links->Flink) {
  5929. PSCB ThisScb;
  5930. ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  5931. ClearFlag( ThisScb->ScbState,
  5932. SCB_STATE_NOTIFY_ADD_STREAM |
  5933. SCB_STATE_NOTIFY_REMOVE_STREAM |
  5934. SCB_STATE_NOTIFY_RESIZE_STREAM |
  5935. SCB_STATE_NOTIFY_MODIFY_STREAM );
  5936. //
  5937. // Clear any remaining reservation - we didn't get rid of when deleting
  5938. // allocation. I.e the file is resident
  5939. //
  5940. if (NtfsIsTypeCodeUserData( ThisScb->AttributeTypeCode ) &&
  5941. (ThisScb->ScbType.Data.ReservedBitMap != NULL)) {
  5942. #ifdef BENL_DBG
  5943. ASSERT( !FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ) );
  5944. #endif
  5945. NtfsDeleteReservedBitmap( ThisScb );
  5946. }
  5947. if (!FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  5948. ThisScb->ValidDataToDisk =
  5949. ThisScb->Header.AllocationSize.QuadPart =
  5950. ThisScb->Header.FileSize.QuadPart =
  5951. ThisScb->Header.ValidDataLength.QuadPart = 0;
  5952. ThisScb->AttributeTypeCode = $UNUSED;
  5953. SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  5954. }
  5955. }
  5956. //
  5957. // We certainly don't need to any on disk update for this
  5958. // file now.
  5959. //
  5960. Fcb->InfoFlags = 0;
  5961. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  5962. } finally {
  5963. DebugUnwind( NtfsDeleteFile );
  5964. NtfsCleanupAttributeContext( IrpContext, &Context );
  5965. ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_QUOTA_DISABLE );
  5966. //
  5967. // Release the reparse point index Scb and the map handle.
  5968. //
  5969. if (AcquiredReparseIndex) {
  5970. NtfsReleaseScb( IrpContext, Vcb->ReparsePointTableScb );
  5971. }
  5972. if (InitializedMapHandle) {
  5973. NtOfsReleaseMap( IrpContext, &MapHandle );
  5974. }
  5975. //
  5976. // Drop the object id index if necessary.
  5977. //
  5978. if (AcquiredObjectIdIndex) {
  5979. NtfsReleaseScb( IrpContext, Vcb->ObjectIdTableScb );
  5980. }
  5981. //
  5982. // Need to roll-back the value of the file attributes and the reparse point
  5983. // flag in case of problems.
  5984. //
  5985. if (AbnormalTermination()) {
  5986. Fcb->Info.FileAttributes = IncomingFileAttributes;
  5987. Fcb->Info.ReparsePointTag = IncomingReparsePointTag;
  5988. }
  5989. }
  5990. return;
  5991. }
  5992. VOID
  5993. NtfsPrepareForUpdateDuplicate (
  5994. IN PIRP_CONTEXT IrpContext,
  5995. IN PFCB Fcb,
  5996. IN OUT PLCB *Lcb,
  5997. IN OUT PSCB *ParentScb,
  5998. IN BOOLEAN AcquireShared
  5999. )
  6000. /*++
  6001. Routine Description:
  6002. This routine is called to prepare for updating the duplicate information.
  6003. At the conclusion of this routine we will have the Lcb and Scb for the
  6004. update along with the Scb acquired. This routine will look at
  6005. the existing values for the input parameters in deciding what actions
  6006. need to be done.
  6007. Arguments:
  6008. Fcb - Fcb for the file. The file must already be acquired exclusively.
  6009. Lcb - This is the address to store the link to update. This may already
  6010. have a value.
  6011. ParentScb - This is the address to store the parent Scb for the update.
  6012. This may already point to a valid Scb.
  6013. AcquireShared - Indicates how to acquire the parent Scb.
  6014. Return Value:
  6015. None
  6016. --*/
  6017. {
  6018. PLIST_ENTRY Links;
  6019. PLCB ThisLcb;
  6020. PAGED_CODE();
  6021. //
  6022. // Start by trying to guarantee we have an Lcb for the update.
  6023. //
  6024. if (*Lcb == NULL) {
  6025. Links = Fcb->LcbQueue.Flink;
  6026. while (Links != &Fcb->LcbQueue) {
  6027. ThisLcb = CONTAINING_RECORD( Links,
  6028. LCB,
  6029. FcbLinks );
  6030. //
  6031. // We can use this link if it is still present on the
  6032. // disk and if we were passed a parent Scb, it matches
  6033. // the one for this Lcb.
  6034. //
  6035. if (!FlagOn( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE ) &&
  6036. ((*ParentScb == NULL) ||
  6037. (*ParentScb == ThisLcb->Scb) ||
  6038. ((ThisLcb == Fcb->Vcb->RootLcb) &&
  6039. (*ParentScb == Fcb->Vcb->RootIndexScb)))) {
  6040. *Lcb = ThisLcb;
  6041. break;
  6042. }
  6043. Links = Links->Flink;
  6044. }
  6045. }
  6046. //
  6047. // If we have an Lcb, try to find the correct Scb.
  6048. //
  6049. if ((*Lcb != NULL) && (*ParentScb == NULL)) {
  6050. if (*Lcb == Fcb->Vcb->RootLcb) {
  6051. *ParentScb = Fcb->Vcb->RootIndexScb;
  6052. } else {
  6053. *ParentScb = (*Lcb)->Scb;
  6054. }
  6055. }
  6056. //
  6057. // Acquire the parent Scb and put it in the transaction queue in the
  6058. // IrpContext.
  6059. //
  6060. if (*ParentScb != NULL) {
  6061. if (AcquireShared) {
  6062. NtfsAcquireSharedScbForTransaction( IrpContext, *ParentScb );
  6063. } else {
  6064. NtfsAcquireExclusiveScb( IrpContext, *ParentScb );
  6065. }
  6066. }
  6067. return;
  6068. }
  6069. VOID
  6070. NtfsUpdateDuplicateInfo (
  6071. IN PIRP_CONTEXT IrpContext,
  6072. IN PFCB Fcb,
  6073. IN PLCB Lcb OPTIONAL,
  6074. IN PSCB ParentScb OPTIONAL
  6075. )
  6076. /*++
  6077. Routine Description:
  6078. This routine is called to update the duplicate information for a file
  6079. in the duplicated information of its parent. If the Lcb is specified
  6080. then this parent is the parent to update. If the link is either an
  6081. NTFS or DOS only link then we must update the complementary link as
  6082. well. If no Lcb is specified then this open was by file id or the
  6083. original link has been deleted. In that case we will try to find a different
  6084. link to update.
  6085. Arguments:
  6086. Fcb - Fcb for the file.
  6087. Lcb - This is the link to update. Specified only if this is not
  6088. an open by Id operation.
  6089. ParentScb - This is the parent directory for the Lcb link if specified.
  6090. Return Value:
  6091. None
  6092. --*/
  6093. {
  6094. PQUICK_INDEX QuickIndex = NULL;
  6095. UCHAR Buffer[sizeof( FILE_NAME ) + 11 * sizeof( WCHAR )];
  6096. PFILE_NAME FileNameAttr;
  6097. BOOLEAN AcquiredFcbTable = FALSE;
  6098. BOOLEAN ReturnedExistingFcb = TRUE;
  6099. BOOLEAN Found;
  6100. UCHAR FileNameFlags;
  6101. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  6102. PVCB Vcb = Fcb->Vcb;
  6103. PFCB ParentFcb = NULL;
  6104. PAGED_CODE();
  6105. ASSERT_EXCLUSIVE_FCB( Fcb );
  6106. //
  6107. // Return immediately if the volume is locked or
  6108. // is mounted readonly.
  6109. //
  6110. if (FlagOn( Vcb->VcbState, (VCB_STATE_LOCKED |
  6111. VCB_STATE_MOUNT_READ_ONLY ))) {
  6112. return;
  6113. }
  6114. NtfsInitializeAttributeContext( &Context );
  6115. try {
  6116. //
  6117. // If we are updating the entry for the root then we know the
  6118. // file name attribute to build.
  6119. //
  6120. if (Fcb == Fcb->Vcb->RootIndexScb->Fcb) {
  6121. Lcb = Fcb->Vcb->RootLcb;
  6122. ParentScb = Fcb->Vcb->RootIndexScb;
  6123. QuickIndex = &Fcb->Vcb->RootLcb->QuickIndex;
  6124. FileNameAttr = (PFILE_NAME) Buffer;
  6125. RtlZeroMemory( FileNameAttr,
  6126. sizeof( FILE_NAME ));
  6127. NtfsBuildFileNameAttribute( IrpContext,
  6128. &Fcb->FileReference,
  6129. NtfsRootIndexString,
  6130. FILE_NAME_DOS | FILE_NAME_NTFS,
  6131. FileNameAttr );
  6132. //
  6133. // If we have and Lcb then it is either present or we noop this update.
  6134. //
  6135. } else if (ARGUMENT_PRESENT( Lcb )) {
  6136. if (!FlagOn( Lcb->LcbState, LCB_STATE_LINK_IS_GONE )) {
  6137. QuickIndex = &Lcb->QuickIndex;
  6138. FileNameAttr = Lcb->FileNameAttr;
  6139. } else {
  6140. leave;
  6141. }
  6142. //
  6143. // If there is no Lcb then lookup the first filename attribute
  6144. // and update its index entry. If there is a parent Scb then we
  6145. // must find a file name attribute for the same parent or we could
  6146. // get into a deadlock situation.
  6147. //
  6148. } else {
  6149. //
  6150. // We now have a name link to update. We will now need
  6151. // an Scb for the parent index. Remember that we may
  6152. // have to teardown the Scb. If we already have a ParentScb
  6153. // then we must find a link to the same parent or to the root.
  6154. // Otherwise we could hit a deadlock.
  6155. //
  6156. Found = NtfsLookupAttributeByCode( IrpContext,
  6157. Fcb,
  6158. &Fcb->FileReference,
  6159. $FILE_NAME,
  6160. &Context );
  6161. if (!Found) {
  6162. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6163. }
  6164. //
  6165. // Loop until we find a suitable link or there are no more on the file.
  6166. //
  6167. do {
  6168. FileNameAttr = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &Context ));
  6169. //
  6170. // If there is a parent and this attribute has the same parent we are
  6171. // done. Our caller will always have acquired the ParentScb.
  6172. //
  6173. if (ARGUMENT_PRESENT( ParentScb )) {
  6174. if (NtfsEqualMftRef( &FileNameAttr->ParentDirectory,
  6175. &ParentScb->Fcb->FileReference )) {
  6176. ASSERT_SHARED_SCB( ParentScb );
  6177. break;
  6178. }
  6179. //
  6180. // If this is the parent of this link is the root then
  6181. // acquire the root directory.
  6182. //
  6183. } else if (NtfsEqualMftRef( &FileNameAttr->ParentDirectory,
  6184. &Vcb->RootIndexScb->Fcb->FileReference )) {
  6185. ParentScb = Vcb->RootIndexScb;
  6186. NtfsAcquireSharedScbForTransaction( IrpContext, ParentScb );
  6187. break;
  6188. //
  6189. // We have a link for this file. If we weren't given a parent
  6190. // Scb then create one here.
  6191. //
  6192. } else if (!ARGUMENT_PRESENT( ParentScb )) {
  6193. NtfsAcquireFcbTable( IrpContext, Vcb );
  6194. AcquiredFcbTable = TRUE;
  6195. ParentFcb = NtfsCreateFcb( IrpContext,
  6196. Vcb,
  6197. FileNameAttr->ParentDirectory,
  6198. FALSE,
  6199. TRUE,
  6200. &ReturnedExistingFcb );
  6201. ParentFcb->ReferenceCount += 1;
  6202. if (!NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK | ACQUIRE_DONT_WAIT )) {
  6203. NtfsReleaseFcbTable( IrpContext, Vcb );
  6204. NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  6205. NtfsAcquireFcbTable( IrpContext, Vcb );
  6206. }
  6207. ParentFcb->ReferenceCount -= 1;
  6208. NtfsReleaseFcbTable( IrpContext, Vcb );
  6209. AcquiredFcbTable = FALSE;
  6210. ParentScb = NtfsCreateScb( IrpContext,
  6211. ParentFcb,
  6212. $INDEX_ALLOCATION,
  6213. &NtfsFileNameIndex,
  6214. FALSE,
  6215. NULL );
  6216. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  6217. break;
  6218. }
  6219. } while (Found = NtfsLookupNextAttributeByCode( IrpContext,
  6220. Fcb,
  6221. $FILE_NAME,
  6222. &Context ));
  6223. //
  6224. // If we didn't find anything then return.
  6225. //
  6226. if (!Found) { leave; }
  6227. }
  6228. //
  6229. // Now update the filename in the parent index.
  6230. //
  6231. NtfsUpdateFileNameInIndex( IrpContext,
  6232. ParentScb,
  6233. FileNameAttr,
  6234. &Fcb->Info,
  6235. QuickIndex );
  6236. //
  6237. // If this filename is either NTFS-ONLY or DOS-ONLY then
  6238. // we need to find the other link.
  6239. //
  6240. if ((FileNameAttr->Flags == FILE_NAME_NTFS) ||
  6241. (FileNameAttr->Flags == FILE_NAME_DOS)) {
  6242. //
  6243. // Find out which flag we should be looking for.
  6244. //
  6245. if (FlagOn( FileNameAttr->Flags, FILE_NAME_NTFS )) {
  6246. FileNameFlags = FILE_NAME_DOS;
  6247. } else {
  6248. FileNameFlags = FILE_NAME_NTFS;
  6249. }
  6250. if (!ARGUMENT_PRESENT( Lcb )) {
  6251. NtfsCleanupAttributeContext( IrpContext, &Context );
  6252. NtfsInitializeAttributeContext( &Context );
  6253. }
  6254. //
  6255. // Now scan for the filename attribute we need.
  6256. //
  6257. Found = NtfsLookupAttributeByCode( IrpContext,
  6258. Fcb,
  6259. &Fcb->FileReference,
  6260. $FILE_NAME,
  6261. &Context );
  6262. while (Found) {
  6263. FileNameAttr = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &Context ));
  6264. if (FileNameAttr->Flags == FileNameFlags) {
  6265. break;
  6266. }
  6267. Found = NtfsLookupNextAttributeByCode( IrpContext,
  6268. Fcb,
  6269. $FILE_NAME,
  6270. &Context );
  6271. }
  6272. //
  6273. // We should have found the entry.
  6274. //
  6275. if (!Found) {
  6276. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6277. }
  6278. NtfsUpdateFileNameInIndex( IrpContext,
  6279. ParentScb,
  6280. FileNameAttr,
  6281. &Fcb->Info,
  6282. NULL );
  6283. }
  6284. } finally {
  6285. DebugUnwind( NtfsUpdateDuplicateInfo );
  6286. if (AcquiredFcbTable) {
  6287. NtfsReleaseFcbTable( IrpContext, Vcb );
  6288. }
  6289. //
  6290. // Cleanup the attribute context for this attribute search.
  6291. //
  6292. NtfsCleanupAttributeContext( IrpContext, &Context );
  6293. //
  6294. // If we created the ParentFcb here then release it and
  6295. // call teardown on it.
  6296. //
  6297. if (!ReturnedExistingFcb && (ParentFcb != NULL)) {
  6298. NtfsTeardownStructures( IrpContext,
  6299. ParentFcb,
  6300. NULL,
  6301. FALSE,
  6302. 0,
  6303. NULL );
  6304. }
  6305. }
  6306. return;
  6307. }
  6308. VOID
  6309. NtfsUpdateLcbDuplicateInfo (
  6310. IN PFCB Fcb,
  6311. IN PLCB Lcb
  6312. )
  6313. /*++
  6314. Routine Description:
  6315. This routine is called after updating duplicate information via an Lcb.
  6316. We want to clear the info flags for this Lcb and any complementary Lcb
  6317. it may be part of. We also want to OR in the Info flags in the Fcb with
  6318. any other Lcb's attached to the Fcb so we will update those in a timely
  6319. fashion as well.
  6320. Arguments:
  6321. Fcb - Fcb for the file.
  6322. Lcb - Lcb used to update duplicate information. It may not be present but
  6323. that would be a rare case and we will perform that test here.
  6324. Return Value:
  6325. None
  6326. --*/
  6327. {
  6328. UCHAR FileNameFlags;
  6329. PLCB NextLcb;
  6330. PLIST_ENTRY Links;
  6331. PAGED_CODE();
  6332. //
  6333. // No work to do unless we were passed an Lcb.
  6334. //
  6335. if (Lcb != NULL) {
  6336. //
  6337. // Check if this is an NTFS only or DOS only link.
  6338. //
  6339. if (Lcb->FileNameAttr->Flags == FILE_NAME_NTFS) {
  6340. FileNameFlags = FILE_NAME_DOS;
  6341. } else if (Lcb->FileNameAttr->Flags == FILE_NAME_DOS) {
  6342. FileNameFlags = FILE_NAME_NTFS;
  6343. } else {
  6344. FileNameFlags = (UCHAR) -1;
  6345. }
  6346. Lcb->InfoFlags = 0;
  6347. Links = Fcb->LcbQueue.Flink;
  6348. do {
  6349. NextLcb = CONTAINING_RECORD( Links,
  6350. LCB,
  6351. FcbLinks );
  6352. if (NextLcb != Lcb) {
  6353. if (NextLcb->FileNameAttr->Flags == FileNameFlags) {
  6354. NextLcb->InfoFlags = 0;
  6355. } else {
  6356. SetFlag( NextLcb->InfoFlags, Fcb->InfoFlags );
  6357. }
  6358. }
  6359. Links = Links->Flink;
  6360. } while (Links != &Fcb->LcbQueue);
  6361. }
  6362. return;
  6363. }
  6364. VOID
  6365. NtfsUpdateFcb (
  6366. IN PFCB Fcb,
  6367. IN ULONG ChangeFlags
  6368. )
  6369. /*++
  6370. Routine Description:
  6371. This routine is called when a timestamp may be updated on an Fcb which
  6372. may have no open handles. We update the time stamps for the flags passed
  6373. in.
  6374. Arguments:
  6375. Fcb - Fcb for the file.
  6376. ChangeFlags - Flags indicating which times to update.
  6377. Return Value:
  6378. None
  6379. --*/
  6380. {
  6381. PAGED_CODE();
  6382. //
  6383. // We need to update the parent directory's time stamps
  6384. // to reflect this change.
  6385. //
  6386. //
  6387. // The change flag should always be set.
  6388. //
  6389. ASSERT( FlagOn( ChangeFlags, FCB_INFO_CHANGED_LAST_CHANGE ));
  6390. KeQuerySystemTime( (PLARGE_INTEGER)&Fcb->Info.LastChangeTime );
  6391. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  6392. //
  6393. // Test for the other flags which may be set.
  6394. //
  6395. if (FlagOn( ChangeFlags, FCB_INFO_CHANGED_LAST_MOD )) {
  6396. Fcb->Info.LastModificationTime = Fcb->Info.LastChangeTime;
  6397. }
  6398. if (FlagOn( ChangeFlags, FCB_INFO_UPDATE_LAST_ACCESS )) {
  6399. Fcb->CurrentLastAccess = Fcb->Info.LastChangeTime;
  6400. }
  6401. SetFlag( Fcb->InfoFlags, ChangeFlags );
  6402. return;
  6403. }
  6404. VOID
  6405. NtfsAddLink (
  6406. IN PIRP_CONTEXT IrpContext,
  6407. IN BOOLEAN CreatePrimaryLink,
  6408. IN PSCB ParentScb,
  6409. IN PFCB Fcb,
  6410. IN PFILE_NAME FileNameAttr,
  6411. IN PBOOLEAN LogIt OPTIONAL,
  6412. OUT PUCHAR FileNameFlags,
  6413. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  6414. IN PNAME_PAIR NamePair OPTIONAL,
  6415. IN PINDEX_CONTEXT IndexContext OPTIONAL
  6416. )
  6417. /*++
  6418. Routine Description:
  6419. This routine adds a link to a file by adding the filename attribute
  6420. for the filename to the file and inserting the name in the parent Scb
  6421. index. If we are creating the primary link for the file and need
  6422. to generate an auxilary name, we will do that here. Use the optional
  6423. NamePair to suggest auxilary names if provided.
  6424. Arguments:
  6425. CreatePrimaryLink - Indicates if we are creating the main Ntfs name
  6426. for the file.
  6427. ParentScb - This is the Scb to add the index entry for this link to.
  6428. Fcb - This is the file to add the hard link to.
  6429. FileNameAttr - File name attribute which is guaranteed only to have the
  6430. name in it.
  6431. LogIt - Indicates whether we should log the creation of this name. If not
  6432. specified then we always log the name creation. On exit we will
  6433. update this to TRUE if we logged the name creation because it
  6434. might cause a split.
  6435. FileNameFlags - We return the file name flags we use to create the link.
  6436. QuickIndex - If specified, supplies a pointer to a quik lookup structure
  6437. to be updated by this routine.
  6438. NamePair - If specified, supplies names that will be checked first as
  6439. possible auxilary names
  6440. IndexContext - Previous result of doing the lookup for the name in the index.
  6441. Return Value:
  6442. None
  6443. --*/
  6444. {
  6445. BOOLEAN LocalLogIt = TRUE;
  6446. PAGED_CODE();
  6447. DebugTrace( +1, Dbg, ("NtfsAddLink: Entered\n") );
  6448. if (!ARGUMENT_PRESENT( LogIt )) {
  6449. LogIt = &LocalLogIt;
  6450. }
  6451. *FileNameFlags = 0;
  6452. //
  6453. // Next add this entry to parent. It is possible that this is a link,
  6454. // an Ntfs name, a DOS name or Ntfs/Dos name. We use the filename
  6455. // attribute structure from earlier, but need to add more information.
  6456. //
  6457. FileNameAttr->ParentDirectory = ParentScb->Fcb->FileReference;
  6458. RtlCopyMemory( &FileNameAttr->Info,
  6459. &Fcb->Info,
  6460. sizeof( DUPLICATED_INFORMATION ));
  6461. FileNameAttr->Flags = 0;
  6462. //
  6463. // We will override the CreatePrimaryLink with the value in the
  6464. // registry.
  6465. //
  6466. NtfsAddNameToParent( IrpContext,
  6467. ParentScb,
  6468. Fcb,
  6469. (BOOLEAN) (FlagOn( NtfsData.Flags,
  6470. NTFS_FLAGS_CREATE_8DOT3_NAMES ) &&
  6471. CreatePrimaryLink),
  6472. LogIt,
  6473. FileNameAttr,
  6474. FileNameFlags,
  6475. QuickIndex,
  6476. NamePair,
  6477. IndexContext );
  6478. //
  6479. // If the name is Ntfs only, we need to generate the DOS name.
  6480. //
  6481. if (*FileNameFlags == FILE_NAME_NTFS) {
  6482. UNICODE_STRING NtfsName;
  6483. NtfsName.Length = (USHORT)(FileNameAttr->FileNameLength * sizeof(WCHAR));
  6484. NtfsName.Buffer = FileNameAttr->FileName;
  6485. NtfsAddDosOnlyName( IrpContext,
  6486. ParentScb,
  6487. Fcb,
  6488. NtfsName,
  6489. *LogIt,
  6490. (NamePair ? &NamePair->Short : NULL) );
  6491. }
  6492. DebugTrace( -1, Dbg, ("NtfsAddLink: Exit\n") );
  6493. return;
  6494. }
  6495. VOID
  6496. NtfsRemoveLink (
  6497. IN PIRP_CONTEXT IrpContext,
  6498. IN PFCB Fcb,
  6499. IN PSCB ParentScb,
  6500. IN UNICODE_STRING LinkName,
  6501. IN OUT PNAME_PAIR NamePair OPTIONAL,
  6502. IN OUT PNTFS_TUNNELED_DATA TunneledData OPTIONAL
  6503. )
  6504. /*++
  6505. Routine Description:
  6506. This routine removes a hard link to a file by removing the filename attribute
  6507. for the filename from the file and removing the name from the parent Scb
  6508. index. It will also remove the other half of a primary link pair.
  6509. A name pair may be used to capture the names.
  6510. Arguments:
  6511. Fcb - This is the file to remove the hard link from
  6512. ParentScb - This is the Scb to remove the index entry for this link from
  6513. LinkName - This is the file name to remove. It will be exact case.
  6514. NamePair - optional name pair for capture
  6515. Return Value:
  6516. None
  6517. --*/
  6518. {
  6519. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6520. ATTRIBUTE_ENUMERATION_CONTEXT OidAttrContext;
  6521. PFILE_NAME FoundFileName;
  6522. UCHAR FileNameFlags;
  6523. UCHAR *ObjectId;
  6524. PAGED_CODE();
  6525. DebugTrace( +1, Dbg, ("NtfsRemoveLink: Entered\n") );
  6526. NtfsInitializeAttributeContext( &AttrContext );
  6527. NtfsInitializeAttributeContext( &OidAttrContext );
  6528. //
  6529. // Use a try-finally to facilitate cleanup.
  6530. //
  6531. try {
  6532. //
  6533. // Now loop through the filenames and find a match.
  6534. // We better find at least one.
  6535. //
  6536. if (!NtfsLookupAttributeByCode( IrpContext,
  6537. Fcb,
  6538. &Fcb->FileReference,
  6539. $FILE_NAME,
  6540. &AttrContext )) {
  6541. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6542. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6543. }
  6544. //
  6545. // Now keep looking until we find a match.
  6546. //
  6547. while (TRUE) {
  6548. FoundFileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  6549. //
  6550. // Do an exact memory comparison.
  6551. //
  6552. if ((*(PLONGLONG)&FoundFileName->ParentDirectory ==
  6553. *(PLONGLONG)&ParentScb->Fcb->FileReference ) &&
  6554. ((FoundFileName->FileNameLength * sizeof( WCHAR )) == (ULONG)LinkName.Length) &&
  6555. (RtlEqualMemory( LinkName.Buffer,
  6556. FoundFileName->FileName,
  6557. LinkName.Length ))) {
  6558. break;
  6559. }
  6560. //
  6561. // Get the next filename attribute.
  6562. //
  6563. if (!NtfsLookupNextAttributeByCode( IrpContext,
  6564. Fcb,
  6565. $FILE_NAME,
  6566. &AttrContext )) {
  6567. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6568. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6569. }
  6570. }
  6571. //
  6572. // Capture the name into caller's area
  6573. //
  6574. if (ARGUMENT_PRESENT(NamePair)) {
  6575. NtfsCopyNameToNamePair( NamePair,
  6576. FoundFileName->FileName,
  6577. FoundFileName->FileNameLength,
  6578. FoundFileName->Flags );
  6579. }
  6580. //
  6581. // It's important to do any object id operations now, before we
  6582. // acquire the Mft. Otherwise we risk a deadlock.
  6583. //
  6584. if (ARGUMENT_PRESENT(TunneledData)) {
  6585. //
  6586. // Find and store the object id, if any, for this file.
  6587. //
  6588. if (NtfsLookupAttributeByCode( IrpContext,
  6589. Fcb,
  6590. &Fcb->FileReference,
  6591. $OBJECT_ID,
  6592. &OidAttrContext )) {
  6593. TunneledData->HasObjectId = TRUE;
  6594. ObjectId = (UCHAR *) NtfsAttributeValue( NtfsFoundAttribute( &OidAttrContext ));
  6595. RtlCopyMemory( TunneledData->ObjectIdBuffer.ObjectId,
  6596. ObjectId,
  6597. sizeof(TunneledData->ObjectIdBuffer.ObjectId) );
  6598. NtfsGetObjectIdExtendedInfo( IrpContext,
  6599. Fcb->Vcb,
  6600. ObjectId,
  6601. TunneledData->ObjectIdBuffer.ExtendedInfo );
  6602. }
  6603. }
  6604. //
  6605. // Now delete the name from the parent Scb.
  6606. //
  6607. NtfsDeleteIndexEntry( IrpContext,
  6608. ParentScb,
  6609. FoundFileName,
  6610. &Fcb->FileReference );
  6611. //
  6612. // Remember the filename flags for this entry.
  6613. //
  6614. FileNameFlags = FoundFileName->Flags;
  6615. //
  6616. // Now delete the entry. Log the operation, discard the file record
  6617. // if empty, and release any and all allocation.
  6618. //
  6619. NtfsDeleteAttributeRecord( IrpContext,
  6620. Fcb,
  6621. (DELETE_LOG_OPERATION |
  6622. DELETE_RELEASE_FILE_RECORD |
  6623. DELETE_RELEASE_ALLOCATION),
  6624. &AttrContext );
  6625. //
  6626. // If the link is a partial link, we need to remove the second
  6627. // half of the link.
  6628. //
  6629. if (FlagOn( FileNameFlags, (FILE_NAME_NTFS | FILE_NAME_DOS) )
  6630. && (FileNameFlags != (FILE_NAME_NTFS | FILE_NAME_DOS))) {
  6631. NtfsRemoveLinkViaFlags( IrpContext,
  6632. Fcb,
  6633. ParentScb,
  6634. (UCHAR)(FlagOn( FileNameFlags, FILE_NAME_NTFS )
  6635. ? FILE_NAME_DOS
  6636. : FILE_NAME_NTFS),
  6637. NamePair,
  6638. NULL );
  6639. }
  6640. } finally {
  6641. DebugUnwind( NtfsRemoveLink );
  6642. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  6643. NtfsCleanupAttributeContext( IrpContext, &OidAttrContext );
  6644. DebugTrace( -1, Dbg, ("NtfsRemoveLink: Exit\n") );
  6645. }
  6646. return;
  6647. }
  6648. VOID
  6649. NtfsRemoveLinkViaFlags (
  6650. IN PIRP_CONTEXT IrpContext,
  6651. IN PFCB Fcb,
  6652. IN PSCB Scb,
  6653. IN UCHAR FileNameFlags,
  6654. IN OUT PNAME_PAIR NamePair OPTIONAL,
  6655. OUT PUNICODE_STRING FileName OPTIONAL
  6656. )
  6657. /*++
  6658. Routine Description:
  6659. This routine is called to remove only a Dos name or only an Ntfs name. We
  6660. already must know that these will be described by separate filename attributes.
  6661. A name pair may be used to capture the name.
  6662. Arguments:
  6663. Fcb - This is the file to remove the hard link from
  6664. ParentScb - This is the Scb to remove the index entry for this link from
  6665. FileNameFlags - This is the single name flag that we must match exactly.
  6666. NamePair - Optional name pair for capture
  6667. FileName - Optional pointer to unicode string. If specified we allocate a buffer and
  6668. return the name deleted.
  6669. Return Value:
  6670. None
  6671. --*/
  6672. {
  6673. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6674. PFILE_NAME FileNameAttr;
  6675. PFILE_NAME FoundFileName;
  6676. PAGED_CODE();
  6677. DebugTrace( +1, Dbg, ("NtfsRemoveLinkViaFlags: Entered\n") );
  6678. NtfsInitializeAttributeContext( &AttrContext );
  6679. FileNameAttr = NULL;
  6680. //
  6681. // Use a try-finally to facilitate cleanup.
  6682. //
  6683. try {
  6684. //
  6685. // Now loop through the filenames and find a match.
  6686. // We better find at least one.
  6687. //
  6688. if (!NtfsLookupAttributeByCode( IrpContext,
  6689. Fcb,
  6690. &Fcb->FileReference,
  6691. $FILE_NAME,
  6692. &AttrContext )) {
  6693. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6694. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6695. }
  6696. //
  6697. // Now keep looking until we find a match.
  6698. //
  6699. while (TRUE) {
  6700. FoundFileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  6701. //
  6702. // Check for an exact flag match.
  6703. //
  6704. if ((*(PLONGLONG)&FoundFileName->ParentDirectory ==
  6705. *(PLONGLONG)&Scb->Fcb->FileReference) &&
  6706. (FoundFileName->Flags == FileNameFlags)) {
  6707. break;
  6708. }
  6709. //
  6710. // Get the next filename attribute.
  6711. //
  6712. if (!NtfsLookupNextAttributeByCode( IrpContext,
  6713. Fcb,
  6714. $FILE_NAME,
  6715. &AttrContext )) {
  6716. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb@ %08lx\n", Fcb) );
  6717. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6718. }
  6719. }
  6720. //
  6721. // Capture the name into caller's area
  6722. //
  6723. if (ARGUMENT_PRESENT(NamePair)) {
  6724. NtfsCopyNameToNamePair( NamePair,
  6725. FoundFileName->FileName,
  6726. FoundFileName->FileNameLength,
  6727. FoundFileName->Flags );
  6728. }
  6729. FileNameAttr = NtfsAllocatePool( PagedPool,
  6730. sizeof( FILE_NAME ) + (FoundFileName->FileNameLength << 1) );
  6731. //
  6732. // We build the file name attribute for the search.
  6733. //
  6734. RtlCopyMemory( FileNameAttr,
  6735. FoundFileName,
  6736. NtfsFileNameSize( FoundFileName ));
  6737. //
  6738. // Now delete the entry.
  6739. //
  6740. NtfsDeleteAttributeRecord( IrpContext,
  6741. Fcb,
  6742. (DELETE_LOG_OPERATION |
  6743. DELETE_RELEASE_FILE_RECORD |
  6744. DELETE_RELEASE_ALLOCATION),
  6745. &AttrContext );
  6746. //
  6747. // Now delete the name from the parent Scb.
  6748. //
  6749. NtfsDeleteIndexEntry( IrpContext,
  6750. Scb,
  6751. FileNameAttr,
  6752. &Fcb->FileReference );
  6753. } finally {
  6754. DebugUnwind( NtfsRemoveLinkViaFlags );
  6755. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  6756. if (FileNameAttr != NULL) {
  6757. //
  6758. // If the user passed in a unicode string then make this look like the name
  6759. // and store the buffer into the input pointer.
  6760. //
  6761. if (ARGUMENT_PRESENT( FileName )) {
  6762. ASSERT( FileName->Buffer == NULL );
  6763. FileName->MaximumLength = FileName->Length = FileNameAttr->FileNameLength * sizeof( WCHAR );
  6764. RtlMoveMemory( FileNameAttr,
  6765. FileNameAttr->FileName,
  6766. FileName->Length );
  6767. FileName->Buffer = (PVOID) FileNameAttr;
  6768. } else {
  6769. NtfsFreePool( FileNameAttr );
  6770. }
  6771. }
  6772. DebugTrace( -1, Dbg, ("NtfsRemoveLinkViaFlags: Exit\n") );
  6773. }
  6774. return;
  6775. }
  6776. VOID
  6777. NtfsUpdateFileNameFlags (
  6778. IN PIRP_CONTEXT IrpContext,
  6779. IN PFCB Fcb,
  6780. IN PSCB ParentScb,
  6781. IN UCHAR FileNameFlags,
  6782. IN PFILE_NAME FileNameLink
  6783. )
  6784. /*++
  6785. Routine Description:
  6786. This routine is called to perform the file name flag update on a name
  6787. link. Nothing else about the name is changing except for the flag
  6788. changes.
  6789. Arguments:
  6790. Fcb - This is the file to change the link flags on.
  6791. ParentScb - This is the Scb which contains the link.
  6792. FileNameFlags - This is the single name flag that we want to change to.
  6793. FileNameLink - Pointer to a copy of the link to change.
  6794. Return Value:
  6795. None
  6796. --*/
  6797. {
  6798. PFILE_NAME FoundFileName;
  6799. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  6800. BOOLEAN CleanupContext = FALSE;
  6801. PAGED_CODE();
  6802. //
  6803. // Use a try-finally to facilitate cleanup.
  6804. //
  6805. try {
  6806. //
  6807. // Look up the correct attribute in the file record.
  6808. //
  6809. NtfsInitializeAttributeContext( &AttrContext );
  6810. CleanupContext = TRUE;
  6811. if (!NtfsLookupAttributeByCode( IrpContext,
  6812. Fcb,
  6813. &Fcb->FileReference,
  6814. $FILE_NAME,
  6815. &AttrContext )) {
  6816. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6817. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6818. }
  6819. //
  6820. // Now keep looking till we find the one we want.
  6821. //
  6822. while (TRUE) {
  6823. FoundFileName = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  6824. //
  6825. // If the names match exactly and the parent directories match then
  6826. // we have a match.
  6827. //
  6828. if (NtfsEqualMftRef( &FileNameLink->ParentDirectory,
  6829. &FoundFileName->ParentDirectory ) &&
  6830. (FileNameLink->FileNameLength == FoundFileName->FileNameLength) &&
  6831. RtlEqualMemory( FileNameLink->FileName,
  6832. FoundFileName->FileName,
  6833. FileNameLink->FileNameLength * sizeof( WCHAR ))) {
  6834. break;
  6835. }
  6836. //
  6837. // Get the next filename attribute.
  6838. //
  6839. if (!NtfsLookupNextAttributeByCode( IrpContext,
  6840. Fcb,
  6841. $FILE_NAME,
  6842. &AttrContext )) {
  6843. //
  6844. // This is bad. We should have found a match.
  6845. //
  6846. DebugTrace( 0, Dbg, ("Can't find filename attribute Fcb @ %08lx\n", Fcb) );
  6847. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  6848. }
  6849. }
  6850. //
  6851. // Unfortunately we can't log the change to the file name flags only so we will have to remove
  6852. // and reinsert the index entry.
  6853. //
  6854. NtfsDeleteIndexEntry( IrpContext,
  6855. ParentScb,
  6856. FoundFileName,
  6857. &Fcb->FileReference );
  6858. //
  6859. // Update just the flags field.
  6860. //
  6861. NtfsChangeAttributeValue( IrpContext,
  6862. Fcb,
  6863. FIELD_OFFSET( FILE_NAME, Flags ),
  6864. &FileNameFlags,
  6865. sizeof( UCHAR ),
  6866. FALSE,
  6867. TRUE,
  6868. FALSE,
  6869. TRUE,
  6870. &AttrContext );
  6871. //
  6872. // Now reinsert the name in the index.
  6873. //
  6874. NtfsAddIndexEntry( IrpContext,
  6875. ParentScb,
  6876. FoundFileName,
  6877. NtfsFileNameSize( FoundFileName ),
  6878. &Fcb->FileReference,
  6879. NULL,
  6880. NULL );
  6881. } finally {
  6882. DebugUnwind( NtfsUpdateFileNameFlags );
  6883. if (CleanupContext) {
  6884. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  6885. }
  6886. }
  6887. return;
  6888. }
  6889. //
  6890. // This routine is intended only for RESTART.
  6891. //
  6892. VOID
  6893. NtfsRestartInsertAttribute (
  6894. IN PIRP_CONTEXT IrpContext,
  6895. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  6896. IN ULONG RecordOffset,
  6897. IN PATTRIBUTE_RECORD_HEADER Attribute,
  6898. IN PUNICODE_STRING AttributeName OPTIONAL,
  6899. IN PVOID ValueOrMappingPairs OPTIONAL,
  6900. IN ULONG Length
  6901. )
  6902. /*++
  6903. Routine Description:
  6904. This routine performs a simple insert of an attribute record into a
  6905. file record, without worrying about Bcbs or logging.
  6906. Arguments:
  6907. FileRecord - File record into which the attribute is to be inserted.
  6908. RecordOffset - ByteOffset within the file record at which insert is to occur.
  6909. Attribute - The attribute record to be inserted.
  6910. AttributeName - May pass an optional attribute name in the running system
  6911. only.
  6912. ValueOrMappingPairs - May pass a value or mapping pairs pointer in the
  6913. running system only.
  6914. Length - Length of the value or mapping pairs array in bytes - nonzero in
  6915. the running system only. If nonzero and the above pointer is NULL,
  6916. then a value is to be zeroed.
  6917. Return Value:
  6918. None
  6919. --*/
  6920. {
  6921. PVOID From, To;
  6922. ULONG MoveLength;
  6923. ULONG AttributeHeaderSize;
  6924. ASSERT_IRP_CONTEXT( IrpContext );
  6925. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  6926. PAGED_CODE();
  6927. DebugTrace( +1, Dbg, ("NtfsRestartInsertAttribute\n") );
  6928. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  6929. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  6930. DebugTrace( 0, Dbg, ("Attribute = %08lx\n", Attribute) );
  6931. //
  6932. // First make room for the attribute
  6933. //
  6934. From = (PCHAR)FileRecord + RecordOffset;
  6935. To = (PCHAR)From + Attribute->RecordLength;
  6936. MoveLength = FileRecord->FirstFreeByte - RecordOffset;
  6937. RtlMoveMemory( To, From, MoveLength );
  6938. //
  6939. // If there is either an attribute name or Length is nonzero, then
  6940. // we are in the running system, and we are to assemble the attribute
  6941. // in place.
  6942. //
  6943. if ((Length != 0) || ARGUMENT_PRESENT(AttributeName)) {
  6944. //
  6945. // First move the attribute header in.
  6946. //
  6947. if (Attribute->FormCode == RESIDENT_FORM) {
  6948. AttributeHeaderSize = SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  6949. } else if (Attribute->NameOffset != 0) {
  6950. AttributeHeaderSize = Attribute->NameOffset;
  6951. } else {
  6952. AttributeHeaderSize = Attribute->Form.Nonresident.MappingPairsOffset;
  6953. }
  6954. RtlCopyMemory( From,
  6955. Attribute,
  6956. AttributeHeaderSize );
  6957. if (ARGUMENT_PRESENT(AttributeName)) {
  6958. RtlCopyMemory( (PCHAR)From + Attribute->NameOffset,
  6959. AttributeName->Buffer,
  6960. AttributeName->Length );
  6961. }
  6962. //
  6963. // If a value was specified, move it in. Else the caller just wants us
  6964. // to clear for that much.
  6965. //
  6966. if (ARGUMENT_PRESENT(ValueOrMappingPairs)) {
  6967. RtlCopyMemory( (PCHAR)From +
  6968. ((Attribute->FormCode == RESIDENT_FORM) ?
  6969. Attribute->Form.Resident.ValueOffset :
  6970. Attribute->Form.Nonresident.MappingPairsOffset),
  6971. ValueOrMappingPairs,
  6972. Length );
  6973. //
  6974. // Only the resident form will pass a NULL pointer.
  6975. //
  6976. } else {
  6977. RtlZeroMemory( (PCHAR)From + Attribute->Form.Resident.ValueOffset,
  6978. Length );
  6979. }
  6980. //
  6981. // For the restart case, we really only have to insert the attribute.
  6982. // (Note we can also hit this case in the running system when a resident
  6983. // attribute is being created with no name and a null value.)
  6984. //
  6985. } else {
  6986. //
  6987. // Now move the attribute in.
  6988. //
  6989. RtlCopyMemory( From, Attribute, Attribute->RecordLength );
  6990. }
  6991. //
  6992. // Update the file record.
  6993. //
  6994. FileRecord->FirstFreeByte += Attribute->RecordLength;
  6995. //
  6996. // We only need to do this if we would be incrementing the instance
  6997. // number. In the abort or restart case, we don't need to do this.
  6998. //
  6999. if (FileRecord->NextAttributeInstance <= Attribute->Instance) {
  7000. FileRecord->NextAttributeInstance = Attribute->Instance + 1;
  7001. }
  7002. //
  7003. // Remember to increment the reference count if this attribute is indexed.
  7004. //
  7005. if (FlagOn(Attribute->Form.Resident.ResidentFlags, RESIDENT_FORM_INDEXED)) {
  7006. FileRecord->ReferenceCount += 1;
  7007. }
  7008. DebugTrace( -1, Dbg, ("NtfsRestartInsertAttribute -> VOID\n") );
  7009. return;
  7010. }
  7011. //
  7012. // This routine is intended only for RESTART.
  7013. //
  7014. VOID
  7015. NtfsRestartRemoveAttribute (
  7016. IN PIRP_CONTEXT IrpContext,
  7017. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  7018. IN ULONG RecordOffset
  7019. )
  7020. /*++
  7021. Routine Description:
  7022. This routine performs a simple remove of an attribute record from a
  7023. file record, without worrying about Bcbs or logging.
  7024. Arguments:
  7025. FileRecord - File record from which the attribute is to be removed.
  7026. RecordOffset - ByteOffset within the file record at which remove is to occur.
  7027. Return Value:
  7028. None
  7029. --*/
  7030. {
  7031. PATTRIBUTE_RECORD_HEADER Attribute;
  7032. ASSERT_IRP_CONTEXT( IrpContext );
  7033. PAGED_CODE();
  7034. DebugTrace( +1, Dbg, ("NtfsRestartRemoveAttribute\n") );
  7035. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  7036. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  7037. //
  7038. // Calculate the address of the attribute we are removing.
  7039. //
  7040. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord + RecordOffset);
  7041. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  7042. //
  7043. // Reduce first free byte by the amount we removed.
  7044. //
  7045. FileRecord->FirstFreeByte -= Attribute->RecordLength;
  7046. //
  7047. // Remember to decrement the reference count if this attribute is indexed.
  7048. //
  7049. if (FlagOn(Attribute->Form.Resident.ResidentFlags, RESIDENT_FORM_INDEXED)) {
  7050. FileRecord->ReferenceCount -= 1;
  7051. }
  7052. //
  7053. // Remove the attribute by moving the rest of the record down.
  7054. //
  7055. RtlMoveMemory( Attribute,
  7056. (PCHAR)Attribute + Attribute->RecordLength,
  7057. FileRecord->FirstFreeByte - RecordOffset );
  7058. DebugTrace( -1, Dbg, ("NtfsRestartRemoveAttribute -> VOID\n") );
  7059. return;
  7060. }
  7061. //
  7062. // This routine is intended only for RESTART.
  7063. //
  7064. VOID
  7065. NtfsRestartChangeAttributeSize (
  7066. IN PIRP_CONTEXT IrpContext,
  7067. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  7068. IN PATTRIBUTE_RECORD_HEADER Attribute,
  7069. IN ULONG NewRecordLength
  7070. )
  7071. /*++
  7072. Routine Description:
  7073. This routine changes the size of an attribute, and makes the related
  7074. changes in the attribute record.
  7075. Arguments:
  7076. FileRecord - Pointer to the file record in which the attribute resides.
  7077. Attribute - Pointer to the attribute whose size is changing.
  7078. NewRecordLength - New attribute record length.
  7079. Return Value:
  7080. None.
  7081. --*/
  7082. {
  7083. LONG SizeChange = NewRecordLength - Attribute->RecordLength;
  7084. PVOID AttributeEnd = Add2Ptr(Attribute, Attribute->RecordLength);
  7085. UNREFERENCED_PARAMETER( IrpContext );
  7086. PAGED_CODE();
  7087. DebugTrace( +1, Dbg, ("NtfsRestartChangeAttributeSize\n") );
  7088. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  7089. DebugTrace( 0, Dbg, ("Attribute = %08lx\n", Attribute) );
  7090. DebugTrace( 0, Dbg, ("NewRecordLength = %08lx\n", NewRecordLength) );
  7091. //
  7092. // First move the end of the file record after the attribute we are changing.
  7093. //
  7094. RtlMoveMemory( Add2Ptr(Attribute, NewRecordLength),
  7095. AttributeEnd,
  7096. FileRecord->FirstFreeByte - PtrOffset(FileRecord, AttributeEnd) );
  7097. //
  7098. // Now update the file and attribute records.
  7099. //
  7100. FileRecord->FirstFreeByte += SizeChange;
  7101. Attribute->RecordLength = NewRecordLength;
  7102. DebugTrace( -1, Dbg, ("NtfsRestartChangeAttributeSize -> VOID\n") );
  7103. }
  7104. //
  7105. // This routine is intended only for RESTART.
  7106. //
  7107. VOID
  7108. NtfsRestartChangeValue (
  7109. IN PIRP_CONTEXT IrpContext,
  7110. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  7111. IN ULONG RecordOffset,
  7112. IN ULONG AttributeOffset,
  7113. IN PVOID Data,
  7114. IN ULONG Length,
  7115. IN BOOLEAN SetNewLength
  7116. )
  7117. /*++
  7118. Routine Description:
  7119. This routine performs a simple change of an attribute value in a
  7120. file record, without worrying about Bcbs or logging.
  7121. Arguments:
  7122. FileRecord - File record in which the attribute is to be changed.
  7123. RecordOffset - ByteOffset within the file record at which the attribute starts.
  7124. AttributeOffset - Offset within the attribute record at which data is to
  7125. be changed.
  7126. Data - Pointer to the new data.
  7127. Length - Length of the new data.
  7128. SetNewLength - TRUE if the attribute length should be changed.
  7129. Return Value:
  7130. None
  7131. --*/
  7132. {
  7133. PATTRIBUTE_RECORD_HEADER Attribute;
  7134. BOOLEAN AlreadyMoved = FALSE;
  7135. BOOLEAN DataInFileRecord = FALSE;
  7136. ASSERT_IRP_CONTEXT( IrpContext );
  7137. PAGED_CODE();
  7138. DebugTrace( +1, Dbg, ("NtfsRestartChangeValue\n") );
  7139. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  7140. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  7141. DebugTrace( 0, Dbg, ("AttributeOffset = %08lx\n", AttributeOffset) );
  7142. DebugTrace( 0, Dbg, ("Data = %08lx\n", Data) );
  7143. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  7144. DebugTrace( 0, Dbg, ("SetNewLength = %02lx\n", SetNewLength) );
  7145. //
  7146. // Calculate the address of the attribute being changed.
  7147. //
  7148. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord + RecordOffset);
  7149. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  7150. ASSERT( IsQuadAligned( RecordOffset ) );
  7151. //
  7152. // First, if we are setting a new length, then move the data after the
  7153. // attribute record and change FirstFreeByte accordingly.
  7154. //
  7155. if (SetNewLength) {
  7156. ULONG NewLength = QuadAlign( AttributeOffset + Length );
  7157. //
  7158. // If we are shrinking the attribute, we need to move the data
  7159. // first to support caller's who are shifting data down in the
  7160. // attribute value, like DeleteFromAttributeList. If we were
  7161. // to shrink the record first in this case, we would clobber some
  7162. // of the data to be moved down.
  7163. //
  7164. if (NewLength < Attribute->RecordLength) {
  7165. //
  7166. // Now move the new data in and remember we moved it.
  7167. //
  7168. AlreadyMoved = TRUE;
  7169. //
  7170. // If there is data to modify do so now.
  7171. //
  7172. if (Length != 0) {
  7173. if (ARGUMENT_PRESENT(Data)) {
  7174. RtlMoveMemory( (PCHAR)Attribute + AttributeOffset, Data, Length );
  7175. } else {
  7176. RtlZeroMemory( (PCHAR)Attribute + AttributeOffset, Length );
  7177. }
  7178. }
  7179. }
  7180. //
  7181. // First move the tail of the file record to make/eliminate room.
  7182. //
  7183. RtlMoveMemory( Add2Ptr( Attribute, NewLength ),
  7184. Add2Ptr( Attribute, Attribute->RecordLength ),
  7185. FileRecord->FirstFreeByte - RecordOffset - Attribute->RecordLength );
  7186. //
  7187. // Now update fields to reflect the change.
  7188. //
  7189. FileRecord->FirstFreeByte += (NewLength - Attribute->RecordLength);
  7190. Attribute->RecordLength = NewLength;
  7191. Attribute->Form.Resident.ValueLength =
  7192. (USHORT)(AttributeOffset + Length -
  7193. (ULONG)Attribute->Form.Resident.ValueOffset);
  7194. }
  7195. //
  7196. // Now move the new data in.
  7197. //
  7198. if (!AlreadyMoved) {
  7199. if (ARGUMENT_PRESENT(Data)) {
  7200. RtlMoveMemory( Add2Ptr( Attribute, AttributeOffset ),
  7201. Data,
  7202. Length );
  7203. } else {
  7204. RtlZeroMemory( Add2Ptr( Attribute, AttributeOffset ),
  7205. Length );
  7206. }
  7207. }
  7208. DebugTrace( -1, Dbg, ("NtfsRestartChangeValue -> VOID\n") );
  7209. return;
  7210. }
  7211. //
  7212. // This routine is intended only for RESTART.
  7213. //
  7214. VOID
  7215. NtfsRestartChangeMapping (
  7216. IN PIRP_CONTEXT IrpContext,
  7217. IN PVCB Vcb,
  7218. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  7219. IN ULONG RecordOffset,
  7220. IN ULONG AttributeOffset,
  7221. IN PVOID Data,
  7222. IN ULONG Length
  7223. )
  7224. /*++
  7225. Routine Description:
  7226. This routine performs a simple change of an attribute's mapping pairs in a
  7227. file record, without worrying about Bcbs or logging.
  7228. Arguments:
  7229. Vcb - Vcb for volume
  7230. FileRecord - File record in which the attribute is to be changed.
  7231. RecordOffset - ByteOffset within the file record at which the attribute starts.
  7232. AttributeOffset - Offset within the attribute record at which mapping is to
  7233. be changed.
  7234. Data - Pointer to the new mapping.
  7235. Length - Length of the new mapping.
  7236. Return Value:
  7237. None
  7238. --*/
  7239. {
  7240. PATTRIBUTE_RECORD_HEADER Attribute;
  7241. VCN HighestVcn;
  7242. PCHAR MappingPairs;
  7243. ULONG NewLength = QuadAlign( AttributeOffset + Length );
  7244. ASSERT_IRP_CONTEXT( IrpContext );
  7245. UNREFERENCED_PARAMETER( Vcb );
  7246. PAGED_CODE();
  7247. DebugTrace( +1, Dbg, ("NtfsRestartChangeMapping\n") );
  7248. DebugTrace( 0, Dbg, ("FileRecord = %08lx\n", FileRecord) );
  7249. DebugTrace( 0, Dbg, ("RecordOffset = %08lx\n", RecordOffset) );
  7250. DebugTrace( 0, Dbg, ("AttributeOffset = %08lx\n", AttributeOffset) );
  7251. DebugTrace( 0, Dbg, ("Data = %08lx\n", Data) );
  7252. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  7253. //
  7254. // Calculate the address of the attribute being changed.
  7255. //
  7256. Attribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)FileRecord + RecordOffset);
  7257. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  7258. ASSERT( IsQuadAligned( RecordOffset ) );
  7259. //
  7260. // First, if we are setting a new length, then move the data after the
  7261. // attribute record and change FirstFreeByte accordingly.
  7262. //
  7263. //
  7264. // First move the tail of the file record to make/eliminate room.
  7265. //
  7266. RtlMoveMemory( (PCHAR)Attribute + NewLength,
  7267. (PCHAR)Attribute + Attribute->RecordLength,
  7268. FileRecord->FirstFreeByte - RecordOffset -
  7269. Attribute->RecordLength );
  7270. //
  7271. // Now update fields to reflect the change.
  7272. //
  7273. FileRecord->FirstFreeByte += NewLength -
  7274. Attribute->RecordLength;
  7275. Attribute->RecordLength = NewLength;
  7276. //
  7277. // Now move the new data in.
  7278. //
  7279. RtlCopyMemory( (PCHAR)Attribute + AttributeOffset, Data, Length );
  7280. //
  7281. // Finally update HighestVcn and (optionally) AllocatedLength fields.
  7282. //
  7283. MappingPairs = (PCHAR)Attribute + (ULONG)Attribute->Form.Nonresident.MappingPairsOffset;
  7284. HighestVcn = NtfsGetHighestVcn( IrpContext,
  7285. Attribute->Form.Nonresident.LowestVcn,
  7286. (PCHAR)Attribute + (ULONG)Attribute->RecordLength,
  7287. MappingPairs );
  7288. ASSERT( IsCharZero( *MappingPairs ) || HighestVcn != -1 );
  7289. Attribute->Form.Nonresident.HighestVcn = HighestVcn;
  7290. DebugTrace( -1, Dbg, ("NtfsRestartChangeMapping -> VOID\n") );
  7291. return;
  7292. }
  7293. VOID
  7294. NtfsAddToAttributeList (
  7295. IN PIRP_CONTEXT IrpContext,
  7296. IN PFCB Fcb,
  7297. IN MFT_SEGMENT_REFERENCE SegmentReference,
  7298. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  7299. )
  7300. /*++
  7301. Routine Description:
  7302. This routine adds an attribute list entry for a newly inserted attribute.
  7303. It is assumed that the context variable is pointing to the attribute
  7304. record in the file record where it has been inserted, and also to the place
  7305. in the attribute list where the new attribute list entry is to be inserted.
  7306. Arguments:
  7307. Fcb - Requested file.
  7308. SegmentReference - Segment reference of the file record the new attribute
  7309. is in.
  7310. Context - Describes the current attribute.
  7311. Return Value:
  7312. None
  7313. --*/
  7314. {
  7315. //
  7316. // Allocate an attribute list entry which hopefully has enough space
  7317. // for the name.
  7318. //
  7319. struct {
  7320. ATTRIBUTE_LIST_ENTRY EntryBuffer;
  7321. WCHAR Name[10];
  7322. } NewEntry;
  7323. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  7324. ULONG EntrySize;
  7325. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  7326. PATTRIBUTE_RECORD_HEADER Attribute;
  7327. PATTRIBUTE_LIST_ENTRY ListEntry = &NewEntry.EntryBuffer;
  7328. BOOLEAN SetNewLength = TRUE;
  7329. ULONG EntryOffset;
  7330. ULONG BeyondEntryOffset;
  7331. PAGED_CODE();
  7332. //
  7333. // First construct the attribute list entry.
  7334. //
  7335. FileRecord = NtfsContainingFileRecord( Context );
  7336. Attribute = NtfsFoundAttribute( Context );
  7337. EntrySize = QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  7338. + ((ULONG) Attribute->NameLength << 1));
  7339. //
  7340. // Allocate the list entry if the one we have is not big enough.
  7341. //
  7342. if (EntrySize > sizeof(NewEntry)) {
  7343. ListEntry = (PATTRIBUTE_LIST_ENTRY)NtfsAllocatePool( NonPagedPool,
  7344. EntrySize );
  7345. }
  7346. RtlZeroMemory( ListEntry, EntrySize );
  7347. NtfsInitializeAttributeContext( &ListContext );
  7348. //
  7349. // Use try-finally to insure cleanup.
  7350. //
  7351. try {
  7352. ULONG OldQuadAttrListSize;
  7353. PATTRIBUTE_RECORD_HEADER ListAttribute;
  7354. PFILE_RECORD_SEGMENT_HEADER ListFileRecord;
  7355. //
  7356. // Now fill in the list entry.
  7357. //
  7358. ListEntry->AttributeTypeCode = Attribute->TypeCode;
  7359. ListEntry->RecordLength = (USHORT)EntrySize;
  7360. ListEntry->AttributeNameLength = Attribute->NameLength;
  7361. ListEntry->Instance = Attribute->Instance;
  7362. ListEntry->AttributeNameOffset =
  7363. (UCHAR)PtrOffset( ListEntry, &ListEntry->AttributeName[0] );
  7364. if (Attribute->FormCode == NONRESIDENT_FORM) {
  7365. ListEntry->LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  7366. }
  7367. ASSERT( (Fcb != Fcb->Vcb->MftScb->Fcb) ||
  7368. (Attribute->TypeCode != $DATA) ||
  7369. ((ULONGLONG)(ListEntry->LowestVcn) > (NtfsFullSegmentNumber( &SegmentReference ) >> Fcb->Vcb->MftToClusterShift)) );
  7370. ListEntry->SegmentReference = SegmentReference;
  7371. if (Attribute->NameLength != 0) {
  7372. RtlCopyMemory( &ListEntry->AttributeName[0],
  7373. Add2Ptr(Attribute, Attribute->NameOffset),
  7374. Attribute->NameLength << 1 );
  7375. }
  7376. //
  7377. // Lookup the list context so that we can modify the attribute list.
  7378. //
  7379. if (!NtfsLookupAttributeByCode( IrpContext,
  7380. Fcb,
  7381. &Fcb->FileReference,
  7382. $ATTRIBUTE_LIST,
  7383. &ListContext )) {
  7384. ASSERT( FALSE );
  7385. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  7386. }
  7387. ListAttribute = NtfsFoundAttribute( &ListContext );
  7388. ListFileRecord = NtfsContainingFileRecord( &ListContext );
  7389. OldQuadAttrListSize = ListAttribute->RecordLength;
  7390. //
  7391. // Remember the relative offsets of list entries.
  7392. //
  7393. EntryOffset = (ULONG) PtrOffset( Context->AttributeList.FirstEntry,
  7394. Context->AttributeList.Entry );
  7395. BeyondEntryOffset = (ULONG) PtrOffset( Context->AttributeList.FirstEntry,
  7396. Context->AttributeList.BeyondFinalEntry );
  7397. //
  7398. // If this operation is possibly going to make the attribute list go
  7399. // non-resident, or else move other attributes around, then we will
  7400. // reserve the space first in the attribute list and then map the
  7401. // value. Note that some of the entries we need to shift up may
  7402. // be modified as a side effect of making space!
  7403. //
  7404. if (NtfsIsAttributeResident( ListAttribute ) &&
  7405. (ListFileRecord->BytesAvailable - ListFileRecord->FirstFreeByte) < EntrySize) {
  7406. ULONG Length;
  7407. //
  7408. // Add enough zeros to the end of the attribute to accommodate
  7409. // the new attribute list entry.
  7410. //
  7411. NtfsChangeAttributeValue( IrpContext,
  7412. Fcb,
  7413. BeyondEntryOffset,
  7414. NULL,
  7415. EntrySize,
  7416. TRUE,
  7417. TRUE,
  7418. FALSE,
  7419. TRUE,
  7420. &ListContext );
  7421. //
  7422. // We now don't have to set the new length.
  7423. //
  7424. SetNewLength = FALSE;
  7425. //
  7426. // In case the attribute list went non-resident on this call, then we
  7427. // need to update both list entry pointers in the found attribute.
  7428. // (We do this "just in case" all the time to avoid a rare code path.)
  7429. //
  7430. //
  7431. // Map the non-resident attribute list.
  7432. //
  7433. NtfsMapAttributeValue( IrpContext,
  7434. Fcb,
  7435. (PVOID *) &Context->AttributeList.FirstEntry,
  7436. &Length,
  7437. &Context->AttributeList.NonresidentListBcb,
  7438. &ListContext );
  7439. //
  7440. // If the list is still resident then unpin the current Bcb in
  7441. // the original context to keep our pin counts in sync.
  7442. //
  7443. if (Context->AttributeList.Bcb == Context->AttributeList.NonresidentListBcb) {
  7444. NtfsUnpinBcb( IrpContext, &Context->AttributeList.NonresidentListBcb );
  7445. }
  7446. Context->AttributeList.Entry = Add2Ptr( Context->AttributeList.FirstEntry,
  7447. EntryOffset );
  7448. Context->AttributeList.BeyondFinalEntry = Add2Ptr( Context->AttributeList.FirstEntry,
  7449. BeyondEntryOffset );
  7450. }
  7451. //
  7452. // Check for adding duplicate entries...
  7453. //
  7454. ASSERT(
  7455. // Not enough room for previous entry to = inserted entry
  7456. ((EntryOffset < EntrySize) ||
  7457. // Previous entry doesn't equal inserted entry
  7458. (!RtlEqualMemory((PVOID)((PCHAR)Context->AttributeList.Entry - EntrySize),
  7459. ListEntry,
  7460. EntrySize)))
  7461. &&
  7462. // At end of attribute list
  7463. ((BeyondEntryOffset == EntryOffset) ||
  7464. // This entry doesn't equal inserted entry
  7465. (!RtlEqualMemory(Context->AttributeList.Entry,
  7466. ListEntry,
  7467. EntrySize))) );
  7468. //
  7469. // Now shift the old contents up to make room for our new entry. We don't let
  7470. // the attribute list grow larger than a cache view however.
  7471. //
  7472. if (EntrySize + BeyondEntryOffset > VACB_MAPPING_GRANULARITY) {
  7473. NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
  7474. }
  7475. NtfsChangeAttributeValue( IrpContext,
  7476. Fcb,
  7477. EntryOffset + EntrySize,
  7478. Context->AttributeList.Entry,
  7479. BeyondEntryOffset - EntryOffset,
  7480. SetNewLength,
  7481. TRUE,
  7482. FALSE,
  7483. TRUE,
  7484. &ListContext );
  7485. //
  7486. // Now write in the new entry.
  7487. //
  7488. NtfsChangeAttributeValue( IrpContext,
  7489. Fcb,
  7490. EntryOffset,
  7491. (PVOID)ListEntry,
  7492. EntrySize,
  7493. FALSE,
  7494. TRUE,
  7495. FALSE,
  7496. FALSE,
  7497. &ListContext );
  7498. //
  7499. // Reload the attribute list values from the list context.
  7500. //
  7501. ListAttribute = NtfsFoundAttribute( &ListContext );
  7502. //
  7503. // Now fix up the context for return
  7504. //
  7505. if (*(PLONGLONG)&FileRecord->BaseFileRecordSegment == 0) {
  7506. //
  7507. // We need to update the attribute pointer for the target attribute
  7508. // by the amount of the change in the attribute list attribute.
  7509. //
  7510. Context->FoundAttribute.Attribute =
  7511. Add2Ptr( Context->FoundAttribute.Attribute,
  7512. ListAttribute->RecordLength - OldQuadAttrListSize );
  7513. }
  7514. Context->AttributeList.BeyondFinalEntry =
  7515. Add2Ptr( Context->AttributeList.BeyondFinalEntry, EntrySize );
  7516. #if DBG
  7517. {
  7518. PATTRIBUTE_LIST_ENTRY LastEntry, Entry;
  7519. for (LastEntry = Context->AttributeList.FirstEntry, Entry = NtfsGetNextRecord(LastEntry);
  7520. Entry < Context->AttributeList.BeyondFinalEntry;
  7521. LastEntry = Entry, Entry = NtfsGetNextRecord(LastEntry)) {
  7522. ASSERT( (LastEntry->RecordLength != Entry->RecordLength) ||
  7523. (!RtlEqualMemory(LastEntry, Entry, Entry->RecordLength)) );
  7524. }
  7525. }
  7526. #endif
  7527. } finally {
  7528. //
  7529. // If we had to allocate a list entry buffer, deallocate it.
  7530. //
  7531. if (ListEntry != &NewEntry.EntryBuffer) {
  7532. NtfsFreePool(ListEntry);
  7533. }
  7534. //
  7535. // Cleanup the enumeration context for the list entry.
  7536. //
  7537. NtfsCleanupAttributeContext( IrpContext, &ListContext);
  7538. }
  7539. }
  7540. VOID
  7541. NtfsDeleteFromAttributeList (
  7542. IN PIRP_CONTEXT IrpContext,
  7543. IN PFCB Fcb,
  7544. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  7545. )
  7546. /*++
  7547. Routine Description:
  7548. This routine deletes an attribute list entry for a recently deleted attribute.
  7549. It is assumed that the context variable is pointing to the place in
  7550. the attribute list where the attribute list entry is to be deleted.
  7551. Arguments:
  7552. Fcb - Requested file.
  7553. Context - Describes the current attribute.
  7554. Return Value:
  7555. None
  7556. --*/
  7557. {
  7558. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  7559. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  7560. PATTRIBUTE_LIST_ENTRY ListEntry, NextListEntry;
  7561. ULONG EntrySize;
  7562. ULONG SavedListSize;
  7563. PAGED_CODE();
  7564. FileRecord = NtfsContainingFileRecord( Context );
  7565. //
  7566. // Lookup the list context so that we can modify the attribute list.
  7567. //
  7568. NtfsInitializeAttributeContext( &ListContext );
  7569. if (!NtfsLookupAttributeByCode( IrpContext,
  7570. Fcb,
  7571. &Fcb->FileReference,
  7572. $ATTRIBUTE_LIST,
  7573. &ListContext )) {
  7574. ASSERT( FALSE );
  7575. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  7576. }
  7577. //
  7578. // Use try-finally to insure cleanup.
  7579. //
  7580. try {
  7581. SavedListSize = NtfsFoundAttribute(&ListContext)->RecordLength;
  7582. //
  7583. // Now shift the old contents down to make room for our new entry.
  7584. //
  7585. ListEntry = Context->AttributeList.Entry;
  7586. EntrySize = ListEntry->RecordLength;
  7587. NextListEntry = Add2Ptr(ListEntry, EntrySize);
  7588. NtfsChangeAttributeValue( IrpContext,
  7589. Fcb,
  7590. PtrOffset( Context->AttributeList.FirstEntry,
  7591. Context->AttributeList.Entry ),
  7592. NextListEntry,
  7593. PtrOffset( NextListEntry,
  7594. Context->AttributeList.BeyondFinalEntry ),
  7595. TRUE,
  7596. TRUE,
  7597. FALSE,
  7598. TRUE,
  7599. &ListContext );
  7600. //
  7601. // Now fix up the context for return
  7602. //
  7603. if (*(PLONGLONG)&FileRecord->BaseFileRecordSegment == 0) {
  7604. SavedListSize -= NtfsFoundAttribute(&ListContext)->RecordLength;
  7605. Context->FoundAttribute.Attribute =
  7606. Add2Ptr( Context->FoundAttribute.Attribute, -(LONG)SavedListSize );
  7607. }
  7608. Context->AttributeList.BeyondFinalEntry =
  7609. Add2Ptr( Context->AttributeList.BeyondFinalEntry, -(LONG)EntrySize );
  7610. } finally {
  7611. //
  7612. // Cleanup the enumeration context for the list entry.
  7613. //
  7614. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  7615. }
  7616. }
  7617. BOOLEAN
  7618. NtfsRewriteMftMapping (
  7619. IN PIRP_CONTEXT IrpContext,
  7620. IN PVCB Vcb
  7621. )
  7622. /*++
  7623. Routine Description:
  7624. This routine is called to rewrite the mapping for the Mft file. This is done
  7625. in the case where either hot-fixing or Mft defragging has caused us to spill
  7626. into the reserved area of a file record. This routine will rewrite the
  7627. mapping from the beginning, using the reserved record if necessary. On return
  7628. it will indicate whether any work was done and if there is more work to do.
  7629. Arguments:
  7630. Vcb - This is the Vcb for the volume to defrag.
  7631. ExcessMapping - Address to store whether there is still excess mapping in
  7632. the file.
  7633. Return Value:
  7634. BOOLEAN - TRUE if we made any changes to the file. FALSE if we found no
  7635. work to do.
  7636. --*/
  7637. {
  7638. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  7639. PUCHAR MappingPairs = NULL;
  7640. PBCB FileRecordBcb = NULL;
  7641. BOOLEAN MadeChanges = FALSE;
  7642. BOOLEAN ExcessMapping = FALSE;
  7643. BOOLEAN LastFileRecord = FALSE;
  7644. BOOLEAN SkipLookup = FALSE;
  7645. PAGED_CODE();
  7646. NtfsInitializeAttributeContext( &AttrContext );
  7647. //
  7648. // Use a try-finally to facilitate cleanup.
  7649. //
  7650. try {
  7651. VCN CurrentVcn; // Starting Vcn for the next file record
  7652. VCN MinimumVcn; // This Vcn must be in the current mapping
  7653. VCN LastVcn; // Last Vcn in the current mapping
  7654. VCN LastMftVcn; // Last Vcn in the file
  7655. VCN NextVcn; // First Vcn past the end of the mapping
  7656. ULONG ReservedIndex; // Reserved index in Mft
  7657. ULONG NextIndex; // Next file record available for Mft mapping
  7658. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  7659. MFT_SEGMENT_REFERENCE FileRecordReference;
  7660. ULONG RecordOffset;
  7661. PATTRIBUTE_RECORD_HEADER Attribute;
  7662. ULONG AttributeOffset;
  7663. ULONG MappingSizeAvailable;
  7664. ULONG MappingPairsSize;
  7665. //
  7666. // Find the initial file record for the Mft.
  7667. //
  7668. NtfsLookupAttributeForScb( IrpContext, Vcb->MftScb, NULL, &AttrContext );
  7669. //
  7670. // Compute some initial values. If this is the only file record
  7671. // for the file then we are done.
  7672. //
  7673. ReservedIndex = Vcb->MftScb->ScbType.Mft.ReservedIndex;
  7674. Attribute = NtfsFoundAttribute( &AttrContext );
  7675. LastMftVcn = Int64ShraMod32(Vcb->MftScb->Header.AllocationSize.QuadPart, Vcb->ClusterShift) - 1;
  7676. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  7677. if (CurrentVcn >= LastMftVcn) {
  7678. try_return( NOTHING );
  7679. }
  7680. //
  7681. // Loop while there are more file records. We will insert any
  7682. // additional file records needed within the loop so that this
  7683. // call should succeed until the remapping is done.
  7684. //
  7685. while (SkipLookup ||
  7686. NtfsLookupNextAttributeForScb( IrpContext,
  7687. Vcb->MftScb,
  7688. &AttrContext )) {
  7689. BOOLEAN ReplaceFileRecord;
  7690. BOOLEAN ReplaceAttributeListEntry;
  7691. ReplaceAttributeListEntry = FALSE;
  7692. //
  7693. // If we just looked up this entry then pin the current
  7694. // attribute.
  7695. //
  7696. if (!SkipLookup) {
  7697. //
  7698. // Always pin the current attribute.
  7699. //
  7700. NtfsPinMappedAttribute( IrpContext,
  7701. Vcb,
  7702. &AttrContext );
  7703. }
  7704. //
  7705. // Extract some pointers from the current file record.
  7706. // Remember if this was the last record.
  7707. //
  7708. ReplaceFileRecord = FALSE;
  7709. FileRecord = NtfsContainingFileRecord( &AttrContext );
  7710. FileRecordReference = AttrContext.AttributeList.Entry->SegmentReference;
  7711. Attribute = NtfsFoundAttribute( &AttrContext );
  7712. AttributeOffset = Attribute->Form.Nonresident.MappingPairsOffset;
  7713. RecordOffset = PtrOffset( FileRecord, Attribute );
  7714. //
  7715. // Remember if we are at the last attribute.
  7716. //
  7717. if (Attribute->Form.Nonresident.HighestVcn == LastMftVcn) {
  7718. LastFileRecord = TRUE;
  7719. }
  7720. //
  7721. // If we have already remapped this entire file record then
  7722. // remove the attribute and it list entry.
  7723. //
  7724. if (!SkipLookup &&
  7725. (CurrentVcn > LastMftVcn)) {
  7726. PATTRIBUTE_LIST_ENTRY ListEntry;
  7727. ULONG Count;
  7728. Count = 0;
  7729. //
  7730. // We want to remove this entry and all subsequent entries.
  7731. //
  7732. ListEntry = AttrContext.AttributeList.Entry;
  7733. while ((ListEntry != AttrContext.AttributeList.BeyondFinalEntry) &&
  7734. (ListEntry->AttributeTypeCode == $DATA) &&
  7735. (ListEntry->AttributeNameLength == 0)) {
  7736. Count += 1;
  7737. NtfsDeallocateMftRecord( IrpContext,
  7738. Vcb,
  7739. NtfsUnsafeSegmentNumber( &ListEntry->SegmentReference ) );
  7740. NtfsDeleteFromAttributeList( IrpContext,
  7741. Vcb->MftScb->Fcb,
  7742. &AttrContext );
  7743. ListEntry = AttrContext.AttributeList.Entry;
  7744. }
  7745. //
  7746. // Clear out the reserved index in case one of these
  7747. // will do.
  7748. //
  7749. NtfsAcquireCheckpoint( IrpContext, Vcb );
  7750. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MFT_REC_RESERVED );
  7751. ClearFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED );
  7752. NtfsReleaseCheckpoint( IrpContext, Vcb );
  7753. Vcb->MftScb->ScbType.Mft.ReservedIndex = 0;
  7754. try_return( NOTHING );
  7755. }
  7756. //
  7757. // Check if we are going to replace this file record with
  7758. // the reserved record.
  7759. //
  7760. if (ReservedIndex < NtfsSegmentNumber( &FileRecordReference )) {
  7761. PATTRIBUTE_RECORD_HEADER NewAttribute;
  7762. PATTRIBUTE_TYPE_CODE NewEnd;
  7763. //
  7764. // Remember this index for our computation for the Minimum mapped
  7765. // Vcn.
  7766. //
  7767. NextIndex = NtfsUnsafeSegmentNumber( &FileRecordReference );
  7768. FileRecord = NtfsCloneFileRecord( IrpContext,
  7769. Vcb->MftScb->Fcb,
  7770. TRUE,
  7771. &FileRecordBcb,
  7772. &FileRecordReference );
  7773. ReservedIndex = MAXULONG;
  7774. //
  7775. // Now lets create an attribute in the new file record.
  7776. //
  7777. NewAttribute = Add2Ptr( FileRecord,
  7778. FileRecord->FirstFreeByte
  7779. - QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE )));
  7780. NewAttribute->TypeCode = Attribute->TypeCode;
  7781. NewAttribute->RecordLength = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  7782. NewAttribute->FormCode = NONRESIDENT_FORM;
  7783. NewAttribute->Flags = Attribute->Flags;
  7784. NewAttribute->Instance = FileRecord->NextAttributeInstance++;
  7785. NewAttribute->Form.Nonresident.LowestVcn = CurrentVcn;
  7786. NewAttribute->Form.Nonresident.HighestVcn = 0;
  7787. NewAttribute->Form.Nonresident.MappingPairsOffset = (USHORT) NewAttribute->RecordLength;
  7788. NewEnd = Add2Ptr( NewAttribute, NewAttribute->RecordLength );
  7789. *NewEnd = $END;
  7790. //
  7791. // Now fix up the file record with this new data.
  7792. //
  7793. FileRecord->FirstFreeByte = PtrOffset( FileRecord, NewEnd )
  7794. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  7795. FileRecord->SequenceNumber += 1;
  7796. if (FileRecord->SequenceNumber == 0) {
  7797. FileRecord->SequenceNumber = 1;
  7798. }
  7799. FileRecordReference.SequenceNumber = FileRecord->SequenceNumber;
  7800. //
  7801. // Now switch this new file record into the attribute context.
  7802. //
  7803. NtfsUnpinBcb( IrpContext, &NtfsFoundBcb( &AttrContext ));
  7804. NtfsFoundBcb( &AttrContext ) = FileRecordBcb;
  7805. AttrContext.FoundAttribute.MftFileOffset = LlBytesFromFileRecords( Vcb, NextIndex );
  7806. AttrContext.FoundAttribute.Attribute = NewAttribute;
  7807. AttrContext.FoundAttribute.FileRecord = FileRecord;
  7808. FileRecordBcb = NULL;
  7809. //
  7810. // Now add an attribute list entry for this entry.
  7811. //
  7812. NtfsAddToAttributeList( IrpContext,
  7813. Vcb->MftScb->Fcb,
  7814. FileRecordReference,
  7815. &AttrContext );
  7816. //
  7817. // Reload our pointers for this file record.
  7818. //
  7819. Attribute = NewAttribute;
  7820. AttributeOffset = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  7821. RecordOffset = PtrOffset( FileRecord, Attribute );
  7822. //
  7823. // We must include either the last Vcn of the file or
  7824. // the Vcn for the next file record to use for the Mft.
  7825. // At this point MinimumVcn is the first Vcn that doesn't
  7826. // have to be in the current mapping.
  7827. //
  7828. if (Vcb->FileRecordsPerCluster == 0) {
  7829. MinimumVcn = (NextIndex + 1) << Vcb->MftToClusterShift;
  7830. } else {
  7831. MinimumVcn = (NextIndex + Vcb->FileRecordsPerCluster - 1) << Vcb->MftToClusterShift;
  7832. }
  7833. ReplaceFileRecord = TRUE;
  7834. //
  7835. // We will be using the current attribute.
  7836. //
  7837. } else {
  7838. //
  7839. // The mapping we write into this page must go
  7840. // to the current end of the page or to the reserved
  7841. // or spare file record, whichever is earlier.
  7842. // If we are adding the reserved record to the end then
  7843. // we know the final Vcn already.
  7844. //
  7845. if (SkipLookup) {
  7846. NextVcn = LastMftVcn;
  7847. } else {
  7848. NextVcn = Attribute->Form.Nonresident.HighestVcn;
  7849. }
  7850. if (Vcb->FileRecordsPerCluster == 0) {
  7851. NextIndex = (ULONG)Int64ShraMod32((NextVcn + 1), Vcb->MftToClusterShift);
  7852. } else {
  7853. NextIndex = (ULONG)Int64ShllMod32((NextVcn + 1), Vcb->MftToClusterShift);
  7854. }
  7855. if (ReservedIndex < NextIndex) {
  7856. NextIndex = ReservedIndex + 1;
  7857. ReplaceFileRecord = TRUE;
  7858. }
  7859. //
  7860. // If we can use this file record unchanged then continue on.
  7861. // Start by checking that it starts on the same Vcn boundary.
  7862. //
  7863. if (!SkipLookup) {
  7864. //
  7865. // If it starts on the same boundary then we check if we
  7866. // can do any work with this.
  7867. //
  7868. if (CurrentVcn == Attribute->Form.Nonresident.LowestVcn) {
  7869. ULONG RemainingFileRecordBytes;
  7870. RemainingFileRecordBytes = FileRecord->BytesAvailable - FileRecord->FirstFreeByte;
  7871. //
  7872. // Check if we have less than the desired cushion
  7873. // left.
  7874. //
  7875. if (RemainingFileRecordBytes < Vcb->MftCushion) {
  7876. //
  7877. // If we have no more file records there is no
  7878. // remapping we can do.
  7879. //
  7880. if (!ReplaceFileRecord) {
  7881. //
  7882. // Remember if we used part of the reserved
  7883. // portion of the file record.
  7884. //
  7885. if (RemainingFileRecordBytes < Vcb->MftReserved) {
  7886. ExcessMapping = TRUE;
  7887. }
  7888. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  7889. continue;
  7890. }
  7891. //
  7892. // We have more than our cushion left. If this
  7893. // is the last file record we will skip this.
  7894. //
  7895. } else if (Attribute->Form.Nonresident.HighestVcn == LastMftVcn) {
  7896. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  7897. continue;
  7898. }
  7899. //
  7900. // If it doesn't start on the same boundary then we have to
  7901. // delete and reinsert the attribute list entry.
  7902. //
  7903. } else {
  7904. ReplaceAttributeListEntry = TRUE;
  7905. }
  7906. }
  7907. ReplaceFileRecord = FALSE;
  7908. //
  7909. // Log the beginning state of this file record.
  7910. //
  7911. NtfsLogMftFileRecord( IrpContext,
  7912. Vcb,
  7913. FileRecord,
  7914. LlBytesFromFileRecords( Vcb, NtfsSegmentNumber( &FileRecordReference ) ),
  7915. NtfsFoundBcb( &AttrContext ),
  7916. FALSE );
  7917. //
  7918. // Compute the Vcn for the file record past the one we will use
  7919. // next. At this point this is the first Vcn that doesn't have
  7920. // to be in the current mapping.
  7921. //
  7922. if (Vcb->FileRecordsPerCluster == 0) {
  7923. MinimumVcn = NextIndex << Vcb->MftToClusterShift;
  7924. } else {
  7925. MinimumVcn = (NextIndex + Vcb->FileRecordsPerCluster - 1) << Vcb->MftToClusterShift;
  7926. }
  7927. }
  7928. //
  7929. // Move back one vcn to adhere to the mapping pairs interface.
  7930. // This is now the last Vcn which MUST appear in the current
  7931. // mapping.
  7932. //
  7933. MinimumVcn = MinimumVcn - 1;
  7934. //
  7935. // Get the available size for the mapping pairs. We won't
  7936. // include the cushion here.
  7937. //
  7938. MappingSizeAvailable = FileRecord->BytesAvailable + Attribute->RecordLength - FileRecord->FirstFreeByte - SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  7939. //
  7940. // We know the range of Vcn's the mapping must cover.
  7941. // Compute the mapping pair size. If they won't fit and
  7942. // leave our desired cushion then use whatever space is
  7943. // needed. The NextVcn value is the first Vcn (or xxMax)
  7944. // for the run after the last run in the current mapping.
  7945. //
  7946. MappingPairsSize = NtfsGetSizeForMappingPairs( &Vcb->MftScb->Mcb,
  7947. MappingSizeAvailable - Vcb->MftCushion,
  7948. CurrentVcn,
  7949. NULL,
  7950. &NextVcn );
  7951. //
  7952. // If this mapping doesn't include the file record we will
  7953. // be using next then extend the mapping to include it.
  7954. //
  7955. if (NextVcn <= MinimumVcn) {
  7956. //
  7957. // Compute the mapping pairs again. This must fit
  7958. // since it already fits.
  7959. //
  7960. MappingPairsSize = NtfsGetSizeForMappingPairs( &Vcb->MftScb->Mcb,
  7961. MappingSizeAvailable,
  7962. CurrentVcn,
  7963. &MinimumVcn,
  7964. &NextVcn );
  7965. //
  7966. // Remember if we still have excess mapping.
  7967. //
  7968. if (MappingSizeAvailable - MappingPairsSize < Vcb->MftReserved) {
  7969. ExcessMapping = TRUE;
  7970. }
  7971. }
  7972. //
  7973. // Remember the last Vcn for the current run. If the NextVcn
  7974. // is xxMax then we are at the end of the file.
  7975. //
  7976. if (NextVcn == MAXLONGLONG) {
  7977. LastVcn = LastMftVcn;
  7978. //
  7979. // Otherwise it is one less than the next vcn value.
  7980. //
  7981. } else {
  7982. LastVcn = NextVcn - 1;
  7983. }
  7984. //
  7985. // Check if we have to rewrite this attribute. We will write the
  7986. // new mapping if any of the following are true.
  7987. //
  7988. // We are replacing a file record
  7989. // The attribute's LowestVcn doesn't match
  7990. // The attributes's HighestVcn doesn't match.
  7991. //
  7992. if (ReplaceFileRecord ||
  7993. (CurrentVcn != Attribute->Form.Nonresident.LowestVcn) ||
  7994. (LastVcn != Attribute->Form.Nonresident.HighestVcn )) {
  7995. Attribute->Form.Nonresident.LowestVcn = CurrentVcn;
  7996. //
  7997. // Replace the attribute list entry at this point if needed.
  7998. //
  7999. if (ReplaceAttributeListEntry) {
  8000. NtfsDeleteFromAttributeList( IrpContext,
  8001. Vcb->MftScb->Fcb,
  8002. &AttrContext );
  8003. NtfsAddToAttributeList( IrpContext,
  8004. Vcb->MftScb->Fcb,
  8005. FileRecordReference,
  8006. &AttrContext );
  8007. }
  8008. //
  8009. // Allocate a buffer for the mapping pairs if we haven't
  8010. // done so.
  8011. //
  8012. if (MappingPairs == NULL) {
  8013. MappingPairs = NtfsAllocatePool(PagedPool, NtfsMaximumAttributeSize( Vcb->BytesPerFileRecordSegment ));
  8014. }
  8015. NtfsBuildMappingPairs( &Vcb->MftScb->Mcb,
  8016. CurrentVcn,
  8017. &NextVcn,
  8018. MappingPairs );
  8019. Attribute->Form.Nonresident.HighestVcn = NextVcn;
  8020. NtfsRestartChangeMapping( IrpContext,
  8021. Vcb,
  8022. FileRecord,
  8023. RecordOffset,
  8024. AttributeOffset,
  8025. MappingPairs,
  8026. MappingPairsSize );
  8027. //
  8028. // Log the changes to this page.
  8029. //
  8030. NtfsLogMftFileRecord( IrpContext,
  8031. Vcb,
  8032. FileRecord,
  8033. LlBytesFromFileRecords( Vcb, NtfsSegmentNumber( &FileRecordReference ) ),
  8034. NtfsFoundBcb( &AttrContext ),
  8035. TRUE );
  8036. MadeChanges = TRUE;
  8037. }
  8038. //
  8039. // Move to the first Vcn of the following record.
  8040. //
  8041. CurrentVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  8042. //
  8043. // If we reached the last file record and have more mapping to do
  8044. // then use the reserved record. It must be available or we would
  8045. // have written out the entire mapping.
  8046. //
  8047. if (LastFileRecord && (CurrentVcn < LastMftVcn)) {
  8048. PATTRIBUTE_RECORD_HEADER NewAttribute;
  8049. PATTRIBUTE_TYPE_CODE NewEnd;
  8050. //
  8051. // Start by moving to the next file record. It better not be
  8052. // there or the file is corrupt. This will position us to
  8053. // insert the new record.
  8054. //
  8055. if (NtfsLookupNextAttributeForScb( IrpContext,
  8056. Vcb->MftScb,
  8057. &AttrContext )) {
  8058. NtfsAcquireCheckpoint( IrpContext, Vcb );
  8059. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
  8060. NtfsReleaseCheckpoint( IrpContext, Vcb );
  8061. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Vcb->MftScb->Fcb );
  8062. }
  8063. FileRecord = NtfsCloneFileRecord( IrpContext,
  8064. Vcb->MftScb->Fcb,
  8065. TRUE,
  8066. &FileRecordBcb,
  8067. &FileRecordReference );
  8068. ReservedIndex = MAXULONG;
  8069. //
  8070. // Now lets create an attribute in the new file record.
  8071. //
  8072. NewAttribute = Add2Ptr( FileRecord,
  8073. FileRecord->FirstFreeByte
  8074. - QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE )));
  8075. NewAttribute->TypeCode = Attribute->TypeCode;
  8076. NewAttribute->RecordLength = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  8077. NewAttribute->FormCode = NONRESIDENT_FORM;
  8078. NewAttribute->Flags = Attribute->Flags;
  8079. NewAttribute->Instance = FileRecord->NextAttributeInstance++;
  8080. NewAttribute->Form.Nonresident.LowestVcn = CurrentVcn;
  8081. NewAttribute->Form.Nonresident.HighestVcn = 0;
  8082. NewAttribute->Form.Nonresident.MappingPairsOffset = (USHORT) NewAttribute->RecordLength;
  8083. NewEnd = Add2Ptr( NewAttribute, NewAttribute->RecordLength );
  8084. *NewEnd = $END;
  8085. //
  8086. // Now fix up the file record with this new data.
  8087. //
  8088. FileRecord->FirstFreeByte = PtrOffset( FileRecord, NewEnd )
  8089. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  8090. FileRecord->SequenceNumber += 1;
  8091. if (FileRecord->SequenceNumber == 0) {
  8092. FileRecord->SequenceNumber = 1;
  8093. }
  8094. FileRecordReference.SequenceNumber = FileRecord->SequenceNumber;
  8095. //
  8096. // Now switch this new file record into the attribute context.
  8097. //
  8098. NtfsUnpinBcb( IrpContext, &NtfsFoundBcb( &AttrContext ));
  8099. NtfsFoundBcb( &AttrContext ) = FileRecordBcb;
  8100. AttrContext.FoundAttribute.MftFileOffset =
  8101. LlBytesFromFileRecords( Vcb, NtfsSegmentNumber( &FileRecordReference ) );
  8102. AttrContext.FoundAttribute.Attribute = NewAttribute;
  8103. AttrContext.FoundAttribute.FileRecord = FileRecord;
  8104. FileRecordBcb = NULL;
  8105. //
  8106. // Now add an attribute list entry for this entry.
  8107. //
  8108. NtfsAddToAttributeList( IrpContext,
  8109. Vcb->MftScb->Fcb,
  8110. FileRecordReference,
  8111. &AttrContext );
  8112. SkipLookup = TRUE;
  8113. LastFileRecord = FALSE;
  8114. } else {
  8115. SkipLookup = FALSE;
  8116. }
  8117. } // End while more file records
  8118. //
  8119. // If we didn't rewrite all of the mapping then there is some error.
  8120. //
  8121. if (CurrentVcn <= LastMftVcn) {
  8122. NtfsAcquireCheckpoint( IrpContext, Vcb );
  8123. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
  8124. NtfsReleaseCheckpoint( IrpContext, Vcb );
  8125. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Vcb->MftScb->Fcb );
  8126. }
  8127. try_exit: NOTHING;
  8128. //
  8129. // Clear the excess mapping flag if no changes were made.
  8130. //
  8131. if (!ExcessMapping) {
  8132. NtfsAcquireCheckpoint( IrpContext, Vcb );
  8133. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_EXCESS_MAP );
  8134. NtfsReleaseCheckpoint( IrpContext, Vcb );
  8135. }
  8136. } finally {
  8137. DebugUnwind( NtfsRewriteMftMapping );
  8138. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8139. NtfsUnpinBcb( IrpContext, &FileRecordBcb );
  8140. if (MappingPairs != NULL) {
  8141. NtfsFreePool( MappingPairs );
  8142. }
  8143. }
  8144. return MadeChanges;
  8145. }
  8146. VOID
  8147. NtfsSetTotalAllocatedField (
  8148. IN PIRP_CONTEXT IrpContext,
  8149. IN PSCB Scb,
  8150. IN USHORT TotalAllocatedNeeded
  8151. )
  8152. /*++
  8153. Routine Description:
  8154. This routine is called to insure that first attribute of a stream has
  8155. the correct size attribute header based on the compression state of the
  8156. file. Compressed streams will have a field for the total allocated space
  8157. in the file in the nonresident header.
  8158. This routine will see if the header is in a valid state and make space
  8159. if necessary. Then it will rewrite any of the attribute data after
  8160. the header.
  8161. Arguments:
  8162. Scb - Scb for affected stream
  8163. TotalAllocatedPresent - 0 if the TotalAllocated field not needed (this would
  8164. be an uncompressed, non-sparse file), nonzero if the TotalAllocated field
  8165. is needed.
  8166. Return Value:
  8167. None.
  8168. --*/
  8169. {
  8170. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  8171. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  8172. PATTRIBUTE_RECORD_HEADER Attribute;
  8173. PATTRIBUTE_RECORD_HEADER NewAttribute = NULL;
  8174. PUNICODE_STRING NewAttributeName = NULL;
  8175. ULONG OldHeaderSize;
  8176. ULONG NewHeaderSize;
  8177. LONG SizeChange;
  8178. PAGED_CODE();
  8179. //
  8180. // This must be a non-resident user data file.
  8181. //
  8182. if (!NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) ||
  8183. FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  8184. return;
  8185. }
  8186. NtfsInitializeAttributeContext( &AttrContext );
  8187. //
  8188. // Use a try-finally to facilitate cleanup.
  8189. //
  8190. try {
  8191. while (TRUE) {
  8192. //
  8193. // Find the current and the new size for the attribute.
  8194. //
  8195. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  8196. FileRecord = NtfsContainingFileRecord( &AttrContext );
  8197. Attribute = NtfsFoundAttribute( &AttrContext );
  8198. OldHeaderSize = Attribute->Form.Nonresident.MappingPairsOffset;
  8199. if (Attribute->NameOffset != 0) {
  8200. OldHeaderSize = Attribute->NameOffset;
  8201. }
  8202. if (TotalAllocatedNeeded) {
  8203. NewHeaderSize = SIZEOF_FULL_NONRES_ATTR_HEADER;
  8204. } else {
  8205. NewHeaderSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  8206. }
  8207. SizeChange = NewHeaderSize - OldHeaderSize;
  8208. //
  8209. // Make space if we need to do so. Lookup the attribute again
  8210. // if necessary.
  8211. //
  8212. if (SizeChange > 0) {
  8213. VCN StartingVcn;
  8214. VCN ClusterCount;
  8215. //
  8216. // If the attribute is alone in the file record and there isn't
  8217. // enough space available then the call to ChangeAttributeSize
  8218. // can't make any space available. In that case we call
  8219. // NtfsChangeAttributeAllocation and let that routine rewrite
  8220. // the mapping to make space available.
  8221. //
  8222. if ((FileRecord->BytesAvailable - FileRecord->FirstFreeByte < (ULONG) SizeChange) &&
  8223. (NtfsFirstAttribute( FileRecord ) == Attribute) &&
  8224. (((PATTRIBUTE_RECORD_HEADER) NtfsGetNextRecord( Attribute ))->TypeCode == $END)) {
  8225. NtfsLookupAllocation( IrpContext,
  8226. Scb,
  8227. Attribute->Form.Nonresident.HighestVcn,
  8228. &StartingVcn,
  8229. &ClusterCount,
  8230. NULL,
  8231. NULL );
  8232. StartingVcn = 0;
  8233. ClusterCount = Attribute->Form.Nonresident.HighestVcn + 1;
  8234. NtfsAddAttributeAllocation( IrpContext,
  8235. Scb,
  8236. &AttrContext,
  8237. &StartingVcn,
  8238. &ClusterCount );
  8239. } else if (NtfsChangeAttributeSize( IrpContext,
  8240. Scb->Fcb,
  8241. Attribute->RecordLength + SizeChange,
  8242. &AttrContext)) {
  8243. break;
  8244. }
  8245. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8246. NtfsInitializeAttributeContext( &AttrContext );
  8247. continue;
  8248. }
  8249. break;
  8250. }
  8251. NtfsPinMappedAttribute( IrpContext, Scb->Vcb, &AttrContext );
  8252. //
  8253. // Make a copy of the existing attribute and modify the total allocated field
  8254. // if necessary.
  8255. //
  8256. NewAttribute = NtfsAllocatePool( PagedPool, Attribute->RecordLength + SizeChange );
  8257. RtlCopyMemory( NewAttribute,
  8258. Attribute,
  8259. SIZEOF_PARTIAL_NONRES_ATTR_HEADER );
  8260. if (Attribute->NameOffset != 0) {
  8261. NewAttribute->NameOffset += (USHORT) SizeChange;
  8262. NewAttributeName = &Scb->AttributeName;
  8263. RtlCopyMemory( Add2Ptr( NewAttribute, NewAttribute->NameOffset ),
  8264. NewAttributeName->Buffer,
  8265. NewAttributeName->Length );
  8266. }
  8267. NewAttribute->Form.Nonresident.MappingPairsOffset += (USHORT) SizeChange;
  8268. NewAttribute->RecordLength += SizeChange;
  8269. RtlCopyMemory( Add2Ptr( NewAttribute, NewAttribute->Form.Nonresident.MappingPairsOffset ),
  8270. Add2Ptr( Attribute, Attribute->Form.Nonresident.MappingPairsOffset ),
  8271. Attribute->RecordLength - Attribute->Form.Nonresident.MappingPairsOffset );
  8272. if (TotalAllocatedNeeded) {
  8273. NewAttribute->Form.Nonresident.TotalAllocated = Scb->TotalAllocated;
  8274. }
  8275. //
  8276. // We now have the before and after image to log.
  8277. //
  8278. FileRecord->Lsn =
  8279. NtfsWriteLog( IrpContext,
  8280. Scb->Vcb->MftScb,
  8281. NtfsFoundBcb( &AttrContext ),
  8282. DeleteAttribute,
  8283. NULL,
  8284. 0,
  8285. CreateAttribute,
  8286. Attribute,
  8287. Attribute->RecordLength,
  8288. NtfsMftOffset( &AttrContext ),
  8289. PtrOffset( FileRecord, Attribute ),
  8290. 0,
  8291. Scb->Vcb->BytesPerFileRecordSegment );
  8292. NtfsRestartRemoveAttribute( IrpContext, FileRecord, PtrOffset( FileRecord, Attribute ));
  8293. FileRecord->Lsn =
  8294. NtfsWriteLog( IrpContext,
  8295. Scb->Vcb->MftScb,
  8296. NtfsFoundBcb( &AttrContext ),
  8297. CreateAttribute,
  8298. NewAttribute,
  8299. NewAttribute->RecordLength,
  8300. DeleteAttribute,
  8301. NULL,
  8302. 0,
  8303. NtfsMftOffset( &AttrContext ),
  8304. PtrOffset( FileRecord, Attribute ),
  8305. 0,
  8306. Scb->Vcb->BytesPerFileRecordSegment );
  8307. NtfsRestartInsertAttribute( IrpContext,
  8308. FileRecord,
  8309. PtrOffset( FileRecord, Attribute ),
  8310. NewAttribute,
  8311. NewAttributeName,
  8312. Add2Ptr( NewAttribute, NewAttribute->Form.Nonresident.MappingPairsOffset ),
  8313. NewAttribute->RecordLength - NewAttribute->Form.Nonresident.MappingPairsOffset );
  8314. } finally {
  8315. DebugUnwind( NtfsSetTotalAllocatedField );
  8316. if (NewAttribute != NULL) {
  8317. NtfsFreePool( NewAttribute );
  8318. }
  8319. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8320. }
  8321. return;
  8322. }
  8323. VOID
  8324. NtfsSetSparseStream (
  8325. IN PIRP_CONTEXT IrpContext,
  8326. IN PSCB ParentScb OPTIONAL,
  8327. IN PSCB Scb
  8328. )
  8329. /*++
  8330. Routine Description:
  8331. This routine is called change the state of a stream to sparse. It may be
  8332. called on behalf of a user or internally to Ntfs (i.e. for the USN
  8333. journal). Our caller may already have begun a transaction but in any
  8334. case will have acquired the main resource and paging resource for the
  8335. stream exclusively.
  8336. This routine will add the TotalAllocated field to the non-resident attribute
  8337. header and fully allocate (or deallocate) the final compression unit of
  8338. the stream. It will set the SPARSE flag in the attribute header as well as
  8339. in standard information and the directory entry for this stream.
  8340. NOTE - This routine will checkpoint the current transaction in order
  8341. to safely change the compression unit size and shift value in the Scb.
  8342. We also will update the Fcb duplicate information which is not protected
  8343. under transaction control.
  8344. Arguments:
  8345. ParentScb - Scb for the parent. If present we will update the directory
  8346. entry for the parent. Otherwise we simply set the FcbInfo flags and
  8347. let the update happen when the handle is closed.
  8348. Scb - Scb for the stream. Caller should have acquired this already.
  8349. Return Value:
  8350. None.
  8351. --*/
  8352. {
  8353. PFCB Fcb = Scb->Fcb;
  8354. PVCB Vcb = Scb->Vcb;
  8355. PLCB Lcb;
  8356. ULONG OriginalFileAttributes;
  8357. USHORT OriginalStreamAttributes;
  8358. UCHAR OriginalCompressionUnitShift;
  8359. ULONG OriginalCompressionUnit;
  8360. LONGLONG OriginalFileAllocatedLength;
  8361. UCHAR NewCompressionUnitShift;
  8362. ULONG NewCompressionUnit;
  8363. LONGLONG StartVcn;
  8364. LONGLONG FinalVcn;
  8365. ULONG AttributeSizeChange;
  8366. PATTRIBUTE_RECORD_HEADER Attribute;
  8367. ATTRIBUTE_RECORD_HEADER NewAttribute;
  8368. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  8369. ASSERT( (Scb->Header.PagingIoResource == NULL) ||
  8370. ExIsResourceAcquiredExclusiveLite( Scb->Header.PagingIoResource ));
  8371. ASSERT_EXCLUSIVE_SCB( Scb );
  8372. ASSERT( NtfsIsTypeCodeCompressible( Scb->AttributeTypeCode ));
  8373. PAGED_CODE();
  8374. //
  8375. // Return immediately if the stream is already sparse.
  8376. //
  8377. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  8378. return;
  8379. }
  8380. //
  8381. // Remember the current compression unit and flags.
  8382. //
  8383. OriginalFileAttributes = Fcb->Info.FileAttributes;
  8384. OriginalStreamAttributes = Scb->AttributeFlags;
  8385. OriginalCompressionUnitShift = Scb->CompressionUnitShift;
  8386. OriginalCompressionUnit = Scb->CompressionUnit;
  8387. OriginalFileAllocatedLength = Fcb->Info.AllocatedLength;
  8388. //
  8389. // Use a try-finally to facilitate cleanup.
  8390. //
  8391. NtfsInitializeAttributeContext( &AttrContext );
  8392. try {
  8393. //
  8394. // Post the change to the Usn Journal
  8395. //
  8396. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_BASIC_INFO_CHANGE );
  8397. //
  8398. // Acquire the parent now for the update duplicate call.
  8399. //
  8400. if (ARGUMENT_PRESENT( ParentScb )) {
  8401. NtfsPrepareForUpdateDuplicate( IrpContext, Fcb, &Lcb, &ParentScb, TRUE );
  8402. }
  8403. //
  8404. // If the file is not already compressed then we need to add a total allocated
  8405. // field and adjust the allocation length.
  8406. //
  8407. NewCompressionUnitShift = Scb->CompressionUnitShift;
  8408. NewCompressionUnit = Scb->CompressionUnit;
  8409. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  8410. //
  8411. // Compute the new compression unit and shift.
  8412. //
  8413. NewCompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
  8414. NewCompressionUnit = BytesFromClusters( Vcb,
  8415. 1 << NTFS_CLUSTERS_PER_COMPRESSION );
  8416. //
  8417. // If the compression unit is larger than 64K then find the correct
  8418. // compression unit to reach exactly 64k.
  8419. //
  8420. while (NewCompressionUnit > Vcb->SparseFileUnit) {
  8421. NewCompressionUnitShift -= 1;
  8422. NewCompressionUnit /= 2;
  8423. }
  8424. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  8425. //
  8426. // Fully allocate the final compression unit.
  8427. //
  8428. if (Scb->Header.AllocationSize.LowPart & (NewCompressionUnit - 1)) {
  8429. StartVcn = LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart );
  8430. FinalVcn = Scb->Header.AllocationSize.QuadPart + NewCompressionUnit - 1;
  8431. ((PLARGE_INTEGER) &FinalVcn)->LowPart &= ~(NewCompressionUnit - 1);
  8432. FinalVcn = LlClustersFromBytesTruncate( Vcb, FinalVcn );
  8433. NtfsAddAllocation( IrpContext,
  8434. NULL,
  8435. Scb,
  8436. StartVcn,
  8437. FinalVcn - StartVcn,
  8438. FALSE,
  8439. NULL );
  8440. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  8441. Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  8442. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  8443. }
  8444. }
  8445. //
  8446. // Add a total allocated field to the attribute record header.
  8447. //
  8448. NtfsSetTotalAllocatedField( IrpContext, Scb, ATTRIBUTE_FLAG_SPARSE );
  8449. }
  8450. }
  8451. //
  8452. // Look up the existing attribute.
  8453. //
  8454. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  8455. NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
  8456. Attribute = NtfsFoundAttribute( &AttrContext );
  8457. //
  8458. // Now we need to set the bits in the attribute flag field.
  8459. //
  8460. if (NtfsIsAttributeResident( Attribute )) {
  8461. RtlCopyMemory( &NewAttribute, Attribute, SIZEOF_RESIDENT_ATTRIBUTE_HEADER );
  8462. AttributeSizeChange = SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  8463. //
  8464. // Else if it is nonresident, copy it here, set the compression parameter,
  8465. // and remember its size.
  8466. //
  8467. } else {
  8468. AttributeSizeChange = Attribute->Form.Nonresident.MappingPairsOffset;
  8469. if (Attribute->NameOffset != 0) {
  8470. AttributeSizeChange = Attribute->NameOffset;
  8471. }
  8472. ASSERT( AttributeSizeChange <= sizeof( NewAttribute ));
  8473. RtlCopyMemory( &NewAttribute, Attribute, AttributeSizeChange );
  8474. NewAttribute.Form.Nonresident.CompressionUnit = NewCompressionUnitShift;
  8475. }
  8476. SetFlag( NewAttribute.Flags, ATTRIBUTE_FLAG_SPARSE );
  8477. //
  8478. // Now, log the changed attribute.
  8479. //
  8480. (VOID)NtfsWriteLog( IrpContext,
  8481. Vcb->MftScb,
  8482. NtfsFoundBcb( &AttrContext ),
  8483. UpdateResidentValue,
  8484. &NewAttribute,
  8485. AttributeSizeChange,
  8486. UpdateResidentValue,
  8487. Attribute,
  8488. AttributeSizeChange,
  8489. NtfsMftOffset( &AttrContext ),
  8490. PtrOffset(NtfsContainingFileRecord( &AttrContext ), Attribute),
  8491. 0,
  8492. Vcb->BytesPerFileRecordSegment );
  8493. //
  8494. // Change the attribute by calling the same routine called at restart.
  8495. //
  8496. NtfsRestartChangeValue( IrpContext,
  8497. NtfsContainingFileRecord( &AttrContext ),
  8498. PtrOffset( NtfsContainingFileRecord( &AttrContext ), Attribute ),
  8499. 0,
  8500. &NewAttribute,
  8501. AttributeSizeChange,
  8502. FALSE );
  8503. //
  8504. // If the file is not already marked sparse then update the standard information
  8505. // and the parent directory (if specified). Also report the change via
  8506. // dirnotify if there is a Ccb with a name.
  8507. //
  8508. ASSERTMSG( "conflict with flush",
  8509. ExIsResourceAcquiredSharedLite( Fcb->Resource ) ||
  8510. (Fcb->PagingIoResource != NULL &&
  8511. ExIsResourceAcquiredSharedLite( Fcb->PagingIoResource )));
  8512. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  8513. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  8514. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  8515. //
  8516. // Update the attributes in standard information.
  8517. //
  8518. NtfsUpdateStandardInformation( IrpContext, Fcb );
  8519. if (ARGUMENT_PRESENT( ParentScb )) {
  8520. //
  8521. // Update the directory entry for this file.
  8522. //
  8523. NtfsUpdateDuplicateInfo( IrpContext, Fcb, NULL, NULL );
  8524. NtfsUpdateLcbDuplicateInfo( Fcb, Lcb );
  8525. Fcb->InfoFlags = 0;
  8526. }
  8527. //
  8528. // Update the compression values and the sparse flag in the Scb.
  8529. //
  8530. Scb->CompressionUnit = NewCompressionUnit;
  8531. Scb->CompressionUnitShift = NewCompressionUnitShift;
  8532. SetFlag( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE );
  8533. //
  8534. // Set the FastIo state.
  8535. //
  8536. NtfsAcquireFsrtlHeader( Scb );
  8537. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  8538. SetFlag( Scb->Header.Flags2, FSRTL_FLAG2_PURGE_WHEN_MAPPED );
  8539. NtfsReleaseFsrtlHeader( Scb );
  8540. //
  8541. // Commit this change.
  8542. //
  8543. NtfsCheckpointCurrentTransaction( IrpContext );
  8544. } finally {
  8545. DebugUnwind( NtfsSetSparseStream );
  8546. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  8547. //
  8548. // Backout the changes to the non-logged structures on abort.
  8549. //
  8550. if (AbnormalTermination()) {
  8551. Fcb->Info.FileAttributes = OriginalFileAttributes;
  8552. Scb->AttributeFlags = OriginalStreamAttributes;
  8553. if (!FlagOn( OriginalStreamAttributes, ATTRIBUTE_FLAG_SPARSE )) {
  8554. NtfsAcquireFsrtlHeader( Scb );
  8555. ClearFlag( Scb->Header.Flags2, FSRTL_FLAG2_PURGE_WHEN_MAPPED );
  8556. NtfsReleaseFsrtlHeader( Scb );
  8557. }
  8558. Scb->CompressionUnitShift = OriginalCompressionUnitShift;
  8559. Scb->CompressionUnit = OriginalCompressionUnit;
  8560. Fcb->Info.AllocatedLength = OriginalFileAllocatedLength;
  8561. }
  8562. }
  8563. return;
  8564. }
  8565. NTSTATUS
  8566. NtfsZeroRangeInStream (
  8567. IN PIRP_CONTEXT IrpContext,
  8568. IN PFILE_OBJECT FileObject OPTIONAL,
  8569. IN PSCB Scb,
  8570. IN PLONGLONG StartingOffset,
  8571. IN LONGLONG FinalZero
  8572. )
  8573. /*++
  8574. Routine Description:
  8575. This routine is the worker routine which will zero a range of a stream and
  8576. (if sparse) deallocate any space in the stream that is convenient. We only
  8577. perform this operation on $DATA streams where there are no user maps. We
  8578. will zero, flush and purge any partial pages. We will zero full pages except
  8579. for sparse streams where we will purge the data and deallocate the
  8580. disk backing for this range.
  8581. This routine will fail if the stream has a user map. Note that if the user
  8582. is zeroing the end of the stream we can choose to simply move valid data length
  8583. and purge the existing data instead of performing extensive flush operations.
  8584. Arguments:
  8585. FileObject - A file object for the stream. We can use this to follow the
  8586. caller's preference for write through.
  8587. Scb - This is the Scb for the stream we are to zero. The user may have acquired
  8588. the paging io resource prior to this call but the main resource should
  8589. not be acquired.
  8590. StartingOffset - Offset in the file to start the zero operation. This may
  8591. lie outside of the file size. We update this to reflect the current
  8592. position through the file. That way if this routine should raise log
  8593. file full our caller can resume from the point where we left off.
  8594. FinalZero - Offset of last byte in the file to zero.
  8595. Return Value:
  8596. NTSTATUS - Result of this operation.
  8597. --*/
  8598. {
  8599. NTSTATUS Status = STATUS_SUCCESS;
  8600. BOOLEAN ReleaseScb = FALSE;
  8601. BOOLEAN UnlockHeader = FALSE;
  8602. LONGLONG LastOffset = -1;
  8603. LONGLONG CurrentBytes;
  8604. LONGLONG CurrentOffset;
  8605. LONGLONG CurrentFinalByte;
  8606. LONGLONG ClusterCount;
  8607. ULONG ClustersPerCompressionUnit;
  8608. BOOLEAN ThrottleWrites;
  8609. VCN NextVcn;
  8610. VCN CurrentVcn;
  8611. LCN Lcn;
  8612. PBCB ZeroBufferBcb = NULL;
  8613. PVOID ZeroBuffer;
  8614. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  8615. BOOLEAN CleanupAttrContext = FALSE;
  8616. PAGED_CODE();
  8617. //
  8618. // We better not be holding the main resource without also holding
  8619. // the paging resource, if any.
  8620. //
  8621. ASSERT( !NtfsIsSharedScb( Scb ) ||
  8622. (Scb->Header.PagingIoResource == NULL) ||
  8623. NtfsIsExclusiveScbPagingIo( Scb ) );
  8624. //
  8625. // We will loop through the requested zero range. We will checkpoint
  8626. // periodically and drop all resources so we don't become a bottle neck
  8627. // in the system.
  8628. //
  8629. try {
  8630. while (TRUE) {
  8631. //
  8632. // Acquire either the paging Io resource if present and lock the header
  8633. // or simply acquire the main resource.
  8634. //
  8635. if (Scb->Header.PagingIoResource != NULL) {
  8636. if (IrpContext->CleanupStructure != NULL) {
  8637. ASSERT( (PFCB)IrpContext->CleanupStructure == Scb->Fcb );
  8638. } else {
  8639. NtfsAcquirePagingResourceExclusive( IrpContext, Scb, TRUE );
  8640. FsRtlLockFsRtlHeader( &Scb->Header );
  8641. IrpContext->CleanupStructure = Scb;
  8642. UnlockHeader = TRUE;
  8643. }
  8644. } else {
  8645. NtfsAcquireExclusiveScb( IrpContext, Scb );
  8646. ReleaseScb = TRUE;
  8647. }
  8648. //
  8649. // Verify that the file and volume are still present.
  8650. //
  8651. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED | SCB_STATE_VOLUME_DISMOUNTED)) {
  8652. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  8653. Status = STATUS_FILE_DELETED;
  8654. } else {
  8655. Status = STATUS_VOLUME_DISMOUNTED;
  8656. }
  8657. leave;
  8658. }
  8659. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  8660. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  8661. }
  8662. //
  8663. // If we are past the end of the file or the length is zero we can break out.
  8664. //
  8665. if ((*StartingOffset >= Scb->Header.FileSize.QuadPart) ||
  8666. (*StartingOffset >= FinalZero)) {
  8667. try_return( NOTHING );
  8668. }
  8669. ThrottleWrites = FALSE;
  8670. //
  8671. // Check for the oplock and file state.
  8672. //
  8673. if (ARGUMENT_PRESENT( FileObject )) {
  8674. CurrentBytes = FinalZero - *StartingOffset;
  8675. if (FinalZero > Scb->Header.FileSize.QuadPart) {
  8676. CurrentBytes = Scb->Header.FileSize.QuadPart - *StartingOffset;
  8677. }
  8678. if (CurrentBytes > NTFS_MAX_ZERO_RANGE) {
  8679. CurrentBytes = NTFS_MAX_ZERO_RANGE;
  8680. }
  8681. Status = NtfsCheckLocksInZeroRange( IrpContext,
  8682. IrpContext->OriginatingIrp,
  8683. Scb,
  8684. FileObject,
  8685. StartingOffset,
  8686. (ULONG) CurrentBytes );
  8687. if (Status != STATUS_SUCCESS) {
  8688. leave;
  8689. }
  8690. }
  8691. //
  8692. // Post the change to the Usn Journal
  8693. //
  8694. NtfsPostUsnChange( IrpContext, Scb, USN_REASON_DATA_OVERWRITE );
  8695. //
  8696. // We are going to make the changes. Make sure we set the file object
  8697. // flag to indicate we are making changes.
  8698. //
  8699. if (ARGUMENT_PRESENT( FileObject )) {
  8700. SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  8701. }
  8702. //
  8703. // If the file is resident then flush and purge the stream and
  8704. // then change the attribute itself.
  8705. //
  8706. if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  8707. //
  8708. // Trim the remaining bytes to file size.
  8709. //
  8710. CurrentBytes = FinalZero - *StartingOffset;
  8711. if (FinalZero > Scb->Header.FileSize.QuadPart) {
  8712. CurrentBytes = Scb->Header.FileSize.QuadPart - *StartingOffset;
  8713. }
  8714. Status = NtfsFlushUserStream( IrpContext, Scb, NULL, 0 );
  8715. NtfsNormalizeAndCleanupTransaction( IrpContext,
  8716. &Status,
  8717. TRUE,
  8718. STATUS_UNEXPECTED_IO_ERROR );
  8719. //
  8720. // Proceed if there is nothing to purge or the purge succeeds.
  8721. //
  8722. if ((Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
  8723. !CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  8724. NULL,
  8725. 0,
  8726. FALSE )) {
  8727. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  8728. leave;
  8729. }
  8730. //
  8731. // Acquire the main resource to change the attribute.
  8732. //
  8733. if (!ReleaseScb) {
  8734. NtfsAcquireExclusiveScb( IrpContext, Scb );
  8735. ReleaseScb = TRUE;
  8736. }
  8737. //
  8738. // Now look up the attribute and zero the requested range.
  8739. //
  8740. NtfsInitializeAttributeContext( &AttrContext );
  8741. CleanupAttrContext = TRUE;
  8742. NtfsLookupAttributeForScb( IrpContext,
  8743. Scb,
  8744. NULL,
  8745. &AttrContext );
  8746. NtfsChangeAttributeValue( IrpContext,
  8747. Scb->Fcb,
  8748. (ULONG) *StartingOffset,
  8749. NULL,
  8750. (ULONG) CurrentBytes,
  8751. FALSE,
  8752. TRUE,
  8753. FALSE,
  8754. FALSE,
  8755. &AttrContext );
  8756. NtfsCheckpointCurrentTransaction( IrpContext );
  8757. *StartingOffset += CurrentBytes;
  8758. try_return( NOTHING );
  8759. }
  8760. //
  8761. // Make sure there are no mapped sections in the range we are trying to
  8762. // zero.
  8763. //
  8764. if (!MmCanFileBeTruncated( &Scb->NonpagedScb->SegmentObject,
  8765. (PLARGE_INTEGER) StartingOffset )) {
  8766. Status = STATUS_USER_MAPPED_FILE;
  8767. try_return( NOTHING );
  8768. }
  8769. //
  8770. // If the file is either sparse or compressed then we look for ranges
  8771. // we need to flush, purge or deallocate.
  8772. //
  8773. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  8774. ClustersPerCompressionUnit = 1 << Scb->CompressionUnitShift;
  8775. //
  8776. // Move our starting point back to a compression unit boundary. If our
  8777. // ending point is past the end of the file then set it to the compression
  8778. // unit past the EOF.
  8779. //
  8780. CurrentOffset = *StartingOffset & ~((LONGLONG) (Scb->CompressionUnit - 1));
  8781. CurrentFinalByte = FinalZero;
  8782. if (CurrentFinalByte >= Scb->Header.FileSize.QuadPart) {
  8783. CurrentFinalByte = BlockAlign( Scb->Header.FileSize.QuadPart, (LONG)Scb->CompressionUnit );
  8784. }
  8785. //
  8786. // Then look forward for either an allocated range or a reserved compression
  8787. // unit. We may have to flush and/or purge data at that offset.
  8788. //
  8789. NextVcn =
  8790. CurrentVcn = LlClustersFromBytesTruncate( Scb->Vcb, CurrentOffset );
  8791. while (!NtfsLookupAllocation( IrpContext,
  8792. Scb,
  8793. NextVcn,
  8794. &Lcn,
  8795. &ClusterCount,
  8796. NULL,
  8797. NULL )) {
  8798. //
  8799. // Move the current Vcn forward by the size of the hole.
  8800. // Break out if we are beyond the final byte.
  8801. //
  8802. NextVcn += ClusterCount;
  8803. if ((LONGLONG) LlBytesFromClusters( Scb->Vcb, NextVcn ) >= CurrentFinalByte) {
  8804. //
  8805. // Trim the final Vcn to the beginning of the last compression unit.
  8806. //
  8807. NextVcn = LlClustersFromBytesTruncate( Scb->Vcb, CurrentFinalByte );
  8808. break;
  8809. }
  8810. }
  8811. //
  8812. // Back up to a compression unit.
  8813. //
  8814. NextVcn = BlockAlignTruncate( NextVcn, (LONG)ClustersPerCompressionUnit );
  8815. //
  8816. // If we found a hole then we need to look for reserved clusters within
  8817. // the range.
  8818. //
  8819. if (NextVcn != CurrentVcn) {
  8820. ClusterCount = NextVcn - CurrentVcn;
  8821. if (Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) {
  8822. NtfsCheckForReservedClusters( Scb, CurrentVcn, &ClusterCount );
  8823. }
  8824. CurrentVcn += ClusterCount;
  8825. }
  8826. //
  8827. // CurrentVcn - points to the first range we might have to zero in memory.
  8828. // NextVcn - points to the first range we might choose to deallocate.
  8829. //
  8830. // Proceed if we aren't beyond the final byte to zero.
  8831. //
  8832. CurrentOffset = LlBytesFromClusters( Scb->Vcb, CurrentVcn );
  8833. if (CurrentOffset >= CurrentFinalByte) {
  8834. ASSERT( IrpContext->TransactionId == 0 );
  8835. *StartingOffset = CurrentFinalByte;
  8836. try_return( NOTHING );
  8837. }
  8838. //
  8839. // If we find a range which is less than our starting offset then we will
  8840. // have to zero this range in the data section.
  8841. //
  8842. ASSERT( ((ULONG) CurrentOffset & (Scb->CompressionUnit - 1)) == 0 );
  8843. if (CurrentOffset < *StartingOffset) {
  8844. //
  8845. // Reserve a cluster to perform the write.
  8846. //
  8847. if (!NtfsReserveClusters( IrpContext, Scb, CurrentOffset, Scb->CompressionUnit )) {
  8848. Status = STATUS_DISK_FULL;
  8849. try_return( NOTHING );
  8850. }
  8851. //
  8852. // Limit the zero range.
  8853. //
  8854. CurrentBytes = Scb->CompressionUnit - (*StartingOffset - CurrentOffset);
  8855. if (CurrentOffset + Scb->CompressionUnit > CurrentFinalByte) {
  8856. CurrentBytes = CurrentFinalByte - *StartingOffset;
  8857. }
  8858. //
  8859. // See if we have to create an internal attribute stream.
  8860. //
  8861. if (Scb->FileObject == NULL) {
  8862. NtfsCreateInternalAttributeStream( IrpContext,
  8863. Scb,
  8864. FALSE,
  8865. &NtfsInternalUseFile[ZERORANGEINSTREAM_FILE_NUMBER] );
  8866. }
  8867. //
  8868. // Zero the data in the cache.
  8869. //
  8870. CcPinRead( Scb->FileObject,
  8871. (PLARGE_INTEGER) &CurrentOffset,
  8872. Scb->CompressionUnit,
  8873. TRUE,
  8874. &ZeroBufferBcb,
  8875. &ZeroBuffer );
  8876. #ifdef MAPCOUNT_DBG
  8877. IrpContext->MapCount++;
  8878. #endif
  8879. RtlZeroMemory( Add2Ptr( ZeroBuffer,
  8880. ((ULONG) *StartingOffset) & (Scb->CompressionUnit - 1)),
  8881. (ULONG) CurrentBytes );
  8882. CcSetDirtyPinnedData( ZeroBufferBcb, NULL );
  8883. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  8884. //
  8885. // Update the current offset to our position within the compression unit.
  8886. //
  8887. CurrentOffset += ((ULONG) *StartingOffset) & (Scb->CompressionUnit - 1);
  8888. //
  8889. // If the current compression unit includes the last byte to zero
  8890. // then we need flush and/or purge this compression unit.
  8891. //
  8892. } else if (CurrentOffset + Scb->CompressionUnit > CurrentFinalByte) {
  8893. //
  8894. // Reserve a cluster to perform the write.
  8895. //
  8896. if (!NtfsReserveClusters( IrpContext, Scb, CurrentOffset, Scb->CompressionUnit )) {
  8897. Status = STATUS_DISK_FULL;
  8898. try_return( NOTHING );
  8899. }
  8900. //
  8901. // Limit the zero range.
  8902. //
  8903. CurrentBytes = (ULONG) CurrentFinalByte & (Scb->CompressionUnit - 1);
  8904. //
  8905. // See if we have to create an internal attribute stream.
  8906. //
  8907. if (Scb->FileObject == NULL) {
  8908. NtfsCreateInternalAttributeStream( IrpContext,
  8909. Scb,
  8910. FALSE,
  8911. &NtfsInternalUseFile[ZERORANGEINSTREAM2_FILE_NUMBER] );
  8912. }
  8913. //
  8914. // Zero the data in the cache.
  8915. //
  8916. CcPinRead( Scb->FileObject,
  8917. (PLARGE_INTEGER) &CurrentOffset,
  8918. (ULONG) CurrentBytes,
  8919. TRUE,
  8920. &ZeroBufferBcb,
  8921. &ZeroBuffer );
  8922. #ifdef MAPCOUNT_DBG
  8923. IrpContext->MapCount++;
  8924. #endif
  8925. RtlZeroMemory( ZeroBuffer, (ULONG) CurrentBytes );
  8926. CcSetDirtyPinnedData( ZeroBufferBcb, NULL );
  8927. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  8928. } else {
  8929. //
  8930. // Compute the range we want to purge. We will process a maximum of 2Gig
  8931. // at a time.
  8932. //
  8933. CurrentBytes = CurrentFinalByte - CurrentOffset;
  8934. if (CurrentBytes > NTFS_MAX_ZERO_RANGE) {
  8935. CurrentBytes = NTFS_MAX_ZERO_RANGE;
  8936. }
  8937. //
  8938. // Round the size to a compression unit.
  8939. //
  8940. CurrentBytes = BlockAlignTruncate( CurrentBytes, (LONG)Scb->CompressionUnit );
  8941. //
  8942. // If this is the retry case then let's reduce the amount to
  8943. // zero.
  8944. //
  8945. if ((*StartingOffset == LastOffset) &&
  8946. (CurrentBytes > Scb->CompressionUnit)) {
  8947. CurrentBytes = Scb->CompressionUnit;
  8948. CurrentFinalByte = CurrentOffset + CurrentBytes;
  8949. }
  8950. //
  8951. // Purge the data in this range.
  8952. //
  8953. if ((Scb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
  8954. !CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  8955. (PLARGE_INTEGER) &CurrentOffset,
  8956. (ULONG) CurrentBytes,
  8957. FALSE )) {
  8958. //
  8959. // There may be a section in the cache manager which is being
  8960. // flushed. Go ahead and see if we can force the data out
  8961. // so the purge will succeed.
  8962. //
  8963. Status = NtfsFlushUserStream( IrpContext,
  8964. Scb,
  8965. &CurrentOffset,
  8966. (ULONG) CurrentBytes );
  8967. NtfsNormalizeAndCleanupTransaction( IrpContext,
  8968. &Status,
  8969. TRUE,
  8970. STATUS_UNEXPECTED_IO_ERROR );
  8971. //
  8972. // If this is the retry case then let's reduce the amount to
  8973. // zero.
  8974. //
  8975. if (CurrentBytes > Scb->CompressionUnit) {
  8976. CurrentBytes = Scb->CompressionUnit;
  8977. CurrentFinalByte = CurrentOffset + CurrentBytes;
  8978. }
  8979. //
  8980. // Now try the purge again.
  8981. //
  8982. if (!CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  8983. (PLARGE_INTEGER) &CurrentOffset,
  8984. (ULONG) CurrentBytes,
  8985. FALSE )) {
  8986. //
  8987. // If our retry failed then give up.
  8988. //
  8989. if (*StartingOffset == LastOffset) {
  8990. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  8991. leave;
  8992. }
  8993. //
  8994. // Otherwise show that we haven't advanced, but we
  8995. // will take one more crack at this.
  8996. //
  8997. CurrentBytes = 0;
  8998. }
  8999. }
  9000. //
  9001. // Delete the allocation if we have any bytes to work with.
  9002. //
  9003. if (CurrentBytes != 0) {
  9004. //
  9005. // Acquire the main resource to change the allocation.
  9006. //
  9007. if (!ReleaseScb) {
  9008. NtfsAcquireExclusiveScb( IrpContext, Scb );
  9009. ReleaseScb = TRUE;
  9010. }
  9011. //
  9012. // Now deallocate the clusters in this range if we have some to delete.
  9013. // Use ClusterCount to indicate the last Vcn to deallocate.
  9014. //
  9015. ClusterCount = CurrentVcn + LlClustersFromBytesTruncate( Scb->Vcb, CurrentBytes ) - 1;
  9016. if (NextVcn <= ClusterCount) {
  9017. NtfsDeleteAllocation( IrpContext,
  9018. FileObject,
  9019. Scb,
  9020. NextVcn,
  9021. ClusterCount,
  9022. TRUE,
  9023. TRUE );
  9024. //
  9025. // Move VDD fwd to protect this hole for compressed files
  9026. //
  9027. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9028. if ((ULONGLONG)Scb->ValidDataToDisk < LlBytesFromClusters( Scb->Vcb, ClusterCount )) {
  9029. Scb->ValidDataToDisk = LlBytesFromClusters( Scb->Vcb, ClusterCount );
  9030. }
  9031. }
  9032. }
  9033. //
  9034. // Free up the reserved bitmap if there are any bits to clear.
  9035. //
  9036. NtfsFreeReservedClusters( Scb, CurrentOffset, (ULONG) CurrentBytes );
  9037. }
  9038. }
  9039. //
  9040. // Otherwise the file is uncompressed/non-sparse, we need to zero partial
  9041. // cluster and then we need to flush and/or purge the existing pages.
  9042. //
  9043. } else {
  9044. //
  9045. // Remember the current offset within the stream and
  9046. // the length to zero now.
  9047. //
  9048. CurrentOffset = *StartingOffset;
  9049. CurrentFinalByte = (CurrentOffset + 0x40000) & ~((LONGLONG) (0x40000 - 1));
  9050. if (CurrentFinalByte > Scb->Header.FileSize.QuadPart) {
  9051. CurrentFinalByte = Scb->Header.FileSize.QuadPart;
  9052. }
  9053. if (CurrentFinalByte > FinalZero) {
  9054. CurrentFinalByte = FinalZero;
  9055. }
  9056. //
  9057. // Determine the number of bytes remaining in the current cache view.
  9058. //
  9059. CurrentBytes = CurrentFinalByte - CurrentOffset;
  9060. //
  9061. // If this is the retry case then let's reduce the amount to
  9062. // zero.
  9063. //
  9064. if ((*StartingOffset == LastOffset) &&
  9065. (CurrentBytes > PAGE_SIZE)) {
  9066. CurrentBytes = PAGE_SIZE;
  9067. CurrentFinalByte = CurrentOffset + CurrentBytes;
  9068. }
  9069. //
  9070. // Purge the data in this range.
  9071. //
  9072. if (Scb->NonpagedScb->SegmentObject.DataSectionObject &&
  9073. !CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  9074. (PLARGE_INTEGER) &CurrentOffset,
  9075. (ULONG) CurrentBytes,
  9076. FALSE )) {
  9077. //
  9078. // There may be a section in the cache manager which is being
  9079. // flushed. Go ahead and see if we can force the data out
  9080. // so the purge will succeed.
  9081. //
  9082. Status = NtfsFlushUserStream( IrpContext,
  9083. Scb,
  9084. &CurrentOffset,
  9085. (ULONG) CurrentBytes );
  9086. NtfsNormalizeAndCleanupTransaction( IrpContext,
  9087. &Status,
  9088. TRUE,
  9089. STATUS_UNEXPECTED_IO_ERROR );
  9090. //
  9091. // Let's trim back the amount of data to purge at once.
  9092. //
  9093. if (CurrentBytes > PAGE_SIZE) {
  9094. CurrentBytes = PAGE_SIZE;
  9095. CurrentFinalByte = CurrentOffset + CurrentBytes;
  9096. }
  9097. //
  9098. // Now try the purge again.
  9099. //
  9100. if (!CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  9101. (PLARGE_INTEGER) &CurrentOffset,
  9102. (ULONG) CurrentBytes,
  9103. FALSE )) {
  9104. //
  9105. // If our retry failed then give up.
  9106. //
  9107. if (*StartingOffset == LastOffset) {
  9108. Status = STATUS_UNABLE_TO_DELETE_SECTION;
  9109. leave;
  9110. }
  9111. //
  9112. // Otherwise show that we haven't advanced, but we
  9113. // will take one more crack at this.
  9114. //
  9115. CurrentBytes = 0;
  9116. }
  9117. }
  9118. //
  9119. // Continue if we have bytes to zero.
  9120. //
  9121. if (CurrentBytes != 0) {
  9122. //
  9123. // If we are within valid data length then zero the data.
  9124. //
  9125. if (CurrentOffset < Scb->Header.ValidDataLength.QuadPart) {
  9126. //
  9127. // See if we have to create an internal attribute stream.
  9128. //
  9129. if (Scb->FileObject == NULL) {
  9130. NtfsCreateInternalAttributeStream( IrpContext,
  9131. Scb,
  9132. FALSE,
  9133. &NtfsInternalUseFile[ZERORANGEINSTREAM3_FILE_NUMBER] );
  9134. }
  9135. //
  9136. // Zero the data in the cache.
  9137. //
  9138. CcPinRead( Scb->FileObject,
  9139. (PLARGE_INTEGER) &CurrentOffset,
  9140. (ULONG) CurrentBytes,
  9141. TRUE,
  9142. &ZeroBufferBcb,
  9143. &ZeroBuffer );
  9144. #ifdef MAPCOUNT_DBG
  9145. IrpContext->MapCount++;
  9146. #endif
  9147. RtlZeroMemory( ZeroBuffer, (ULONG) CurrentBytes );
  9148. CcSetDirtyPinnedData( ZeroBufferBcb, NULL );
  9149. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  9150. }
  9151. //
  9152. // We want to throttle the writes if there is more to do.
  9153. //
  9154. if (CurrentFinalByte < FinalZero) {
  9155. ThrottleWrites = TRUE;
  9156. }
  9157. }
  9158. }
  9159. //
  9160. // Check and see if we can advance valid data length.
  9161. //
  9162. if ((CurrentOffset + CurrentBytes > Scb->Header.ValidDataLength.QuadPart) &&
  9163. (*StartingOffset <= Scb->Header.ValidDataLength.QuadPart)) {
  9164. NtfsAcquireFsrtlHeader( Scb );
  9165. Scb->Header.ValidDataLength.QuadPart = CurrentOffset + CurrentBytes;
  9166. if (Scb->Header.ValidDataLength.QuadPart > Scb->Header.FileSize.QuadPart) {
  9167. Scb->Header.ValidDataLength.QuadPart = Scb->Header.FileSize.QuadPart;
  9168. }
  9169. #ifdef SYSCACHE_DEBUG
  9170. if (ScbIsBeingLogged( Scb )) {
  9171. FsRtlLogSyscacheEvent( Scb, SCE_ZERO_STREAM, SCE_FLAG_SET_VDL, Scb->Header.ValidDataLength.QuadPart, 0, 0 );
  9172. }
  9173. #endif
  9174. NtfsReleaseFsrtlHeader( Scb );
  9175. }
  9176. //
  9177. // Checkpoint and past the current bytes.
  9178. //
  9179. if (NtfsIsExclusiveScb( Scb->Vcb->MftScb )) {
  9180. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_MFT );
  9181. }
  9182. NtfsCheckpointCurrentTransaction( IrpContext );
  9183. LastOffset = *StartingOffset;
  9184. if (CurrentBytes != 0) {
  9185. *StartingOffset = CurrentOffset + CurrentBytes;
  9186. }
  9187. //
  9188. // Release all of the resources so we don't create a bottleneck.
  9189. //
  9190. if (UnlockHeader) {
  9191. FsRtlUnlockFsRtlHeader( &Scb->Header );
  9192. IrpContext->CleanupStructure = NULL;
  9193. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  9194. UnlockHeader = FALSE;
  9195. }
  9196. if (ReleaseScb) {
  9197. NtfsReleaseScb( IrpContext, Scb );
  9198. ReleaseScb = FALSE;
  9199. }
  9200. //
  9201. // Now throttle the writes if we are accessing an uncompressed/non-sparse file.
  9202. //
  9203. if (ARGUMENT_PRESENT( FileObject ) && ThrottleWrites) {
  9204. CcCanIWrite( FileObject, 0x40000, TRUE, FALSE );
  9205. }
  9206. }
  9207. try_exit: NOTHING;
  9208. //
  9209. // If we have a user file object then check if we need to write any
  9210. // data to disk.
  9211. //
  9212. if ((Status == STATUS_SUCCESS) && ARGUMENT_PRESENT( FileObject )) {
  9213. if ((FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) ||
  9214. IsFileWriteThrough( FileObject, Scb->Vcb ))) {
  9215. //
  9216. // We either want to flush the Scb or flush and purge the Scb.
  9217. //
  9218. if ((Scb->CleanupCount == Scb->NonCachedCleanupCount) &&
  9219. !FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9220. //
  9221. // Flush and purge will alter filesizes on disk so preacquire the file exclusive
  9222. //
  9223. if (!ReleaseScb) {
  9224. NtfsAcquireExclusiveScb( IrpContext, Scb );
  9225. ReleaseScb = TRUE;
  9226. }
  9227. NtfsFlushAndPurgeScb( IrpContext, Scb, NULL );
  9228. } else {
  9229. Status = NtfsFlushUserStream( IrpContext, Scb, NULL, 0 );
  9230. NtfsNormalizeAndCleanupTransaction( IrpContext,
  9231. &Status,
  9232. TRUE,
  9233. STATUS_UNEXPECTED_IO_ERROR );
  9234. }
  9235. }
  9236. //
  9237. // If this is write through or non-cached then flush the log file as well.
  9238. //
  9239. if (IsFileWriteThrough( FileObject, Scb->Vcb ) ||
  9240. FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {
  9241. LfsFlushToLsn( Scb->Vcb->LogHandle, LiMax );
  9242. }
  9243. }
  9244. } finally {
  9245. DebugUnwind( NtfsZeroRangeInStream );
  9246. if (Status != STATUS_PENDING) {
  9247. //
  9248. // Release any held resources.
  9249. //
  9250. if (UnlockHeader) {
  9251. FsRtlUnlockFsRtlHeader( &Scb->Header );
  9252. IrpContext->CleanupStructure = NULL;
  9253. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  9254. }
  9255. if (ReleaseScb) {
  9256. NtfsReleaseScb( IrpContext, Scb );
  9257. }
  9258. //
  9259. // Even if STATUS_PENDING is returned we need to release the paging io
  9260. // resource. PrePostIrp will clear the IoAtEOF bit.
  9261. //
  9262. } else if (UnlockHeader) {
  9263. ExReleaseResourceLite( Scb->Header.PagingIoResource );
  9264. }
  9265. //
  9266. // Cleanup the attribute context if used.
  9267. //
  9268. if (CleanupAttrContext) {
  9269. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  9270. }
  9271. NtfsUnpinBcb( IrpContext, &ZeroBufferBcb );
  9272. }
  9273. return Status;
  9274. }
  9275. BOOLEAN
  9276. NtfsModifyAttributeFlags (
  9277. IN PIRP_CONTEXT IrpContext,
  9278. IN PSCB Scb,
  9279. IN USHORT NewAttributeFlags
  9280. )
  9281. /*++
  9282. Routine Description:
  9283. This routine is called to change the attribute for an Scb. It changes the values
  9284. associated with the AttributeFlags (Encryption, Sparse, Compressed).
  9285. This routine does not commit so our caller must know how to unwind changes to the Scb and
  9286. Fcb (compression fields and Fcb Info).
  9287. NOTE - This routine will update the Fcb duplicate info and flags as well as the compression unit
  9288. fields in the Scb. The caller is responsible for cleaning these up on error.
  9289. Arguments:
  9290. Scb - Scb for the stream being modified.
  9291. NewAttributeFlags - New flags to associate with the stream.
  9292. FcbInfoFlags - Pointer to store changes to apply to the Fcb Info flags.
  9293. Return Value:
  9294. BOOLEAN - TRUE if our caller needs to update duplicate info. FALSE otherwise.
  9295. --*/
  9296. {
  9297. PFCB Fcb = Scb->Fcb;
  9298. PVCB Vcb = Scb->Vcb;
  9299. ATTRIBUTE_RECORD_HEADER NewAttribute;
  9300. PATTRIBUTE_RECORD_HEADER Attribute;
  9301. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  9302. ULONG AttributeSizeChange;
  9303. BOOLEAN ChangeTotalAllocated = FALSE;
  9304. BOOLEAN ChangeCompression = FALSE;
  9305. BOOLEAN ChangeSparse = FALSE;
  9306. BOOLEAN ChangeEncryption = FALSE;
  9307. ULONG NewCompressionUnit;
  9308. UCHAR NewCompressionUnitShift;
  9309. BOOLEAN UpdateDuplicate = FALSE;
  9310. PAGED_CODE();
  9311. ASSERT( Scb->AttributeFlags != NewAttributeFlags );
  9312. NtfsInitializeAttributeContext( &AttrContext );
  9313. //
  9314. // Use a try-finally to facilitate cleanup.
  9315. //
  9316. try {
  9317. //
  9318. // Lookup the attribute and pin it so that we can modify it.
  9319. //
  9320. if ((Scb->Header.NodeTypeCode == NTFS_NTC_SCB_INDEX) ||
  9321. (Scb->Header.NodeTypeCode == NTFS_NTC_SCB_ROOT_INDEX)) {
  9322. //
  9323. // Lookup the attribute record from the Scb.
  9324. //
  9325. if (!NtfsLookupAttributeByName( IrpContext,
  9326. Fcb,
  9327. &Fcb->FileReference,
  9328. $INDEX_ROOT,
  9329. &Scb->AttributeName,
  9330. NULL,
  9331. FALSE,
  9332. &AttrContext )) {
  9333. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
  9334. }
  9335. Attribute = NtfsFoundAttribute( &AttrContext );
  9336. } else {
  9337. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  9338. Attribute = NtfsFoundAttribute( &AttrContext );
  9339. //
  9340. // If the new state is encrypted and the file is not currently encrypted then convert to
  9341. // non-resident.
  9342. //
  9343. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED ) &&
  9344. NtfsIsAttributeResident( Attribute )) {
  9345. NtfsConvertToNonresident( IrpContext,
  9346. Fcb,
  9347. Attribute,
  9348. FALSE,
  9349. &AttrContext );
  9350. }
  9351. }
  9352. //
  9353. // Remember which flags are changing.
  9354. //
  9355. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) !=
  9356. FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9357. ChangeCompression = TRUE;
  9358. }
  9359. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_SPARSE ) !=
  9360. FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_SPARSE )) {
  9361. ChangeSparse = TRUE;
  9362. }
  9363. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED ) !=
  9364. FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  9365. ChangeEncryption = TRUE;
  9366. }
  9367. //
  9368. // Point to the current attribute and save the current flags.
  9369. //
  9370. NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
  9371. Attribute = NtfsFoundAttribute( &AttrContext );
  9372. //
  9373. // Compute the new compression size. Use the following to determine this
  9374. //
  9375. // - New state is not compressed/sparse - Unit/UnitShift = 0
  9376. // - New state includes compressed/sparse
  9377. // - Current state includes compressed/sparse - No change
  9378. // - Stream is compressible - Default values (64K max)
  9379. // - Stream is not compressible - Unit/UnitShift = 0
  9380. //
  9381. NewCompressionUnit = Scb->CompressionUnit;
  9382. NewCompressionUnitShift = Scb->CompressionUnitShift;
  9383. //
  9384. // Set the correct compression unit but only for data streams. We
  9385. // don't want to change this value for the Index Root.
  9386. //
  9387. if (NtfsIsTypeCodeCompressible( Attribute->TypeCode )) {
  9388. //
  9389. // We need a compression unit for the attribute now.
  9390. //
  9391. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9392. if (!FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9393. ChangeTotalAllocated = TRUE;
  9394. NewCompressionUnit = BytesFromClusters( Scb->Vcb, 1 << NTFS_CLUSTERS_PER_COMPRESSION );
  9395. NewCompressionUnitShift = NTFS_CLUSTERS_PER_COMPRESSION;
  9396. //
  9397. // If the compression unit is larger than 64K then find the correct
  9398. // compression unit to reach exactly 64k.
  9399. //
  9400. while (NewCompressionUnit > Vcb->SparseFileUnit) {
  9401. NewCompressionUnitShift -= 1;
  9402. NewCompressionUnit /= 2;
  9403. }
  9404. }
  9405. } else {
  9406. //
  9407. // Check if we to remove the extra total allocated field.
  9408. //
  9409. if (FlagOn( Attribute->Flags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9410. ChangeTotalAllocated = TRUE;
  9411. }
  9412. NewCompressionUnit = 0;
  9413. NewCompressionUnitShift = 0;
  9414. }
  9415. }
  9416. //
  9417. // If the attribute is resident, copy it here and remember its
  9418. // header size.
  9419. //
  9420. if (NtfsIsAttributeResident( Attribute )) {
  9421. RtlCopyMemory( &NewAttribute, Attribute, SIZEOF_RESIDENT_ATTRIBUTE_HEADER );
  9422. AttributeSizeChange = SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  9423. //
  9424. // Else if it is nonresident, copy it here, set the compression parameter,
  9425. // and remember its size.
  9426. //
  9427. } else {
  9428. ASSERT( NtfsIsTypeCodeCompressible( Attribute->TypeCode ));
  9429. //
  9430. // Pad the allocation if the new type includes sparse or compressed and file is
  9431. // not sparse or compressed (non-resident only).
  9432. //
  9433. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ) &&
  9434. !FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE )) {
  9435. LONGLONG Temp;
  9436. ULONG CompressionUnitInClusters;
  9437. //
  9438. // If we are turning compression on, then we need to fill out the
  9439. // allocation of the compression unit containing file size, or else
  9440. // it will be interpreted as compressed when we fault it in. This
  9441. // is peanuts compared to the dual copies of clusters we keep around
  9442. // in the loop below when we rewrite the file. We don't do this
  9443. // work if the file is sparse because the allocation has already
  9444. // been rounded up.
  9445. //
  9446. CompressionUnitInClusters = 1 << NewCompressionUnitShift;
  9447. Temp = LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart );
  9448. //
  9449. // If FileSize is not already at a cluster boundary, then add
  9450. // allocation.
  9451. //
  9452. if ((ULONG) Temp & (CompressionUnitInClusters - 1)) {
  9453. NtfsAddAllocation( IrpContext,
  9454. NULL,
  9455. Scb,
  9456. Temp,
  9457. CompressionUnitInClusters - ((ULONG)Temp & (CompressionUnitInClusters - 1)),
  9458. FALSE,
  9459. NULL );
  9460. //
  9461. // Update the duplicate info.
  9462. //
  9463. if (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA )) {
  9464. Scb->Fcb->Info.AllocatedLength = Scb->TotalAllocated;
  9465. SetFlag( Scb->Fcb->InfoFlags, FCB_INFO_CHANGED_ALLOC_SIZE );
  9466. UpdateDuplicate = TRUE;
  9467. }
  9468. NtfsWriteFileSizes( IrpContext,
  9469. Scb,
  9470. &Scb->Header.ValidDataLength.QuadPart,
  9471. FALSE,
  9472. TRUE,
  9473. TRUE );
  9474. //
  9475. // The attribute may have moved. We will cleanup the attribute
  9476. // context and look it up again.
  9477. //
  9478. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  9479. NtfsInitializeAttributeContext( &AttrContext );
  9480. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  9481. NtfsPinMappedAttribute( IrpContext, Vcb, &AttrContext );
  9482. Attribute = NtfsFoundAttribute( &AttrContext );
  9483. }
  9484. }
  9485. AttributeSizeChange = Attribute->Form.Nonresident.MappingPairsOffset;
  9486. if (Attribute->NameOffset != 0) {
  9487. AttributeSizeChange = Attribute->NameOffset;
  9488. }
  9489. RtlCopyMemory( &NewAttribute, Attribute, AttributeSizeChange );
  9490. }
  9491. //
  9492. // Set the new attribute flags.
  9493. //
  9494. NewAttribute.Flags = NewAttributeFlags;
  9495. //
  9496. // Now, log the changed attribute.
  9497. //
  9498. (VOID)NtfsWriteLog( IrpContext,
  9499. Vcb->MftScb,
  9500. NtfsFoundBcb( &AttrContext ),
  9501. UpdateResidentValue,
  9502. &NewAttribute,
  9503. AttributeSizeChange,
  9504. UpdateResidentValue,
  9505. Attribute,
  9506. AttributeSizeChange,
  9507. NtfsMftOffset( &AttrContext ),
  9508. PtrOffset( NtfsContainingFileRecord( &AttrContext ), Attribute),
  9509. 0,
  9510. Vcb->BytesPerFileRecordSegment );
  9511. //
  9512. // Change the attribute by calling the same routine called at restart.
  9513. //
  9514. NtfsRestartChangeValue( IrpContext,
  9515. NtfsContainingFileRecord( &AttrContext ),
  9516. PtrOffset( NtfsContainingFileRecord( &AttrContext ), Attribute ),
  9517. 0,
  9518. &NewAttribute,
  9519. AttributeSizeChange,
  9520. FALSE );
  9521. //
  9522. // See if we need to either add or remove a total allocated field.
  9523. //
  9524. if (ChangeTotalAllocated) {
  9525. NtfsSetTotalAllocatedField( IrpContext,
  9526. Scb,
  9527. (USHORT) FlagOn( NewAttributeFlags,
  9528. ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ));
  9529. }
  9530. //
  9531. // If this is the main stream for a file we want to change the file attribute
  9532. // for this stream in both the standard information and duplicate
  9533. // information structure.
  9534. //
  9535. if (ChangeCompression &&
  9536. (FlagOn( Scb->ScbState, SCB_STATE_UNNAMED_DATA ) ||
  9537. (Attribute->TypeCode == $INDEX_ALLOCATION))) {
  9538. if (FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9539. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9540. } else {
  9541. ClearFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9542. }
  9543. ASSERTMSG( "conflict with flush",
  9544. NtfsIsSharedFcb( Fcb ) ||
  9545. (Fcb->PagingIoResource != NULL &&
  9546. NtfsIsSharedFcbPagingIo( Fcb )) );
  9547. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  9548. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  9549. UpdateDuplicate = TRUE;
  9550. }
  9551. if (ChangeSparse &&
  9552. FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_SPARSE ) &&
  9553. !FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE )) {
  9554. ASSERTMSG( "conflict with flush",
  9555. NtfsIsSharedFcb( Fcb ) ||
  9556. (Fcb->PagingIoResource != NULL &&
  9557. NtfsIsSharedFcbPagingIo( Fcb )) );
  9558. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  9559. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  9560. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  9561. UpdateDuplicate = TRUE;
  9562. }
  9563. if (ChangeEncryption &&
  9564. FlagOn( NewAttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED ) &&
  9565. !FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_ENCRYPTED )) {
  9566. ASSERTMSG( "conflict with flush",
  9567. NtfsIsSharedFcb( Fcb ) ||
  9568. (Fcb->PagingIoResource != NULL &&
  9569. NtfsIsSharedFcbPagingIo( Fcb )) );
  9570. SetFlag( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_ENCRYPTED );
  9571. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_FILE_ATTR );
  9572. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  9573. UpdateDuplicate = TRUE;
  9574. }
  9575. //
  9576. // Now put the new compression values in the Scb.
  9577. //
  9578. Scb->CompressionUnit = NewCompressionUnit;
  9579. Scb->CompressionUnitShift = NewCompressionUnitShift;
  9580. Scb->AttributeFlags = NewAttributeFlags;
  9581. } finally {
  9582. DebugUnwind( NtfsModifyAttributeFlags );
  9583. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  9584. }
  9585. return UpdateDuplicate;
  9586. }
  9587. PFCB
  9588. NtfsInitializeFileInExtendDirectory (
  9589. IN PIRP_CONTEXT IrpContext,
  9590. IN PVCB Vcb,
  9591. IN PCUNICODE_STRING FileName,
  9592. IN BOOLEAN ViewIndex,
  9593. IN ULONG CreateIfNotExist
  9594. )
  9595. /*++
  9596. Routine Description:
  9597. This routine creates/opens a file in the $Extend directory, by file name,
  9598. and returns an Fcb for the file.
  9599. Arguments:
  9600. Vcb - Pointer to the Vcb for the volume
  9601. FileName - Name of file to create in extend directory
  9602. ViewIndex - Indicates that the file is a view index.
  9603. CreateIfNotExist - Supplies TRUE if file should be created if it does not
  9604. already exist, or FALSE if file should not be created.
  9605. Return Value:
  9606. Fcb file existed or was created, NULL if file did not exist and was not created.
  9607. --*/
  9608. {
  9609. struct {
  9610. FILE_NAME FileName;
  9611. WCHAR FileNameChars[10];
  9612. } FileNameAttr;
  9613. FILE_REFERENCE FileReference;
  9614. LONGLONG FileRecordOffset;
  9615. PINDEX_ENTRY IndexEntry;
  9616. PBCB FileRecordBcb = NULL;
  9617. PBCB IndexEntryBcb = NULL;
  9618. PBCB ParentSecurityBcb = NULL;
  9619. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  9620. UCHAR FileNameFlags;
  9621. BOOLEAN FoundEntry;
  9622. PFCB ExtendFcb = Vcb->ExtendDirectory->Fcb;
  9623. PFCB Fcb = NULL;
  9624. BOOLEAN AcquiredFcbTable = FALSE;
  9625. BOOLEAN ReturnedExistingFcb = TRUE;
  9626. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  9627. PAGED_CODE();
  9628. ASSERT( NtfsIsExclusiveScb( Vcb->ExtendDirectory ) );
  9629. //
  9630. // Initialize the FileName.
  9631. //
  9632. ASSERT((FileName->Length / sizeof( WCHAR )) <= 10);
  9633. RtlZeroMemory( &FileNameAttr, sizeof(FileNameAttr) );
  9634. FileNameAttr.FileName.ParentDirectory = ExtendFcb->FileReference;
  9635. FileNameAttr.FileName.FileNameLength = (UCHAR)(FileName->Length / sizeof( WCHAR ));
  9636. RtlCopyMemory( FileNameAttr.FileName.FileName, FileName->Buffer, FileName->Length );
  9637. NtfsInitializeAttributeContext( &Context );
  9638. try {
  9639. //
  9640. // Does the file already exist?
  9641. //
  9642. FoundEntry = NtfsFindIndexEntry( IrpContext,
  9643. Vcb->ExtendDirectory,
  9644. &FileNameAttr,
  9645. FALSE,
  9646. NULL,
  9647. &IndexEntryBcb,
  9648. &IndexEntry,
  9649. NULL );
  9650. //
  9651. // Only procede if we either found the file or are supposed to create it.
  9652. //
  9653. if (FoundEntry || CreateIfNotExist) {
  9654. //
  9655. // If we did not find it, then start creating the file.
  9656. //
  9657. if (!FoundEntry) {
  9658. //
  9659. // We will now try to do all of the on-disk operations. This means first
  9660. // allocating and initializing an Mft record. After that we create
  9661. // an Fcb to use to access this record.
  9662. //
  9663. FileReference = NtfsAllocateMftRecord( IrpContext, Vcb, FALSE );
  9664. //
  9665. // Pin the file record we need.
  9666. //
  9667. NtfsPinMftRecord( IrpContext,
  9668. Vcb,
  9669. &FileReference,
  9670. TRUE,
  9671. &FileRecordBcb,
  9672. &FileRecord,
  9673. &FileRecordOffset );
  9674. //
  9675. // Initialize the file record header.
  9676. //
  9677. NtfsInitializeMftRecord( IrpContext,
  9678. Vcb,
  9679. &FileReference,
  9680. FileRecord,
  9681. FileRecordBcb,
  9682. FALSE );
  9683. //
  9684. // If we found the file, then just get its FileReference out of the
  9685. // IndexEntry.
  9686. //
  9687. } else {
  9688. FileReference = IndexEntry->FileReference;
  9689. }
  9690. //
  9691. // Now that we know the FileReference, we can create the Fcb.
  9692. //
  9693. NtfsAcquireFcbTable( IrpContext, Vcb );
  9694. AcquiredFcbTable = TRUE;
  9695. Fcb = NtfsCreateFcb( IrpContext,
  9696. Vcb,
  9697. FileReference,
  9698. FALSE,
  9699. ViewIndex,
  9700. &ReturnedExistingFcb );
  9701. //
  9702. // Reference the Fcb so it doesn't go away.
  9703. //
  9704. Fcb->ReferenceCount += 1;
  9705. NtfsReleaseFcbTable( IrpContext, Vcb );
  9706. AcquiredFcbTable = FALSE;
  9707. //
  9708. // Try to do a fast acquire, otherwise we need to release
  9709. // the parent extend directory and acquire in the canonical order
  9710. // child and then parent.
  9711. // Use AcquireWithPaging for don't wait functionality. Since the flag
  9712. // isn't set despite its name this will only acquire main
  9713. //
  9714. if (!NtfsAcquireFcbWithPaging( IrpContext, Fcb, ACQUIRE_DONT_WAIT )) {
  9715. NtfsReleaseScb( IrpContext, Vcb->ExtendDirectory );
  9716. NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, 0 );
  9717. NtfsAcquireExclusiveScb( IrpContext, Vcb->ExtendDirectory );
  9718. }
  9719. NtfsAcquireFcbTable( IrpContext, Vcb );
  9720. Fcb->ReferenceCount -= 1;
  9721. NtfsReleaseFcbTable( IrpContext, Vcb );
  9722. //
  9723. // If we are creating this file, then carry on.
  9724. //
  9725. if (!FoundEntry) {
  9726. BOOLEAN LogIt = FALSE;
  9727. //
  9728. // Just copy the Security Id from the parent.
  9729. //
  9730. NtfsAcquireFcbSecurity( Fcb->Vcb );
  9731. Fcb->SecurityId = ExtendFcb->SecurityId;
  9732. ASSERT( Fcb->SharedSecurity == NULL );
  9733. Fcb->SharedSecurity = ExtendFcb->SharedSecurity;
  9734. Fcb->SharedSecurity->ReferenceCount++;
  9735. NtfsReleaseFcbSecurity( Fcb->Vcb );
  9736. //
  9737. // The changes to make on disk are first to create a standard information
  9738. // attribute. We start by filling the Fcb with the information we
  9739. // know and creating the attribute on disk.
  9740. //
  9741. NtfsInitializeFcbAndStdInfo( IrpContext,
  9742. Fcb,
  9743. FALSE,
  9744. ViewIndex,
  9745. FALSE,
  9746. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  9747. NULL );
  9748. //
  9749. // Now link the file into the $Extend directory.
  9750. //
  9751. NtfsAddLink( IrpContext,
  9752. TRUE,
  9753. Vcb->ExtendDirectory,
  9754. Fcb,
  9755. (PFILE_NAME)&FileNameAttr,
  9756. &LogIt,
  9757. &FileNameFlags,
  9758. NULL,
  9759. NULL,
  9760. NULL );
  9761. //
  9762. // Set this flag to indicate that the file is to be locked via the Scb
  9763. // pointers in the Vcb.
  9764. //
  9765. SetFlag( FileRecord->Flags, FILE_SYSTEM_FILE );
  9766. //
  9767. // Log the file record.
  9768. //
  9769. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  9770. Vcb->MftScb,
  9771. FileRecordBcb,
  9772. InitializeFileRecordSegment,
  9773. FileRecord,
  9774. FileRecord->FirstFreeByte,
  9775. Noop,
  9776. NULL,
  9777. 0,
  9778. FileRecordOffset,
  9779. 0,
  9780. 0,
  9781. Vcb->BytesPerFileRecordSegment );
  9782. //
  9783. // Verify that the file record for this file is valid.
  9784. //
  9785. } else {
  9786. ULONG CorruptHint;
  9787. if (!NtfsLookupAttributeByCode( IrpContext,
  9788. Fcb,
  9789. &Fcb->FileReference,
  9790. $STANDARD_INFORMATION,
  9791. &Context ) ||
  9792. !NtfsCheckFileRecord( Vcb, NtfsContainingFileRecord( &Context ), &Fcb->FileReference, &CorruptHint )) {
  9793. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, &Fcb->FileReference, NULL );
  9794. }
  9795. }
  9796. //
  9797. // Update Fcb fields from disk.
  9798. //
  9799. SetFlag( Fcb->FcbState, FCB_STATE_SYSTEM_FILE );
  9800. NtfsUpdateFcbInfoFromDisk( IrpContext, TRUE, Fcb, NULL );
  9801. }
  9802. } finally {
  9803. NtfsCleanupAttributeContext( IrpContext, &Context );
  9804. NtfsUnpinBcb( IrpContext, &FileRecordBcb );
  9805. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  9806. NtfsUnpinBcb( IrpContext, &ParentSecurityBcb );
  9807. //
  9808. // On any kind of error, nuke the Fcb.
  9809. //
  9810. if (AbnormalTermination()) {
  9811. //
  9812. // If some error caused us to abort, then delete
  9813. // the Fcb, because we are the only ones who will.
  9814. //
  9815. if (!ReturnedExistingFcb && Fcb) {
  9816. if (!AcquiredFcbTable) {
  9817. NtfsAcquireFcbTable( IrpContext, Vcb );
  9818. AcquiredFcbTable = TRUE;
  9819. }
  9820. NtfsDeleteFcb( IrpContext, &Fcb, &AcquiredFcbTable );
  9821. ASSERT(!AcquiredFcbTable);
  9822. }
  9823. if (AcquiredFcbTable) {
  9824. NtfsReleaseFcbTable( IrpContext, Vcb );
  9825. }
  9826. }
  9827. }
  9828. return Fcb;
  9829. }
  9830. VOID
  9831. NtfsFillBasicInfo (
  9832. OUT PFILE_BASIC_INFORMATION Buffer,
  9833. IN PSCB Scb
  9834. )
  9835. /*++
  9836. Routine Description:
  9837. This is the common routine which transfers data from the Scb/Fcb to the BasicInfo structure.
  9838. Arguments:
  9839. Buffer - Pointer to structure to fill in. Our caller has already validated it.
  9840. Scb - Stream the caller has a handle to.
  9841. Return Value:
  9842. None
  9843. --*/
  9844. {
  9845. PFCB Fcb = Scb->Fcb;
  9846. PAGED_CODE();
  9847. //
  9848. // Zero the output buffer.
  9849. //
  9850. RtlZeroMemory( Buffer, sizeof( FILE_BASIC_INFORMATION ));
  9851. //
  9852. // Fill in the basic information fields
  9853. //
  9854. Buffer->CreationTime.QuadPart = Fcb->Info.CreationTime;
  9855. Buffer->LastWriteTime.QuadPart = Fcb->Info.LastModificationTime;
  9856. Buffer->ChangeTime.QuadPart = Fcb->Info.LastChangeTime;
  9857. Buffer->LastAccessTime.QuadPart = Fcb->CurrentLastAccess;
  9858. //
  9859. // Capture the attributes from the Fcb except for the stream specific values.
  9860. // Also mask out any private Ntfs attribute flags.
  9861. //
  9862. Buffer->FileAttributes = Fcb->Info.FileAttributes;
  9863. ClearFlag( Buffer->FileAttributes,
  9864. (~FILE_ATTRIBUTE_VALID_FLAGS |
  9865. FILE_ATTRIBUTE_COMPRESSED |
  9866. FILE_ATTRIBUTE_TEMPORARY |
  9867. FILE_ATTRIBUTE_SPARSE_FILE |
  9868. FILE_ATTRIBUTE_ENCRYPTED) );
  9869. //
  9870. // Pick up the sparse, encrypted and temp bits for this stream from the Scb.
  9871. //
  9872. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  9873. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  9874. }
  9875. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  9876. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_ENCRYPTED );
  9877. }
  9878. if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
  9879. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
  9880. }
  9881. //
  9882. // If this is an index stream then mark it as a directory. Capture the compressed
  9883. // state from either the Fcb or Scb.
  9884. //
  9885. if (Scb->AttributeTypeCode == $INDEX_ALLOCATION) {
  9886. if (IsDirectory( &Fcb->Info ) || IsViewIndex( &Fcb->Info )) {
  9887. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
  9888. //
  9889. // Capture the compression state from the Fcb.
  9890. //
  9891. SetFlag( Buffer->FileAttributes,
  9892. FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED ));
  9893. //
  9894. // Otherwise capture the value in the Scb itself.
  9895. //
  9896. } else if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9897. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9898. }
  9899. //
  9900. // In all other cases we can use the value in the Scb.
  9901. //
  9902. } else {
  9903. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  9904. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  9905. }
  9906. }
  9907. //
  9908. // If there are no flags set then explicitly set the NORMAL flag.
  9909. //
  9910. if (Buffer->FileAttributes == 0) {
  9911. Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  9912. }
  9913. return;
  9914. }
  9915. VOID
  9916. NtfsFillStandardInfo (
  9917. OUT PFILE_STANDARD_INFORMATION Buffer,
  9918. IN PSCB Scb,
  9919. IN PCCB Ccb OPTIONAL
  9920. )
  9921. /*++
  9922. Routine Description:
  9923. This is the common routine which transfers data from the Scb/Fcb to the StandardInfo structure.
  9924. Arguments:
  9925. Buffer - Pointer to structure to fill in. Our caller has already validated it.
  9926. Scb - Stream the caller has a handle to.
  9927. Ccb - Ccb for the user's open.
  9928. Return Value:
  9929. None
  9930. --*/
  9931. {
  9932. PFCB Fcb = Scb->Fcb;
  9933. PAGED_CODE();
  9934. //
  9935. // Zero out the output buffer.
  9936. //
  9937. RtlZeroMemory( Buffer, sizeof( FILE_STANDARD_INFORMATION ));
  9938. //
  9939. // Fill in the buffer from the Scb, Fcb and Ccb.
  9940. //
  9941. //
  9942. // Return sizes only for non-index streams.
  9943. //
  9944. if ((Scb->AttributeTypeCode != $INDEX_ALLOCATION) ||
  9945. (!IsDirectory( &Fcb->Info ) && !IsViewIndex( &Fcb->Info ))) {
  9946. Buffer->AllocationSize.QuadPart = Scb->TotalAllocated;
  9947. Buffer->EndOfFile = Scb->Header.FileSize;
  9948. }
  9949. Buffer->NumberOfLinks = Fcb->LinkCount;
  9950. //
  9951. // Let's initialize these boolean fields.
  9952. //
  9953. Buffer->DeletePending = Buffer->Directory = FALSE;
  9954. //
  9955. // Get the delete and directory flags from the Fcb/Scb state. Note that
  9956. // the sense of the delete pending bit refers to the file if opened as
  9957. // file. Otherwise it refers to the attribute only.
  9958. //
  9959. // But only do the test if the Ccb has been supplied.
  9960. //
  9961. if (ARGUMENT_PRESENT( Ccb )) {
  9962. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  9963. if ((Scb->Fcb->LinkCount == 0) ||
  9964. ((Ccb->Lcb != NULL) && FlagOn( Ccb->Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE ))) {
  9965. Buffer->DeletePending = TRUE;
  9966. }
  9967. Buffer->Directory = BooleanIsDirectory( &Scb->Fcb->Info );
  9968. } else {
  9969. Buffer->DeletePending = BooleanFlagOn( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
  9970. }
  9971. } else {
  9972. Buffer->Directory = BooleanIsDirectory( &Scb->Fcb->Info );
  9973. }
  9974. return;
  9975. }
  9976. VOID
  9977. NtfsFillNetworkOpenInfo (
  9978. OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
  9979. IN PSCB Scb
  9980. )
  9981. /*++
  9982. Routine Description:
  9983. This is the common routine which transfers data from the Scb/Fcb to the NetworkOpenInfo structure.
  9984. Arguments:
  9985. Buffer - Pointer to structure to fill in. Our caller has already validated it.
  9986. Scb - Stream the caller has a handle to.
  9987. Return Value:
  9988. None
  9989. --*/
  9990. {
  9991. PFCB Fcb = Scb->Fcb;
  9992. PAGED_CODE();
  9993. //
  9994. // Zero the output buffer.
  9995. //
  9996. RtlZeroMemory( Buffer, sizeof( FILE_NETWORK_OPEN_INFORMATION ));
  9997. //
  9998. // Fill in the basic information fields
  9999. //
  10000. Buffer->CreationTime.QuadPart = Fcb->Info.CreationTime;
  10001. Buffer->LastWriteTime.QuadPart = Fcb->Info.LastModificationTime;
  10002. Buffer->ChangeTime.QuadPart = Fcb->Info.LastChangeTime;
  10003. Buffer->LastAccessTime.QuadPart = Fcb->CurrentLastAccess;
  10004. //
  10005. // Capture the attributes from the Fcb except for the stream specific values.
  10006. // Also mask out any private Ntfs attribute flags.
  10007. //
  10008. Buffer->FileAttributes = Fcb->Info.FileAttributes;
  10009. ClearFlag( Buffer->FileAttributes,
  10010. (~FILE_ATTRIBUTE_VALID_FLAGS |
  10011. FILE_ATTRIBUTE_COMPRESSED |
  10012. FILE_ATTRIBUTE_TEMPORARY |
  10013. FILE_ATTRIBUTE_SPARSE_FILE |
  10014. FILE_ATTRIBUTE_ENCRYPTED) );
  10015. //
  10016. // Pick up the sparse, encrypted and temp bits for this stream from the Scb.
  10017. //
  10018. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  10019. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_SPARSE_FILE );
  10020. }
  10021. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_ENCRYPTED )) {
  10022. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_ENCRYPTED );
  10023. }
  10024. if (FlagOn( Scb->ScbState, SCB_STATE_TEMPORARY )) {
  10025. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
  10026. }
  10027. //
  10028. // If this is an index stream then mark it as a directory. Capture the compressed
  10029. // state from either the Fcb or Scb.
  10030. //
  10031. if (Scb->AttributeTypeCode == $INDEX_ALLOCATION) {
  10032. if (IsDirectory( &Fcb->Info ) || IsViewIndex( &Fcb->Info )) {
  10033. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY );
  10034. //
  10035. // Capture the compression state from the Fcb.
  10036. //
  10037. SetFlag( Buffer->FileAttributes,
  10038. FlagOn( Fcb->Info.FileAttributes, FILE_ATTRIBUTE_COMPRESSED ));
  10039. //
  10040. // Otherwise capture the value in the Scb itself.
  10041. //
  10042. } else if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  10043. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  10044. }
  10045. //
  10046. // In all other cases we can use the value in the Scb.
  10047. //
  10048. } else {
  10049. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
  10050. SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_COMPRESSED );
  10051. }
  10052. //
  10053. // In the non-index case we use the sizes from the Scb.
  10054. //
  10055. Buffer->AllocationSize.QuadPart = Scb->TotalAllocated;
  10056. Buffer->EndOfFile = Scb->Header.FileSize;
  10057. }
  10058. //
  10059. // If there are no flags set then explicitly set the NORMAL flag.
  10060. //
  10061. if (Buffer->FileAttributes == 0) {
  10062. Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
  10063. }
  10064. return;
  10065. }
  10066. //
  10067. // Internal support routine
  10068. //
  10069. BOOLEAN
  10070. NtfsLookupInFileRecord (
  10071. IN PIRP_CONTEXT IrpContext,
  10072. IN PFCB Fcb,
  10073. IN PFILE_REFERENCE BaseFileReference OPTIONAL,
  10074. IN ATTRIBUTE_TYPE_CODE QueriedTypeCode,
  10075. IN PCUNICODE_STRING QueriedName OPTIONAL,
  10076. IN PVCN Vcn OPTIONAL,
  10077. IN BOOLEAN IgnoreCase,
  10078. IN PVOID QueriedValue OPTIONAL,
  10079. IN ULONG QueriedValueLength,
  10080. OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10081. )
  10082. /*++
  10083. Routine Description:
  10084. This routine attempts to find the fist occurrence of an attribute with
  10085. the specified AttributeTypeCode and the specified QueriedName in the
  10086. specified BaseFileReference. If we find one, its attribute record is
  10087. pinned and returned.
  10088. Arguments:
  10089. Fcb - Requested file.
  10090. BaseFileReference - The base entry for this file in the MFT. Only needed
  10091. on initial invocation.
  10092. QueriedTypeCode - The attribute code to search for, if present.
  10093. QueriedName - The attribute name to search for, if present.
  10094. Vcn - Search for the nonresident attribute instance that has this Vcn
  10095. IgnoreCase - Ignore case while comparing names. Ignored if QueriedName
  10096. not present.
  10097. QueriedValue - The actual attribute value to search for, if present.
  10098. QueriedValueLength - The length of the attribute value to search for.
  10099. Ignored if QueriedValue is not present.
  10100. Context - Describes the prior found attribute on invocation (if
  10101. this was not the initial enumeration), and contains the next found
  10102. attribute on return.
  10103. Return Value:
  10104. BOOLEAN - True if we found an attribute, false otherwise.
  10105. --*/
  10106. {
  10107. PATTRIBUTE_RECORD_HEADER Attribute;
  10108. BOOLEAN Result = FALSE;
  10109. PAGED_CODE();
  10110. DebugTrace( +1, Dbg, ("NtfsLookupInFileRecord\n") );
  10111. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10112. DebugTrace( 0, Dbg, ("BaseFileReference = %08I64x\n",
  10113. ARGUMENT_PRESENT(BaseFileReference) ?
  10114. NtfsFullSegmentNumber( BaseFileReference ) :
  10115. 0xFFFFFFFFFFFF) );
  10116. DebugTrace( 0, Dbg, ("QueriedTypeCode = %08lx\n", QueriedTypeCode) );
  10117. DebugTrace( 0, Dbg, ("QueriedName = %08lx\n", QueriedName) );
  10118. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  10119. DebugTrace( 0, Dbg, ("QueriedValue = %08lx\n", QueriedValue) );
  10120. DebugTrace( 0, Dbg, ("QueriedValueLength = %08lx\n", QueriedValueLength) );
  10121. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10122. //
  10123. // Is this the initial enumeration? If so start at the beginning.
  10124. //
  10125. if (Context->FoundAttribute.Bcb == NULL) {
  10126. PBCB Bcb;
  10127. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10128. PATTRIBUTE_RECORD_HEADER TempAttribute;
  10129. ASSERT(!ARGUMENT_PRESENT(QueriedName) || !ARGUMENT_PRESENT(QueriedValue));
  10130. NtfsReadFileRecord( IrpContext,
  10131. Fcb->Vcb,
  10132. BaseFileReference,
  10133. &Bcb,
  10134. &FileRecord,
  10135. &TempAttribute,
  10136. &Context->FoundAttribute.MftFileOffset );
  10137. Attribute = TempAttribute;
  10138. //
  10139. // Initialize the found attribute context
  10140. //
  10141. Context->FoundAttribute.Bcb = Bcb;
  10142. Context->FoundAttribute.FileRecord = FileRecord;
  10143. //
  10144. // And show that we have neither found nor used the External
  10145. // Attributes List attribute.
  10146. //
  10147. Context->AttributeList.Bcb = NULL;
  10148. Context->AttributeList.AttributeList = NULL;
  10149. //
  10150. // The Usn Journal support uses the Usn Journal Fcb to look up $STANDARD_INFORMATION
  10151. // in an arbitrary file. We will detect the case of $STANDARD_INFORMATION and the
  10152. // "wrong" Fcb and get out.
  10153. //
  10154. if (ARGUMENT_PRESENT( BaseFileReference ) &&
  10155. !NtfsEqualMftRef( BaseFileReference, &Fcb->FileReference ) &&
  10156. (QueriedTypeCode == $STANDARD_INFORMATION) &&
  10157. (Attribute->TypeCode == $STANDARD_INFORMATION)) {
  10158. //
  10159. // We found it. Return it in the enumeration context.
  10160. //
  10161. Context->FoundAttribute.Attribute = Attribute;
  10162. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10163. Attribute ));
  10164. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (No code or SI)\n") );
  10165. try_return( Result = TRUE );
  10166. }
  10167. //
  10168. // Scan to see if there is an attribute list, and if so, defer
  10169. // immediately to NtfsLookupExternalAttribute - we must guide the
  10170. // enumeration by the attribute list.
  10171. //
  10172. while (TempAttribute->TypeCode <= $ATTRIBUTE_LIST) {
  10173. if (TempAttribute->RecordLength == 0) {
  10174. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10175. }
  10176. if (TempAttribute->TypeCode == $ATTRIBUTE_LIST) {
  10177. ULONG AttributeListLength;
  10178. PATTRIBUTE_LIST_CONTEXT Ex = &Context->AttributeList;
  10179. Context->FoundAttribute.Attribute = TempAttribute;
  10180. if ((QueriedTypeCode != $UNUSED) &&
  10181. (QueriedTypeCode == $ATTRIBUTE_LIST)) {
  10182. //
  10183. // We found it. Return it in the enumeration context.
  10184. //
  10185. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10186. TempAttribute) );
  10187. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (attribute list)\n") );
  10188. try_return( Result = TRUE );
  10189. }
  10190. //
  10191. // Build up the context for the attribute list by hand here
  10192. // for efficiency, so that we can call NtfsMapAttributeValue.
  10193. //
  10194. Ex->AttributeList = TempAttribute;
  10195. NtfsMapAttributeValue( IrpContext,
  10196. Fcb,
  10197. (PVOID *)&Ex->FirstEntry,
  10198. &AttributeListLength,
  10199. &Ex->Bcb,
  10200. Context );
  10201. Ex->Entry = Ex->FirstEntry;
  10202. Ex->BeyondFinalEntry = Add2Ptr( Ex->FirstEntry, AttributeListLength );
  10203. //
  10204. // If the list is non-resident then remember the correct Bcb for
  10205. // the list.
  10206. //
  10207. if (!NtfsIsAttributeResident( TempAttribute )) {
  10208. Ex->NonresidentListBcb = Ex->Bcb;
  10209. Ex->Bcb = Context->FoundAttribute.Bcb;
  10210. Context->FoundAttribute.Bcb = NULL;
  10211. //
  10212. // Otherwise unpin the Bcb for the current attribute.
  10213. //
  10214. } else {
  10215. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10216. }
  10217. //
  10218. // We are now ready to iterate through the external attributes.
  10219. // The Context->FoundAttribute.Bcb being NULL signals
  10220. // NtfsLookupExternalAttribute that is should start at
  10221. // Context->External.Entry instead of the entry immediately following.
  10222. //
  10223. Result = NtfsLookupExternalAttribute( IrpContext,
  10224. Fcb,
  10225. QueriedTypeCode,
  10226. QueriedName,
  10227. Vcn,
  10228. IgnoreCase,
  10229. QueriedValue,
  10230. QueriedValueLength,
  10231. Context );
  10232. try_return( NOTHING );
  10233. }
  10234. TempAttribute = NtfsGetNextRecord( TempAttribute );
  10235. NtfsCheckRecordBound( TempAttribute, FileRecord, Fcb->Vcb->BytesPerFileRecordSegment );
  10236. }
  10237. if ((QueriedTypeCode == $UNUSED) ||
  10238. ((QueriedTypeCode == $STANDARD_INFORMATION) &&
  10239. (Attribute->TypeCode == $STANDARD_INFORMATION))) {
  10240. //
  10241. // We found it. Return it in the enumeration context.
  10242. //
  10243. Context->FoundAttribute.Attribute = Attribute;
  10244. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10245. Attribute ));
  10246. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (No code or SI)\n") );
  10247. try_return( Result = TRUE );
  10248. }
  10249. } else {
  10250. //
  10251. // Special case if the prior found attribute was $END, this is
  10252. // because we cannot search for the next entry after $END.
  10253. //
  10254. Attribute = Context->FoundAttribute.Attribute;
  10255. if (!Context->FoundAttribute.AttributeDeleted) {
  10256. Attribute = NtfsGetNextRecord( Attribute );
  10257. }
  10258. NtfsCheckRecordBound( Attribute, Context->FoundAttribute.FileRecord, Fcb->Vcb->BytesPerFileRecordSegment );
  10259. Context->FoundAttribute.AttributeDeleted = FALSE;
  10260. if (Attribute->TypeCode == $END) {
  10261. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> FALSE ($END)\n") );
  10262. try_return( Result = FALSE );
  10263. }
  10264. if (Attribute->RecordLength == 0) {
  10265. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10266. }
  10267. if (QueriedTypeCode == $UNUSED) {
  10268. //
  10269. // We found it. Return it in the enumeration context.
  10270. //
  10271. Context->FoundAttribute.Attribute = Attribute;
  10272. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10273. Attribute) );
  10274. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord -> TRUE (No code)\n") );
  10275. try_return( Result = TRUE );
  10276. }
  10277. }
  10278. Result = NtfsFindInFileRecord( IrpContext,
  10279. Attribute,
  10280. &Context->FoundAttribute.Attribute,
  10281. QueriedTypeCode,
  10282. QueriedName,
  10283. IgnoreCase,
  10284. QueriedValue,
  10285. QueriedValueLength );
  10286. try_exit: NOTHING;
  10287. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord ->\n") );
  10288. return Result;
  10289. }
  10290. //
  10291. // Internal support routine
  10292. //
  10293. BOOLEAN
  10294. NtfsFindInFileRecord (
  10295. IN PIRP_CONTEXT IrpContext,
  10296. IN PATTRIBUTE_RECORD_HEADER Attribute,
  10297. OUT PATTRIBUTE_RECORD_HEADER *ReturnAttribute,
  10298. IN ATTRIBUTE_TYPE_CODE QueriedTypeCode,
  10299. IN PCUNICODE_STRING QueriedName OPTIONAL,
  10300. IN BOOLEAN IgnoreCase,
  10301. IN PVOID QueriedValue OPTIONAL,
  10302. IN ULONG QueriedValueLength
  10303. )
  10304. /*++
  10305. Routine Description:
  10306. This routine looks up an attribute in a file record. It returns
  10307. TRUE if the attribute was found, or FALSE if not found. If FALSE
  10308. is returned, the return attribute pointer points to the spot where
  10309. the described attribute should be inserted. Thus this routine
  10310. determines how attributes are collated within file records.
  10311. Arguments:
  10312. Attribute - The attribute within the file record at which the search
  10313. should begin.
  10314. ReturnAttribute - Pointer to the found attribute if returning TRUE,
  10315. or to the position to insert the attribute if returning
  10316. FALSE.
  10317. QueriedTypeCode - The attribute code to search for, if present.
  10318. QueriedName - The attribute name to search for, if present.
  10319. IgnoreCase - Ignore case while comparing names. Ignored if QueriedName
  10320. not present.
  10321. QueriedValue - The actual attribute value to search for, if present.
  10322. QueriedValueLength - The length of the attribute value to search for.
  10323. Ignored if QueriedValue is not present.
  10324. Return Value:
  10325. BOOLEAN - True if we found an attribute, false otherwise.
  10326. --*/
  10327. {
  10328. PWCH UpcaseTable = IrpContext->Vcb->UpcaseTable;
  10329. ULONG UpcaseTableSize = IrpContext->Vcb->UpcaseTableSize;
  10330. PAGED_CODE();
  10331. //
  10332. // Now walk through the base file record looking for the atttribute. If
  10333. // the query is "exhausted", i.e., if a type code, attribute name, or
  10334. // value is encountered which is greater than the one we are querying for,
  10335. // then we return FALSE immediately out of this loop. If an exact match
  10336. // is seen, we break, and return the match at the end of this routine.
  10337. // Otherwise we keep looping while the query is not exhausted.
  10338. //
  10339. // IMPORTANT NOTE:
  10340. //
  10341. // The exact semantics of this loop are important, as they determine the
  10342. // exact details of attribute ordering within the file record. A change
  10343. // in the order of the tests within this loop CHANGES THE FILE STRUCTURE,
  10344. // and possibly makes older NTFS volumes unreadable.
  10345. //
  10346. while ( TRUE ) {
  10347. //
  10348. // Mark this attribute position, since we may be returning TRUE
  10349. // or FALSE below.
  10350. //
  10351. *ReturnAttribute = Attribute;
  10352. //
  10353. // Leave with the correct current position intact, if we hit the
  10354. // end or a greater attribute type code.
  10355. //
  10356. // COLLATION RULE:
  10357. //
  10358. // Attributes are ordered by increasing attribute type code.
  10359. //
  10360. if (QueriedTypeCode < Attribute->TypeCode) {
  10361. DebugTrace( -1, Dbg, ("NtfsLookupInFileRecord->FALSE (Type Code)\n") );
  10362. return FALSE;
  10363. }
  10364. if (Attribute->RecordLength == 0) {
  10365. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
  10366. }
  10367. //
  10368. // If the attribute type code is a match, then need to check either
  10369. // the name or the value or return a match.
  10370. //
  10371. // COLLATION RULE:
  10372. //
  10373. // Within equal attribute type codes, attribute names are ordered
  10374. // by increasing lexigraphical order ignoring case. If two names
  10375. // exist which are equal when case is ignored, they must not be
  10376. // equal when compared with exact case, and within such equal
  10377. // names they are ordered by increasing lexical value with exact
  10378. // case.
  10379. //
  10380. if (QueriedTypeCode == Attribute->TypeCode) {
  10381. //
  10382. // Handle name-match case
  10383. //
  10384. if (ARGUMENT_PRESENT(QueriedName)) {
  10385. UNICODE_STRING AttributeName;
  10386. FSRTL_COMPARISON_RESULT Result;
  10387. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  10388. //
  10389. // See if we have a name match.
  10390. //
  10391. if (NtfsAreNamesEqual( UpcaseTable,
  10392. &AttributeName,
  10393. QueriedName,
  10394. IgnoreCase )) {
  10395. break;
  10396. }
  10397. //
  10398. // Compare the names ignoring case.
  10399. //
  10400. Result = NtfsCollateNames( UpcaseTable,
  10401. UpcaseTableSize,
  10402. QueriedName,
  10403. &AttributeName,
  10404. GreaterThan,
  10405. TRUE);
  10406. //
  10407. // Break out if the result is LessThan, or if the result
  10408. // is Equal to *and* the exact case compare yields LessThan.
  10409. //
  10410. if ((Result == LessThan) || ((Result == EqualTo) &&
  10411. (NtfsCollateNames( UpcaseTable,
  10412. UpcaseTableSize,
  10413. QueriedName,
  10414. &AttributeName,
  10415. GreaterThan,
  10416. FALSE) == LessThan))) {
  10417. return FALSE;
  10418. }
  10419. //
  10420. // Handle value-match case
  10421. //
  10422. // COLLATION RULE:
  10423. //
  10424. // Values are collated by increasing values with unsigned-byte
  10425. // compares. I.e., the first different byte is compared unsigned,
  10426. // and the value with the highest byte comes second. If a shorter
  10427. // value is exactly equal to the first part of a longer value, then
  10428. // the shorter value comes first.
  10429. //
  10430. // Note that for values which are actually Unicode strings, the
  10431. // collation is different from attribute name ordering above. However,
  10432. // attribute ordering is visible outside the file system (you can
  10433. // query "openable" attributes), whereas the ordering of indexed values
  10434. // is not visible (for example you cannot query links). In any event,
  10435. // the ordering of values must be considered up to the system, and
  10436. // *must* be considered nondetermistic from the standpoint of a user.
  10437. //
  10438. } else if (ARGUMENT_PRESENT( QueriedValue )) {
  10439. ULONG Diff, MinLength;
  10440. //
  10441. // Form the minimum of the ValueLength and the Attribute Value.
  10442. //
  10443. MinLength = Attribute->Form.Resident.ValueLength;
  10444. if (QueriedValueLength < MinLength) {
  10445. MinLength = QueriedValueLength;
  10446. }
  10447. //
  10448. // Find the first different byte.
  10449. //
  10450. Diff = (ULONG)RtlCompareMemory( QueriedValue,
  10451. NtfsGetValue(Attribute),
  10452. MinLength );
  10453. //
  10454. // The first substring was equal.
  10455. //
  10456. if (Diff == MinLength) {
  10457. //
  10458. // If the two lengths are equal, then we have an exact
  10459. // match.
  10460. //
  10461. if (QueriedValueLength == Attribute->Form.Resident.ValueLength) {
  10462. break;
  10463. }
  10464. //
  10465. // Otherwise the shorter guy comes first; we can return
  10466. // FALSE if the queried value is shorter.
  10467. //
  10468. if (QueriedValueLength < Attribute->Form.Resident.ValueLength) {
  10469. return FALSE;
  10470. }
  10471. //
  10472. // Otherwise some byte was different. Do an unsigned compare
  10473. // of that byte to determine the ordering. Time to leave if
  10474. // the queried value byte is less.
  10475. //
  10476. } else if (*((PUCHAR)QueriedValue + Diff) <
  10477. *((PUCHAR)NtfsGetValue(Attribute) + Diff)) {
  10478. return FALSE;
  10479. }
  10480. //
  10481. // Otherwise we have a simple match on code
  10482. //
  10483. } else {
  10484. break;
  10485. }
  10486. }
  10487. Attribute = NtfsGetNextRecord( Attribute );
  10488. NtfsCheckRecordBound( Attribute,
  10489. (ULONG_PTR)*ReturnAttribute & ~((ULONG_PTR)IrpContext->Vcb->BytesPerFileRecordSegment - 1),
  10490. IrpContext->Vcb->BytesPerFileRecordSegment );
  10491. }
  10492. return TRUE;
  10493. }
  10494. //
  10495. // Internal support routine
  10496. //
  10497. BOOLEAN
  10498. NtfsLookupExternalAttribute (
  10499. IN PIRP_CONTEXT IrpContext,
  10500. IN PFCB Fcb,
  10501. IN ATTRIBUTE_TYPE_CODE QueriedTypeCode,
  10502. IN PCUNICODE_STRING QueriedName OPTIONAL,
  10503. IN PVCN Vcn OPTIONAL,
  10504. IN BOOLEAN IgnoreCase,
  10505. IN PVOID QueriedValue OPTIONAL,
  10506. IN ULONG QueriedValueLength,
  10507. OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10508. )
  10509. /*++
  10510. Routine Description:
  10511. This routine attempts to find the first occurrence of an attribute with
  10512. the specified AttributeTypeCode and the specified QueriedName and Value
  10513. among the external attributes described by the Context. If we find one,
  10514. its attribute record is pinned and returned.
  10515. Arguments:
  10516. Fcb - Requested file.
  10517. QueriedTypeCode - The attribute code to search for, if present.
  10518. QueriedName - The attribute name to search for, if present.
  10519. Vcn - Lookup nonresident attribute instance with this Vcn
  10520. IgnoreCase - Ignore case while comparing names. Ignored if QueriedName
  10521. not present.
  10522. QueriedValue - The actual attribute value to search for, if present.
  10523. QueriedValueLength - The length of the attribute value to search for.
  10524. Ignored if QueriedValue is not present.
  10525. Context - Describes the prior found attribute on invocation (if
  10526. this was not the initial enumeration), and contains the next found
  10527. attribute on return.
  10528. Return Value:
  10529. BOOLEAN - True if we found an attribute, false otherwise.
  10530. --*/
  10531. {
  10532. PATTRIBUTE_LIST_ENTRY Entry, LastEntry;
  10533. PWCH UpcaseTable = IrpContext->Vcb->UpcaseTable;
  10534. ULONG UpcaseTableSize = IrpContext->Vcb->UpcaseTableSize;
  10535. BOOLEAN Terminating = FALSE;
  10536. BOOLEAN TerminateOnNext = FALSE;
  10537. PAGED_CODE();
  10538. DebugTrace( +1, Dbg, ("NtfsLookupExternalAttribute\n") );
  10539. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10540. DebugTrace( 0, Dbg, ("QueriedTypeCode = %08lx\n", QueriedTypeCode) );
  10541. DebugTrace( 0, Dbg, ("QueriedName = %08lx\n", QueriedName) );
  10542. DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) );
  10543. DebugTrace( 0, Dbg, ("QueriedValue = %08lx\n", QueriedValue) );
  10544. DebugTrace( 0, Dbg, ("QueriedValueLength = %08lx\n", QueriedValueLength) );
  10545. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10546. //
  10547. // Check that our list is kosher.
  10548. //
  10549. if ((Context->AttributeList.Entry >= Context->AttributeList.BeyondFinalEntry) &&
  10550. !Context->FoundAttribute.AttributeDeleted) {
  10551. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10552. }
  10553. //
  10554. // Is this the initial enumeration? If so start at the beginning.
  10555. //
  10556. LastEntry = NULL;
  10557. if (Context->FoundAttribute.Bcb == NULL) {
  10558. Entry = Context->AttributeList.Entry;
  10559. //
  10560. // Else set Entry and LastEntry appropriately.
  10561. //
  10562. } else if (!Context->FoundAttribute.AttributeDeleted) {
  10563. LastEntry = Context->AttributeList.Entry;
  10564. Entry = NtfsGetNextRecord( LastEntry );
  10565. } else {
  10566. Entry = Context->AttributeList.Entry;
  10567. Context->FoundAttribute.AttributeDeleted = FALSE;
  10568. //
  10569. // If we are beyond the attribute list, we return false. This will
  10570. // happen in the case where have removed an attribute record and
  10571. // there are no entries left in the attribute list.
  10572. //
  10573. if (Context->AttributeList.Entry >= Context->AttributeList.BeyondFinalEntry) {
  10574. //
  10575. // In case the caller is doing an insert, we will position him at the end
  10576. // of the first file record, an always try to insert new attributes there.
  10577. //
  10578. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10579. if (QueriedTypeCode != $UNUSED) {
  10580. NtfsReadFileRecord( IrpContext,
  10581. Fcb->Vcb,
  10582. &Fcb->FileReference,
  10583. &Context->FoundAttribute.Bcb,
  10584. &Context->FoundAttribute.FileRecord,
  10585. &Context->FoundAttribute.Attribute,
  10586. &Context->FoundAttribute.MftFileOffset );
  10587. //
  10588. // If returning FALSE, then take the time to really find the
  10589. // correct position in the file record for a subsequent insert.
  10590. //
  10591. NtfsFindInFileRecord( IrpContext,
  10592. Context->FoundAttribute.Attribute,
  10593. &Context->FoundAttribute.Attribute,
  10594. QueriedTypeCode,
  10595. QueriedName,
  10596. IgnoreCase,
  10597. QueriedValue,
  10598. QueriedValueLength );
  10599. }
  10600. DebugTrace( -1, Dbg, ("NtfsLookupExternalAttribute -> FALSE\n") );
  10601. return FALSE;
  10602. }
  10603. }
  10604. //
  10605. // Now walk through the entries looking for an atttribute.
  10606. //
  10607. while (TRUE) {
  10608. PATTRIBUTE_RECORD_HEADER Attribute;
  10609. UNICODE_STRING EntryName;
  10610. UNICODE_STRING AttributeName;
  10611. PATTRIBUTE_LIST_ENTRY NextEntry;
  10612. BOOLEAN CorrespondingAttributeFound;
  10613. //
  10614. // Check to see if we are now pointing beyond the final entry
  10615. // and if so fall in to the loop to terminate pointing just
  10616. // after the last entry.
  10617. //
  10618. if (Entry >= Context->AttributeList.BeyondFinalEntry) {
  10619. Terminating = TRUE;
  10620. TerminateOnNext = TRUE;
  10621. Entry = Context->AttributeList.Entry;
  10622. } else {
  10623. NtfsCheckRecordBound( Entry,
  10624. Context->AttributeList.FirstEntry,
  10625. PtrOffset( Context->AttributeList.FirstEntry,
  10626. Context->AttributeList.BeyondFinalEntry ));
  10627. if (Entry->RecordLength == 0) {
  10628. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10629. }
  10630. NextEntry = NtfsGetNextRecord( Entry );
  10631. }
  10632. Context->AttributeList.Entry = Entry;
  10633. //
  10634. // Compare the type codes. The external attribute entry list is
  10635. // ordered by type code, so if the queried type code is less than
  10636. // the entry type code we continue the while(), if it is
  10637. // greater than we break out of the while() and return failure.
  10638. // If equal, we move on to compare names.
  10639. //
  10640. if ((QueriedTypeCode != $UNUSED) &&
  10641. !Terminating &&
  10642. (QueriedTypeCode != Entry->AttributeTypeCode)) {
  10643. if (QueriedTypeCode > Entry->AttributeTypeCode) {
  10644. Entry = NextEntry;
  10645. continue;
  10646. //
  10647. // Set up to terminate on seeing a higher type code.
  10648. //
  10649. } else {
  10650. Terminating = TRUE;
  10651. }
  10652. }
  10653. //
  10654. // At this point we are OK by TypeCode, compare names.
  10655. //
  10656. EntryName.Length = EntryName.MaximumLength = Entry->AttributeNameLength * sizeof( WCHAR );
  10657. EntryName.Buffer = Add2Ptr( Entry, Entry->AttributeNameOffset );
  10658. if (ARGUMENT_PRESENT( QueriedName ) && !Terminating) {
  10659. FSRTL_COMPARISON_RESULT Result;
  10660. //
  10661. // See if we have a name match.
  10662. //
  10663. if (!NtfsAreNamesEqual( UpcaseTable,
  10664. &EntryName,
  10665. QueriedName,
  10666. IgnoreCase )) {
  10667. //
  10668. // Compare the names ignoring case.
  10669. //
  10670. Result = NtfsCollateNames( UpcaseTable,
  10671. UpcaseTableSize,
  10672. QueriedName,
  10673. &EntryName,
  10674. GreaterThan,
  10675. TRUE);
  10676. //
  10677. // Break out if the result is LessThan, or if the result
  10678. // is Equal to *and* the exact case compare yields LessThan.
  10679. //
  10680. if ((Result == LessThan) || ((Result == EqualTo) &&
  10681. (NtfsCollateNames( UpcaseTable,
  10682. UpcaseTableSize,
  10683. QueriedName,
  10684. &EntryName,
  10685. GreaterThan,
  10686. FALSE) == LessThan))) {
  10687. Terminating = TRUE;
  10688. } else {
  10689. Entry = NextEntry;
  10690. continue;
  10691. }
  10692. }
  10693. }
  10694. //
  10695. // Now search for the right Vcn range, if specified. If we were passed a
  10696. // Vcn then look for the matching range in the current attribute. In some
  10697. // cases we may be looking for the lowest range in the following complete
  10698. // attribute. In those cases skip forward.
  10699. //
  10700. if (ARGUMENT_PRESENT( Vcn ) && !Terminating) {
  10701. //
  10702. // Skip to the next attribute record under the following conditions.
  10703. //
  10704. // 1 - We are already past the Vcn point we are looking for in the current
  10705. // attribute. Typically this happens when the caller is looking for
  10706. // the first attribute record for each of the attributes in the file.
  10707. //
  10708. // 2 - The desired Vcn for the current attribute falls in one of the
  10709. // subsequent attribute records.
  10710. //
  10711. if ((Entry->LowestVcn > *Vcn) ||
  10712. ((NextEntry < Context->AttributeList.BeyondFinalEntry) &&
  10713. (NextEntry->LowestVcn <= *Vcn) &&
  10714. (NextEntry->AttributeTypeCode == Entry->AttributeTypeCode) &&
  10715. (NextEntry->AttributeNameLength == Entry->AttributeNameLength) &&
  10716. (RtlEqualMemory( Add2Ptr( NextEntry, NextEntry->AttributeNameOffset ),
  10717. Add2Ptr( Entry, Entry->AttributeNameOffset ),
  10718. Entry->AttributeNameLength * sizeof( WCHAR ))))) {
  10719. Entry = NextEntry;
  10720. continue;
  10721. }
  10722. }
  10723. //
  10724. // Now we are also OK by name and Vcn, so now go find the attribute and
  10725. // compare against value, if specified.
  10726. //
  10727. if ((LastEntry == NULL) ||
  10728. !NtfsEqualMftRef( &LastEntry->SegmentReference, &Entry->SegmentReference )) {
  10729. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10730. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10731. NtfsReadFileRecord( IrpContext,
  10732. Fcb->Vcb,
  10733. &Entry->SegmentReference,
  10734. &Context->FoundAttribute.Bcb,
  10735. &FileRecord,
  10736. &Attribute,
  10737. &Context->FoundAttribute.MftFileOffset );
  10738. Context->FoundAttribute.FileRecord = FileRecord;
  10739. //
  10740. // If we already have the right record pinned, reload this pointer.
  10741. //
  10742. } else {
  10743. Attribute = NtfsFirstAttribute( Context->FoundAttribute.FileRecord );
  10744. }
  10745. //
  10746. // Now quickly loop through looking for the correct attribute
  10747. // instance.
  10748. //
  10749. CorrespondingAttributeFound = FALSE;
  10750. while (TRUE) {
  10751. //
  10752. // Check that we can safely access this attribute.
  10753. //
  10754. NtfsCheckRecordBound( Attribute,
  10755. Context->FoundAttribute.FileRecord,
  10756. Fcb->Vcb->BytesPerFileRecordSegment );
  10757. //
  10758. // Exit the loop if we have reached the $END record.
  10759. //
  10760. if (Attribute->TypeCode == $END) {
  10761. break;
  10762. }
  10763. //
  10764. // Check that the attribute has a non-zero length.
  10765. //
  10766. if (Attribute->RecordLength == 0) {
  10767. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10768. }
  10769. if (Entry->Instance == Attribute->Instance) {
  10770. //
  10771. // Well, the attribute list saved us from having to compare
  10772. // type code and name as we went through this file record,
  10773. // however now that we have found our attribute by its
  10774. // instance number, we will do a quick check to see that
  10775. // we got the right one. Else the file is corrupt.
  10776. //
  10777. if (Entry->AttributeTypeCode != Attribute->TypeCode) {
  10778. break;
  10779. }
  10780. if (ARGUMENT_PRESENT( QueriedName )) {
  10781. NtfsInitializeStringFromAttribute( &AttributeName, Attribute );
  10782. if (!NtfsAreNamesEqual( UpcaseTable, &AttributeName, &EntryName, FALSE )) {
  10783. break;
  10784. }
  10785. }
  10786. //
  10787. // Show that we correctly found the attribute described in
  10788. // the attribute list.
  10789. //
  10790. CorrespondingAttributeFound = TRUE;
  10791. Context->FoundAttribute.Attribute = Attribute;
  10792. //
  10793. // Now we may just be here because we are terminating the
  10794. // scan on seeing the end, a higher attribute code, or a
  10795. // higher name. If so, return FALSE here.
  10796. //
  10797. if (Terminating) {
  10798. //
  10799. // If we hit the end of the attribute list, then we
  10800. // are supposed to terminate after advancing the
  10801. // attribute list entry.
  10802. //
  10803. if (TerminateOnNext) {
  10804. Context->AttributeList.Entry = NtfsGetNextRecord(Entry);
  10805. }
  10806. //
  10807. // In case the caller is doing an insert, we will position him at the end
  10808. // of the first file record, an always try to insert new attributes there.
  10809. //
  10810. NtfsUnpinBcb( IrpContext, &Context->FoundAttribute.Bcb );
  10811. if (QueriedTypeCode != $UNUSED) {
  10812. NtfsReadFileRecord( IrpContext,
  10813. Fcb->Vcb,
  10814. &Fcb->FileReference,
  10815. &Context->FoundAttribute.Bcb,
  10816. &Context->FoundAttribute.FileRecord,
  10817. &Context->FoundAttribute.Attribute,
  10818. &Context->FoundAttribute.MftFileOffset );
  10819. //
  10820. // If returning FALSE, then take the time to really find the
  10821. // correct position in the file record for a subsequent insert.
  10822. //
  10823. NtfsFindInFileRecord( IrpContext,
  10824. Context->FoundAttribute.Attribute,
  10825. &Context->FoundAttribute.Attribute,
  10826. QueriedTypeCode,
  10827. QueriedName,
  10828. IgnoreCase,
  10829. QueriedValue,
  10830. QueriedValueLength );
  10831. }
  10832. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10833. Attribute) );
  10834. DebugTrace( -1, Dbg, ("NtfsLookupExternalAttribute -> FALSE\n") );
  10835. return FALSE;
  10836. }
  10837. //
  10838. // Now compare the value, if so queried.
  10839. //
  10840. if (!ARGUMENT_PRESENT( QueriedValue ) ||
  10841. NtfsEqualAttributeValue( Attribute,
  10842. QueriedValue,
  10843. QueriedValueLength ) ) {
  10844. //
  10845. // It matches. Return it in the enumeration context.
  10846. //
  10847. DebugTrace( 0, Dbg, ("Context->FoundAttribute.Attribute < %08lx\n",
  10848. Attribute ));
  10849. DebugTrace( -1, Dbg, ("NtfsLookupExternalAttribute -> TRUE\n") );
  10850. //
  10851. // Do basic attribute consistency check
  10852. //
  10853. if ((NtfsIsAttributeResident( Attribute )) &&
  10854. (Attribute->Form.Resident.ValueOffset + Attribute->Form.Resident.ValueLength > Attribute->RecordLength)) {
  10855. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10856. }
  10857. return TRUE;
  10858. }
  10859. }
  10860. //
  10861. // Get the next attribute, and continue.
  10862. //
  10863. Attribute = NtfsGetNextRecord( Attribute );
  10864. }
  10865. //
  10866. // Did we even find the attribute corresponding to the entry?
  10867. // If not, something is messed up. Raise file corrupt error.
  10868. //
  10869. if (!CorrespondingAttributeFound) {
  10870. //
  10871. // For the moment, ASSERT this falsehood so that we may have
  10872. // a chance to peek before raising.
  10873. //
  10874. ASSERT( CorrespondingAttributeFound );
  10875. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  10876. }
  10877. Entry = NtfsGetNextRecord( Entry );
  10878. }
  10879. }
  10880. //
  10881. // Internal support routine
  10882. //
  10883. BOOLEAN
  10884. NtfsGetSpaceForAttribute (
  10885. IN PIRP_CONTEXT IrpContext,
  10886. IN PFCB Fcb,
  10887. IN ULONG Length,
  10888. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10889. )
  10890. /*++
  10891. Routine Description:
  10892. This routine gets space for a new attribute record at the position indicated
  10893. in the Context structure. As required, it will move attributes around,
  10894. allocate an additional record in the Mft, or convert some other existing
  10895. attribute to nonresident form. The caller should already have checked if
  10896. the new attribute he is inserting should be stored resident or nonresident.
  10897. On return, it is invalid to continue to use any previously-retrieved pointers,
  10898. Bcbs, or other position-dependent information retrieved from the Context
  10899. structure, as any of these values are liable to change. The file record in
  10900. which the space has been found will already be pinned.
  10901. Note, this routine DOES NOT actually make space for the attribute, it only
  10902. verifies that sufficient space is there. The caller may call
  10903. NtfsRestartInsertAttribute to actually insert the attribute in place.
  10904. Arguments:
  10905. Fcb - Requested file.
  10906. Length - Quad-aligned length required in bytes.
  10907. Context - Describes the position for the new attribute, as returned from
  10908. the enumeration which failed to find an existing occurrence of
  10909. the attribute. This pointer will either be pointing to some
  10910. other attribute in the record, or to the first free quad-aligned
  10911. byte if the new attribute is to go at the end.
  10912. Return Value:
  10913. FALSE - if a major move was necessary, and the caller should look up
  10914. its desired position again and call back.
  10915. TRUE - if the space was created
  10916. --*/
  10917. {
  10918. PATTRIBUTE_RECORD_HEADER NextAttribute;
  10919. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10920. PAGED_CODE();
  10921. DebugTrace( +1, Dbg, ("NtfsGetSpaceForAttribute\n") );
  10922. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10923. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  10924. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10925. ASSERT( IsQuadAligned( Length ) );
  10926. NextAttribute = NtfsFoundAttribute( Context );
  10927. FileRecord = NtfsContainingFileRecord( Context );
  10928. //
  10929. // Make sure the buffer is pinned.
  10930. //
  10931. NtfsPinMappedAttribute( IrpContext, Fcb->Vcb, Context );
  10932. //
  10933. // If the space is not there now, then make room and return with FALSE
  10934. //
  10935. if ((FileRecord->BytesAvailable - FileRecord->FirstFreeByte) < Length ) {
  10936. MakeRoomForAttribute( IrpContext, Fcb, Length, Context );
  10937. DebugTrace( -1, Dbg, ("NtfsGetSpaceForAttribute -> FALSE\n") );
  10938. return FALSE;
  10939. }
  10940. DebugTrace( -1, Dbg, ("NtfsGetSpaceForAttribute -> TRUE\n") );
  10941. return TRUE;
  10942. }
  10943. //
  10944. // Internal support routine
  10945. //
  10946. BOOLEAN
  10947. NtfsChangeAttributeSize (
  10948. IN PIRP_CONTEXT IrpContext,
  10949. IN PFCB Fcb,
  10950. IN ULONG Length,
  10951. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  10952. )
  10953. /*++
  10954. Routine Description:
  10955. This routine adjustss the space occupied by the current attribute record
  10956. in the Context structure. As required, it will move attributes around,
  10957. allocate an additional record in the Mft, or convert some other existing
  10958. attribute to nonresident form. The caller should already have checked if
  10959. the current attribute he is inserting should rather be converted to
  10960. nonresident.
  10961. When done, this routine has updated any file records whose allocation was
  10962. changed, and also the RecordLength field in the adjusted attribute. No
  10963. other attribute fields are updated.
  10964. On return, it is invalid to continue to use any previously-retrieved pointers,
  10965. Bcbs, or other position-dependent information retrieved from the Context
  10966. structure, as any of these values are liable to change. The file record in
  10967. which the space has been found will already be pinned.
  10968. Arguments:
  10969. Fcb - Requested file.
  10970. Length - New quad-aligned length of attribute record in bytes
  10971. Context - Describes the current attribute.
  10972. Return Value:
  10973. FALSE - if a major move was necessary, and the caller should look up
  10974. its desired position again and call back.
  10975. TRUE - if the space was created
  10976. --*/
  10977. {
  10978. PATTRIBUTE_RECORD_HEADER Attribute;
  10979. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  10980. LONG SizeChange;
  10981. PAGED_CODE();
  10982. DebugTrace( +1, Dbg, ("NtfsChangeAttributeSize\n") );
  10983. DebugTrace( 0, Dbg, ("Fcb = %08lx\n", Fcb) );
  10984. DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) );
  10985. DebugTrace( 0, Dbg, ("Context = %08lx\n", Context) );
  10986. ASSERT( IsQuadAligned( Length ) );
  10987. Attribute = NtfsFoundAttribute( Context );
  10988. FileRecord = NtfsContainingFileRecord( Context );
  10989. //
  10990. // Make sure the buffer is pinned.
  10991. //
  10992. NtfsPinMappedAttribute( IrpContext, Fcb->Vcb, Context );
  10993. //
  10994. // Calculate the change in attribute record size.
  10995. //
  10996. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  10997. SizeChange = Length - Attribute->RecordLength;
  10998. //
  10999. // If there is not currently enough space, then we have to make room
  11000. // and return FALSE to our caller.
  11001. //
  11002. if ( (LONG)(FileRecord->BytesAvailable - FileRecord->FirstFreeByte) < SizeChange ) {
  11003. MakeRoomForAttribute( IrpContext, Fcb, SizeChange, Context );
  11004. DebugTrace( -1, Dbg, ("NtfsChangeAttributeSize -> FALSE\n") );
  11005. return FALSE;
  11006. }
  11007. DebugTrace( -1, Dbg, ("NtfsChangeAttributeSize -> TRUE\n") );
  11008. return TRUE;
  11009. }
  11010. //
  11011. // Internal support routine
  11012. //
  11013. VOID
  11014. MakeRoomForAttribute (
  11015. IN PIRP_CONTEXT IrpContext,
  11016. IN PFCB Fcb,
  11017. IN ULONG SizeNeeded,
  11018. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  11019. )
  11020. /*++
  11021. Routine Description:
  11022. This routine attempts to make additional room for a new attribute or
  11023. a growing attribute in a file record. The algorithm is as follows.
  11024. First continuously loop through the record looking at the largest n
  11025. attributes, from the largest down, to see which one of these attributes
  11026. is big enough to move, and which one qualifies for one of the following
  11027. actions:
  11028. 1. For an index root attribute, the indexing package may be called
  11029. to "push" the index root, i.e., add another level to the BTree
  11030. leaving only an end index record in the root.
  11031. 2. For a resident attribute which is allowed to be made nonresident,
  11032. the attribute is made nonresident, leaving only run information
  11033. in the root.
  11034. 3. If the attribute is already nonresident, then it can be moved to
  11035. a separate file record.
  11036. If none of the above operations can be performed, or not enough free space
  11037. is recovered, then as a last resort the file record is split in two. This
  11038. would typically indicate that the file record is populated with a large
  11039. number of small attributes.
  11040. The first time step 3 above or a split of the file record occurs, the
  11041. attribute list must be created for the file.
  11042. Arguments:
  11043. Fcb - Requested file.
  11044. SizeNeeded - Supplies the total amount of free space needed, in bytes.
  11045. Context - Describes the insertion point for the attribute which does
  11046. not fit. NOTE -- This context is not valid on return.
  11047. Return Value:
  11048. None
  11049. --*/
  11050. {
  11051. PATTRIBUTE_RECORD_HEADER LargestAttributes[MAX_MOVEABLE_ATTRIBUTES];
  11052. PATTRIBUTE_RECORD_HEADER Attribute;
  11053. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  11054. ULONG i;
  11055. PVCB Vcb = Fcb->Vcb;
  11056. PAGED_CODE();
  11057. //
  11058. // Here is the current threshhold at which a move of an attribute will
  11059. // be considered.
  11060. //
  11061. FileRecord = NtfsContainingFileRecord( Context );
  11062. //
  11063. // Find the largest attributes for this file record.
  11064. //
  11065. FindLargestAttributes( FileRecord, MAX_MOVEABLE_ATTRIBUTES, LargestAttributes );
  11066. //
  11067. // Now loop from largest to smallest of the largest attributes,
  11068. // and see if there is something we can do.
  11069. //
  11070. for (i = 0; i < MAX_MOVEABLE_ATTRIBUTES; i += 1) {
  11071. Attribute = LargestAttributes[i];
  11072. //
  11073. // Look to the next attribute if there is no attribute at this array
  11074. // position.
  11075. //
  11076. if (Attribute == NULL) {
  11077. continue;
  11078. //
  11079. // If this is the Mft then any attribute that is 'BigEnoughToMove'
  11080. // except $DATA attributes outside the base file record.
  11081. // We need to keep those where they are in order to enforce the
  11082. // boot-strap mapping.
  11083. //
  11084. } else if (Fcb == Vcb->MftScb->Fcb) {
  11085. if (Attribute->TypeCode == $DATA &&
  11086. ((*(PLONGLONG) &FileRecord->BaseFileRecordSegment != 0) ||
  11087. (Attribute->RecordLength < Vcb->BigEnoughToMove))) {
  11088. continue;
  11089. }
  11090. //
  11091. // Any attribute in a non-Mft file which is 'BigEnoughToMove' can
  11092. // be considered. We also accept an $ATTRIBUTE_LIST attribute
  11093. // in a non-Mft file which must go non-resident in order for
  11094. // the attribute name to fit. Otherwise we could be trying to
  11095. // add an attribute with a large name into the base file record.
  11096. // We will need space to store the name twice, once for the
  11097. // attribute list entry and once in the attribute. This can take
  11098. // up 1024 bytes by itself. We want to force the attribute list
  11099. // non-resident first so that the new attribute will fit. We
  11100. // look at whether the attribute list followed by just the new data
  11101. // will fit in the file record.
  11102. //
  11103. } else if (Attribute->RecordLength < Vcb->BigEnoughToMove) {
  11104. if ((Attribute->TypeCode != $ATTRIBUTE_LIST) ||
  11105. ((PtrOffset( FileRecord, Attribute ) + Attribute->RecordLength + SizeNeeded + sizeof( LONGLONG)) <= FileRecord->BytesAvailable)) {
  11106. continue;
  11107. }
  11108. }
  11109. //
  11110. // If this attribute is an index root, then we can just call the
  11111. // indexing support to allocate a new index buffer and push the
  11112. // current resident contents down.
  11113. //
  11114. if (Attribute->TypeCode == $INDEX_ROOT) {
  11115. PSCB IndexScb;
  11116. UNICODE_STRING IndexName;
  11117. //
  11118. // Don't push the root now if we previously deferred pushing the root.
  11119. // Set the IrpContext flag to indicate we should do the push
  11120. // and raise CANT_WAIT.
  11121. //
  11122. if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_PUSH )) {
  11123. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_FORCE_PUSH );
  11124. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  11125. }
  11126. IndexName.Length =
  11127. IndexName.MaximumLength = (USHORT)Attribute->NameLength << 1;
  11128. IndexName.Buffer = Add2Ptr( Attribute, Attribute->NameOffset );
  11129. IndexScb = NtfsCreateScb( IrpContext,
  11130. Fcb,
  11131. $INDEX_ALLOCATION,
  11132. &IndexName,
  11133. FALSE,
  11134. NULL );
  11135. NtfsPushIndexRoot( IrpContext, IndexScb );
  11136. return;
  11137. //
  11138. // Otherwise, if this is a resident attribute which can go nonresident,
  11139. // then make it nonresident now.
  11140. //
  11141. } else if ((Attribute->FormCode == RESIDENT_FORM) &&
  11142. !FlagOn(NtfsGetAttributeDefinition(Vcb,
  11143. Attribute->TypeCode)->Flags,
  11144. ATTRIBUTE_DEF_MUST_BE_RESIDENT)) {
  11145. NtfsConvertToNonresident( IrpContext, Fcb, Attribute, FALSE, NULL );
  11146. return;
  11147. //
  11148. // Finally, if the attribute is nonresident already, move it to its
  11149. // own record unless it is an attribute list.
  11150. //
  11151. } else if ((Attribute->FormCode == NONRESIDENT_FORM)
  11152. && (Attribute->TypeCode != $ATTRIBUTE_LIST)) {
  11153. LONGLONG MftFileOffset;
  11154. MftFileOffset = Context->FoundAttribute.MftFileOffset;
  11155. MoveAttributeToOwnRecord( IrpContext,
  11156. Fcb,
  11157. Attribute,
  11158. Context,
  11159. NULL,
  11160. NULL );
  11161. return;
  11162. }
  11163. }
  11164. //
  11165. // If we get here, it is because we failed to find enough space above.
  11166. // Our last resort is to split into two file records, and this has
  11167. // to work. We should never reach this point for the Mft.
  11168. //
  11169. if (Fcb == Vcb->MftScb->Fcb) {
  11170. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  11171. }
  11172. SplitFileRecord( IrpContext, Fcb, SizeNeeded, Context );
  11173. }
  11174. //
  11175. // Internal support routine
  11176. //
  11177. VOID
  11178. FindLargestAttributes (
  11179. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  11180. IN ULONG Number,
  11181. OUT PATTRIBUTE_RECORD_HEADER *AttributeArray
  11182. )
  11183. /*++
  11184. Routine Description:
  11185. This routine returns the n largest attributes from a file record in an
  11186. array, ordered from largest to smallest.
  11187. Arguments:
  11188. FileRecord - Supplies file record to scan for largest attributes.
  11189. Number - Supplies the number of entries in the array.
  11190. AttributeArray - Supplies the array which is to receive pointers to the
  11191. largest attributes. This array must be zeroed prior
  11192. to calling this routine.
  11193. Return Value:
  11194. None
  11195. --*/
  11196. {
  11197. ULONG i, j;
  11198. PATTRIBUTE_RECORD_HEADER Attribute;
  11199. PAGED_CODE();
  11200. RtlZeroMemory( AttributeArray, Number * sizeof(PATTRIBUTE_RECORD_HEADER) );
  11201. Attribute = Add2Ptr( FileRecord, FileRecord->FirstAttributeOffset );
  11202. while (Attribute->TypeCode != $END) {
  11203. for (i = 0; i < Number; i++) {
  11204. if ((AttributeArray[i] == NULL)
  11205. ||
  11206. (AttributeArray[i]->RecordLength < Attribute->RecordLength)) {
  11207. for (j = Number - 1; j != i; j--) {
  11208. AttributeArray[j] = AttributeArray[j-1];
  11209. }
  11210. AttributeArray[i] = Attribute;
  11211. break;
  11212. }
  11213. }
  11214. Attribute = Add2Ptr( Attribute, Attribute->RecordLength );
  11215. }
  11216. }
  11217. //
  11218. // Internal support routine
  11219. //
  11220. LONGLONG
  11221. MoveAttributeToOwnRecord (
  11222. IN PIRP_CONTEXT IrpContext,
  11223. IN PFCB Fcb,
  11224. IN PATTRIBUTE_RECORD_HEADER Attribute,
  11225. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context,
  11226. OUT PBCB *NewBcb OPTIONAL,
  11227. OUT PFILE_RECORD_SEGMENT_HEADER *NewFileRecord OPTIONAL
  11228. )
  11229. /*++
  11230. Routine Description:
  11231. This routine may be called to move a particular attribute to a separate
  11232. file record. If the file does not already have an attribute list, then
  11233. one is created (else it is updated).
  11234. Arguments:
  11235. Fcb - Requested file.
  11236. Attribute - Supplies a pointer to the attribute which is to be moved.
  11237. Context - Supplies a pointer to a context which was used to look up
  11238. another attribute in the same file record. If this is an Mft
  11239. $DATA split we will point to the part that was split out of the
  11240. first file record on return. The call from NtfsAddAttributeAllocation
  11241. depends on this.
  11242. NewBcb - If supplied, returns the Bcb address for the file record
  11243. that the attribute was moved to. NewBcb and NewFileRecord must
  11244. either both be specified or neither specified.
  11245. NewFileRecord - If supplied, returns a pointer to the file record
  11246. that the attribute was moved to. The caller may assume
  11247. that the moved attribute is the first one in the file
  11248. record. NewBcb and NewFileRecord must either both be
  11249. specified or neither specified.
  11250. Return Value:
  11251. LONGLONG - Segment reference number of new record without a sequence number.
  11252. --*/
  11253. {
  11254. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  11255. ATTRIBUTE_ENUMERATION_CONTEXT MoveContext;
  11256. PFILE_RECORD_SEGMENT_HEADER FileRecord1, FileRecord2;
  11257. PATTRIBUTE_RECORD_HEADER Attribute2;
  11258. BOOLEAN FoundListContext;
  11259. MFT_SEGMENT_REFERENCE Reference2;
  11260. LONGLONG MftRecordNumber2;
  11261. WCHAR NameBuffer[8];
  11262. UNICODE_STRING AttributeName;
  11263. ATTRIBUTE_TYPE_CODE AttributeTypeCode;
  11264. VCN LowestVcn;
  11265. BOOLEAN IsNonresident = FALSE;
  11266. PBCB Bcb = NULL;
  11267. PATTRIBUTE_TYPE_CODE NewEnd;
  11268. PVCB Vcb = Fcb->Vcb;
  11269. ULONG NewListSize = 0;
  11270. BOOLEAN MftData = FALSE;
  11271. PATTRIBUTE_RECORD_HEADER OldPosition = NULL;
  11272. PAGED_CODE();
  11273. //
  11274. // Make sure the attribute is pinned.
  11275. //
  11276. NtfsPinMappedAttribute( IrpContext,
  11277. Vcb,
  11278. Context );
  11279. //
  11280. // See if we are being asked to move the Mft Data.
  11281. //
  11282. if ((Fcb == Vcb->MftScb->Fcb) && (Attribute->TypeCode == $DATA)) {
  11283. MftData = TRUE;
  11284. }
  11285. NtfsInitializeAttributeContext( &ListContext );
  11286. NtfsInitializeAttributeContext( &MoveContext );
  11287. FileRecord1 = NtfsContainingFileRecord(Context);
  11288. //
  11289. // Save a description of the attribute to help us look it up
  11290. // again, and to make clones if necessary.
  11291. //
  11292. ASSERT( IsQuadAligned( Attribute->RecordLength ) );
  11293. AttributeTypeCode = Attribute->TypeCode;
  11294. AttributeName.Length =
  11295. AttributeName.MaximumLength = (USHORT)Attribute->NameLength << 1;
  11296. AttributeName.Buffer = NameBuffer;
  11297. if (AttributeName.Length > sizeof(NameBuffer)) {
  11298. AttributeName.Buffer = NtfsAllocatePool( NonPagedPool, AttributeName.Length );
  11299. }
  11300. RtlCopyMemory( AttributeName.Buffer,
  11301. Add2Ptr(Attribute, Attribute->NameOffset),
  11302. AttributeName.Length );
  11303. if (Attribute->FormCode == NONRESIDENT_FORM) {
  11304. IsNonresident = TRUE;
  11305. LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  11306. }
  11307. try {
  11308. //
  11309. // Lookup the list context so that we know where it is at.
  11310. //
  11311. FoundListContext =
  11312. NtfsLookupAttributeByCode( IrpContext,
  11313. Fcb,
  11314. &Fcb->FileReference,
  11315. $ATTRIBUTE_LIST,
  11316. &ListContext );
  11317. //
  11318. // If we do not already have an attribute list, then calculate
  11319. // how big it must be. Note, there must only be one file record
  11320. // at this point.
  11321. //
  11322. if (!FoundListContext) {
  11323. ASSERT( FileRecord1 == NtfsContainingFileRecord(&ListContext) );
  11324. NewListSize = GetSizeForAttributeList( FileRecord1 );
  11325. //
  11326. // Now if the attribute list already exists, we have to look up
  11327. // the first one we are going to move in order to update the
  11328. // attribute list later.
  11329. //
  11330. } else {
  11331. if (!NtfsLookupAttributeByName( IrpContext,
  11332. Fcb,
  11333. &Fcb->FileReference,
  11334. Attribute->TypeCode,
  11335. &AttributeName,
  11336. IsNonresident ?
  11337. &LowestVcn :
  11338. NULL,
  11339. FALSE,
  11340. &MoveContext )) {
  11341. ASSERT( FALSE );
  11342. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11343. }
  11344. ASSERT(Attribute == NtfsFoundAttribute(&MoveContext));
  11345. }
  11346. //
  11347. // Allocate a new file record and move the attribute over.
  11348. //
  11349. FileRecord2 = NtfsCloneFileRecord( IrpContext, Fcb, MftData, &Bcb, &Reference2 );
  11350. //
  11351. // Remember the file record number for the new file record.
  11352. //
  11353. MftRecordNumber2 = NtfsFullSegmentNumber( &Reference2 );
  11354. Attribute2 = Add2Ptr( FileRecord2, FileRecord2->FirstAttributeOffset );
  11355. RtlCopyMemory( Attribute2, Attribute, (ULONG)Attribute->RecordLength );
  11356. Attribute2->Instance = FileRecord2->NextAttributeInstance++;
  11357. NewEnd = Add2Ptr( Attribute2, Attribute2->RecordLength );
  11358. *NewEnd = $END;
  11359. FileRecord2->FirstFreeByte = PtrOffset(FileRecord2, NewEnd)
  11360. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  11361. //
  11362. // If this is the Mft Data attribute, we cannot really move it, we
  11363. // have to move all but the first part of it.
  11364. //
  11365. if (MftData) {
  11366. PCHAR MappingPairs;
  11367. ULONG NewSize;
  11368. VCN OriginalLastVcn;
  11369. VCN LastVcn;
  11370. LONGLONG SavedFileSize = Attribute->Form.Nonresident.FileSize;
  11371. LONGLONG SavedValidDataLength = Attribute->Form.Nonresident.ValidDataLength;
  11372. PNTFS_MCB Mcb = &Vcb->MftScb->Mcb;
  11373. NtfsCleanupAttributeContext( IrpContext, Context );
  11374. NtfsInitializeAttributeContext( Context );
  11375. if (!NtfsLookupAttributeByCode( IrpContext,
  11376. Fcb,
  11377. &Fcb->FileReference,
  11378. $DATA,
  11379. Context )) {
  11380. ASSERT( FALSE );
  11381. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11382. }
  11383. //
  11384. // Calculate the number of clusters in the Mft up to (possibly past) the
  11385. // first user file record, and decrement to get LastVcn to stay in first
  11386. // file record.
  11387. //
  11388. LastVcn = LlClustersFromBytes( Vcb,
  11389. FIRST_USER_FILE_NUMBER *
  11390. Vcb->BytesPerFileRecordSegment ) - 1;
  11391. OriginalLastVcn = Attribute->Form.Nonresident.HighestVcn;
  11392. //
  11393. // Now truncate the first Mft record.
  11394. //
  11395. NtfsDeleteAttributeAllocation( IrpContext,
  11396. Vcb->MftScb,
  11397. TRUE,
  11398. &LastVcn,
  11399. Context,
  11400. FALSE );
  11401. //
  11402. // Now get the first Lcn for the new file record.
  11403. //
  11404. LastVcn = Attribute->Form.Nonresident.HighestVcn + 1;
  11405. Attribute2->Form.Nonresident.LowestVcn = LastVcn;
  11406. //
  11407. // Calculate the size of the attribute record we will need.
  11408. // We only create mapping pairs through the highest Vcn on the
  11409. // disk. We don't include any that are being added through the
  11410. // Mcb yet.
  11411. //
  11412. NewSize = SIZEOF_PARTIAL_NONRES_ATTR_HEADER
  11413. + QuadAlign( AttributeName.Length )
  11414. + QuadAlign( NtfsGetSizeForMappingPairs( Mcb,
  11415. MAXULONG,
  11416. LastVcn,
  11417. &OriginalLastVcn,
  11418. &LastVcn ));
  11419. Attribute2->RecordLength = NewSize;
  11420. //
  11421. // Assume no attribute name, and calculate where the Mapping Pairs
  11422. // will go. (Update below if we are wrong.)
  11423. //
  11424. MappingPairs = (PCHAR)Attribute2 + SIZEOF_PARTIAL_NONRES_ATTR_HEADER;
  11425. //
  11426. // If the attribute has a name, take care of that now.
  11427. //
  11428. if (AttributeName.Length != 0) {
  11429. Attribute2->NameLength = (UCHAR)(AttributeName.Length / sizeof(WCHAR));
  11430. Attribute2->NameOffset = (USHORT)PtrOffset(Attribute2, MappingPairs);
  11431. RtlCopyMemory( MappingPairs,
  11432. AttributeName.Buffer,
  11433. AttributeName.Length );
  11434. MappingPairs += QuadAlign( AttributeName.Length );
  11435. }
  11436. //
  11437. // We always need the mapping pairs offset.
  11438. //
  11439. Attribute2->Form.Nonresident.MappingPairsOffset =
  11440. (USHORT)PtrOffset(Attribute2, MappingPairs);
  11441. NewEnd = Add2Ptr( Attribute2, Attribute2->RecordLength );
  11442. *NewEnd = $END;
  11443. FileRecord2->FirstFreeByte = PtrOffset(FileRecord2, NewEnd)
  11444. + QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  11445. //
  11446. // Now add the space in the file record.
  11447. //
  11448. *MappingPairs = 0;
  11449. NtfsBuildMappingPairs( Mcb,
  11450. Attribute2->Form.Nonresident.LowestVcn,
  11451. &LastVcn,
  11452. MappingPairs );
  11453. Attribute2->Form.Nonresident.HighestVcn = LastVcn;
  11454. } else {
  11455. //
  11456. // Now log these changes and fix up the first file record.
  11457. //
  11458. FileRecord1->Lsn =
  11459. NtfsWriteLog( IrpContext,
  11460. Vcb->MftScb,
  11461. NtfsFoundBcb(Context),
  11462. DeleteAttribute,
  11463. NULL,
  11464. 0,
  11465. CreateAttribute,
  11466. Attribute,
  11467. Attribute->RecordLength,
  11468. NtfsMftOffset( Context ),
  11469. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord1),
  11470. 0,
  11471. Vcb->BytesPerFileRecordSegment );
  11472. //
  11473. // Remember the old position for the CreateAttributeList
  11474. //
  11475. OldPosition = Attribute;
  11476. NtfsRestartRemoveAttribute( IrpContext,
  11477. FileRecord1,
  11478. (ULONG)((PCHAR)Attribute - (PCHAR)FileRecord1) );
  11479. }
  11480. FileRecord2->Lsn =
  11481. NtfsWriteLog( IrpContext,
  11482. Vcb->MftScb,
  11483. Bcb,
  11484. InitializeFileRecordSegment,
  11485. FileRecord2,
  11486. FileRecord2->FirstFreeByte,
  11487. Noop,
  11488. NULL,
  11489. 0,
  11490. LlBytesFromFileRecords( Vcb, MftRecordNumber2 ),
  11491. 0,
  11492. 0,
  11493. Vcb->BytesPerFileRecordSegment );
  11494. //
  11495. // Finally, create the attribute list attribute if needed.
  11496. //
  11497. if (!FoundListContext) {
  11498. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11499. NtfsInitializeAttributeContext( &ListContext );
  11500. CreateAttributeList( IrpContext,
  11501. Fcb,
  11502. FileRecord1,
  11503. MftData ? NULL : FileRecord2,
  11504. Reference2,
  11505. OldPosition,
  11506. NewListSize,
  11507. &ListContext );
  11508. //
  11509. // Otherwise we have to update the existing attribute list, but only
  11510. // if this is not the Mft data. In that case the attribute list is
  11511. // still correct since we haven't moved the attribute entirely.
  11512. //
  11513. } else if (!MftData) {
  11514. UpdateAttributeListEntry( IrpContext,
  11515. Fcb,
  11516. &MoveContext.AttributeList.Entry->SegmentReference,
  11517. MoveContext.AttributeList.Entry->Instance,
  11518. &Reference2,
  11519. Attribute2->Instance,
  11520. &ListContext );
  11521. }
  11522. NtfsCleanupAttributeContext( IrpContext, Context );
  11523. NtfsInitializeAttributeContext( Context );
  11524. if (!NtfsLookupAttributeByName( IrpContext,
  11525. Fcb,
  11526. &Fcb->FileReference,
  11527. AttributeTypeCode,
  11528. &AttributeName,
  11529. IsNonresident ? &LowestVcn : NULL,
  11530. FALSE,
  11531. Context )) {
  11532. ASSERT( FALSE );
  11533. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11534. }
  11535. ASSERT(!IsNonresident || (LowestVcn == NtfsFoundAttribute(Context)->Form.Nonresident.LowestVcn));
  11536. //
  11537. // For the case of the Mft split, we now add the final entry.
  11538. //
  11539. if (MftData) {
  11540. //
  11541. // Finally, we have to add the entry to the attribute list.
  11542. // The routine we have to do this gets most of its inputs
  11543. // out of an attribute context. Our context at this point
  11544. // does not have quite the right information, so we have to
  11545. // update it here before calling AddToAttributeList.
  11546. //
  11547. Context->FoundAttribute.FileRecord = FileRecord2;
  11548. Context->FoundAttribute.Attribute = Attribute2;
  11549. Context->AttributeList.Entry =
  11550. NtfsGetNextRecord(Context->AttributeList.Entry);
  11551. NtfsAddToAttributeList( IrpContext, Fcb, Reference2, Context );
  11552. NtfsCleanupAttributeContext( IrpContext, Context );
  11553. NtfsInitializeAttributeContext( Context );
  11554. if (!NtfsLookupAttributeByCode( IrpContext,
  11555. Fcb,
  11556. &Fcb->FileReference,
  11557. $DATA,
  11558. Context )) {
  11559. ASSERT( FALSE );
  11560. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11561. }
  11562. while (IsNonresident &&
  11563. (Attribute2->Form.Nonresident.LowestVcn !=
  11564. NtfsFoundAttribute(Context)->Form.Nonresident.LowestVcn)) {
  11565. if (!NtfsLookupNextAttributeByCode( IrpContext,
  11566. Fcb,
  11567. $DATA,
  11568. Context )) {
  11569. ASSERT( FALSE );
  11570. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11571. }
  11572. }
  11573. }
  11574. } finally {
  11575. if (AttributeName.Buffer != NameBuffer) {
  11576. NtfsFreePool(AttributeName.Buffer);
  11577. }
  11578. if (ARGUMENT_PRESENT(NewBcb)) {
  11579. ASSERT(ARGUMENT_PRESENT(NewFileRecord));
  11580. *NewBcb = Bcb;
  11581. *NewFileRecord = FileRecord2;
  11582. } else {
  11583. ASSERT(!ARGUMENT_PRESENT(NewFileRecord));
  11584. NtfsUnpinBcb( IrpContext, &Bcb );
  11585. }
  11586. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11587. NtfsCleanupAttributeContext( IrpContext, &MoveContext );
  11588. }
  11589. return MftRecordNumber2;
  11590. }
  11591. //
  11592. // Internal support routine
  11593. //
  11594. VOID
  11595. SplitFileRecord (
  11596. IN PIRP_CONTEXT IrpContext,
  11597. IN PFCB Fcb,
  11598. IN ULONG SizeNeeded,
  11599. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT Context
  11600. )
  11601. /*++
  11602. Routine Description:
  11603. This routine splits a file record in two, when it has been found that
  11604. there is no room for a new attribute. If the file does not already have
  11605. an attribute list attribute then one is created.
  11606. Essentially this routine finds the midpoint in the current file record
  11607. (accounting for a potential new attribute list and also the space needed).
  11608. Then it copies the second half of the file record over and fixes up the
  11609. first record. The attribute list is created at the end if required.
  11610. Arguments:
  11611. Fcb - Requested file.
  11612. SizeNeeded - Supplies the additional size needed, which is causing the split
  11613. to occur.
  11614. Context - Supplies the attribute enumeration context pointing to the spot
  11615. where the new attribute is to be inserted or grown.
  11616. Return Value:
  11617. None
  11618. --*/
  11619. {
  11620. ATTRIBUTE_ENUMERATION_CONTEXT ListContext;
  11621. ATTRIBUTE_ENUMERATION_CONTEXT MoveContext;
  11622. PFILE_RECORD_SEGMENT_HEADER FileRecord1, FileRecord2;
  11623. PATTRIBUTE_RECORD_HEADER Attribute1, Attribute2, Attribute;
  11624. ULONG NewListOffset = 0;
  11625. ULONG NewListSize = 0;
  11626. ULONG NewAttributeOffset;
  11627. ULONG SizeToStay;
  11628. ULONG CurrentOffset, FutureOffset;
  11629. ULONG SizeToMove;
  11630. BOOLEAN FoundListContext;
  11631. MFT_SEGMENT_REFERENCE Reference1, Reference2;
  11632. LONGLONG MftFileRecord2;
  11633. PBCB Bcb = NULL;
  11634. ATTRIBUTE_TYPE_CODE EndCode = $END;
  11635. PVCB Vcb = Fcb->Vcb;
  11636. ULONG AdjustedAvailBytes;
  11637. PAGED_CODE();
  11638. //
  11639. // Make sure the attribute is pinned.
  11640. //
  11641. NtfsPinMappedAttribute( IrpContext,
  11642. Vcb,
  11643. Context );
  11644. //
  11645. // Something is broken if we decide to split an Mft record.
  11646. //
  11647. ASSERT(Fcb != Vcb->MftScb->Fcb);
  11648. NtfsInitializeAttributeContext( &ListContext );
  11649. NtfsInitializeAttributeContext( &MoveContext );
  11650. FileRecord1 = NtfsContainingFileRecord(Context);
  11651. Attribute1 = NtfsFoundAttribute(Context);
  11652. try {
  11653. //
  11654. // Lookup the list context so that we know where it is at.
  11655. //
  11656. FoundListContext =
  11657. NtfsLookupAttributeByCode( IrpContext,
  11658. Fcb,
  11659. &Fcb->FileReference,
  11660. $ATTRIBUTE_LIST,
  11661. &ListContext );
  11662. //
  11663. // If we do not already have an attribute list, then calculate
  11664. // where it will go and how big it must be. Note, there must
  11665. // only be one file record at this point.
  11666. //
  11667. if (!FoundListContext) {
  11668. ASSERT( FileRecord1 == NtfsContainingFileRecord(&ListContext) );
  11669. NewListOffset = PtrOffset( FileRecord1,
  11670. NtfsFoundAttribute(&ListContext) );
  11671. NewListSize = GetSizeForAttributeList( FileRecord1 ) +
  11672. SIZEOF_RESIDENT_ATTRIBUTE_HEADER;
  11673. }
  11674. //
  11675. // Similarly describe where the new attribute is to go, and how
  11676. // big it is (already in SizeNeeded).
  11677. //
  11678. NewAttributeOffset = PtrOffset( FileRecord1, Attribute1 );
  11679. //
  11680. // Now calculate the approximate number of bytes that is to be split
  11681. // across two file records, and divide it in two, and that should give
  11682. // the amount that is to stay in the first record.
  11683. //
  11684. SizeToStay = (FileRecord1->FirstFreeByte + NewListSize +
  11685. SizeNeeded + sizeof(FILE_RECORD_SEGMENT_HEADER)) / 2;
  11686. //
  11687. // We know that since we called this routine we need to split at
  11688. // least one entry from this file record. We also base our
  11689. // split logic by finding the first attribute which WILL lie beyond
  11690. // the split point (after adding an attribute list and possibly
  11691. // an intermediate attribute). We shrink the split point to the
  11692. // position at the end of where the current last attribute will be
  11693. // after adding the attribute list. If we also add space before
  11694. // the last attribute then we know the last attribute will surely
  11695. // be split out.
  11696. //
  11697. if (SizeToStay > (FileRecord1->FirstFreeByte - sizeof( LONGLONG ) + NewListSize)) {
  11698. SizeToStay = FileRecord1->FirstFreeByte - sizeof( LONGLONG ) + NewListSize;
  11699. }
  11700. //
  11701. // Now begin the loop through the attributes to find the splitting
  11702. // point. We stop when we reach the end record or are past the attribute
  11703. // which contains the split point. We will split at the current attribute
  11704. // if the remaining bytes after this attribute won't allow us to add
  11705. // the bytes we need for the caller or create an attribute list if
  11706. // it doesn't exist.
  11707. //
  11708. // At this point the following variables indicate the following:
  11709. //
  11710. // FutureOffset - This the offset of the current attribute
  11711. // after adding an attribute list and the attribute we
  11712. // are making space for.
  11713. //
  11714. // CurrentOffset - Current position in the file record of
  11715. // of attribute being examined now.
  11716. //
  11717. // NewListOffset - Offset to insert new attribute list into
  11718. // file record (0 indicates the list already exists).
  11719. //
  11720. // NewAttributeOffset - Offset in the file record of the new
  11721. // attribute. This refers to the file record as it exists
  11722. // when this routine is called.
  11723. //
  11724. FutureOffset =
  11725. CurrentOffset = (ULONG)FileRecord1->FirstAttributeOffset;
  11726. Attribute1 = Add2Ptr( FileRecord1, CurrentOffset );
  11727. AdjustedAvailBytes = FileRecord1->BytesAvailable
  11728. - QuadAlign( sizeof( ATTRIBUTE_TYPE_CODE ));
  11729. while (Attribute1->TypeCode != $END) {
  11730. //
  11731. // See if the attribute list goes here.
  11732. //
  11733. if (CurrentOffset == NewListOffset) {
  11734. //
  11735. // This attribute and all later attributes will be moved
  11736. // by the size of attribute list.
  11737. //
  11738. FutureOffset += NewListSize;
  11739. }
  11740. //
  11741. // See if the new attribute goes here.
  11742. //
  11743. if (CurrentOffset == NewAttributeOffset) {
  11744. //
  11745. // This attribute and all later attributes will be moved
  11746. // by the size of new attribute.
  11747. //
  11748. FutureOffset += SizeNeeded;
  11749. }
  11750. FutureOffset += Attribute1->RecordLength;
  11751. //
  11752. // Check if we are at the split point. We split at this point
  11753. // if the end of the current attribute will be at or beyond the
  11754. // split point after adjusting for adding either an attribute list
  11755. // or new attribute. We make this test >= since these two values
  11756. // will be equal if we reach the last attribute without finding
  11757. // the split point. This way we guarantee a split will happen.
  11758. //
  11759. // Note that we will go to the next attribute if the current attribute
  11760. // is the first attribute in the file record. This can happen if the
  11761. // first attribute is resident and must stay resident but takes up
  11762. // half the file record or more (i.e. large filename attribute).
  11763. // We must make sure to split at least one attribute out of this
  11764. // record.
  11765. //
  11766. // Never split when pointing at $STANDARD_INFORMATION or $ATTRIBUTE_LIST.
  11767. //
  11768. if ((Attribute1->TypeCode > $ATTRIBUTE_LIST) &&
  11769. (FutureOffset >= SizeToStay) &&
  11770. (CurrentOffset != FileRecord1->FirstAttributeOffset)) {
  11771. break;
  11772. }
  11773. CurrentOffset += Attribute1->RecordLength;
  11774. Attribute1 = Add2Ptr( Attribute1, Attribute1->RecordLength );
  11775. }
  11776. SizeToMove = FileRecord1->FirstFreeByte - CurrentOffset;
  11777. //
  11778. // If we are pointing at the attribute list or at the end record
  11779. // we don't do the split. Raise INSUFFICIENT_RESOURCES so our caller
  11780. // knows that we can't do the split.
  11781. //
  11782. if ((Attribute1->TypeCode == $END) || (Attribute1->TypeCode <= $ATTRIBUTE_LIST)) {
  11783. NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
  11784. }
  11785. //
  11786. // Now if the attribute list already exists, we have to look up
  11787. // the first one we are going to move in order to update the
  11788. // attribute list later.
  11789. //
  11790. if (FoundListContext) {
  11791. UNICODE_STRING AttributeName;
  11792. BOOLEAN FoundIt;
  11793. AttributeName.Length =
  11794. AttributeName.MaximumLength = (USHORT)Attribute1->NameLength << 1;
  11795. AttributeName.Buffer = Add2Ptr( Attribute1, Attribute1->NameOffset );
  11796. FoundIt = NtfsLookupAttributeByName( IrpContext,
  11797. Fcb,
  11798. &Fcb->FileReference,
  11799. Attribute1->TypeCode,
  11800. &AttributeName,
  11801. (Attribute1->FormCode == NONRESIDENT_FORM) ?
  11802. &Attribute1->Form.Nonresident.LowestVcn :
  11803. NULL,
  11804. FALSE,
  11805. &MoveContext );
  11806. //
  11807. // If we are splitting the file record between multiple attributes with
  11808. // the same name (i.e. FILE_NAME attributes) then we need to find the
  11809. // correct attribute. Since this is an unusual case we will just scan
  11810. // forwards from the current attribute until we find the correct attribute.
  11811. //
  11812. while (FoundIt && (Attribute1 != NtfsFoundAttribute( &MoveContext ))) {
  11813. FoundIt = NtfsLookupNextAttributeByName( IrpContext,
  11814. Fcb,
  11815. Attribute1->TypeCode,
  11816. &AttributeName,
  11817. FALSE,
  11818. &MoveContext );
  11819. }
  11820. if (!FoundIt) {
  11821. ASSERT( FALSE );
  11822. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  11823. }
  11824. ASSERT(Attribute1 == NtfsFoundAttribute(&MoveContext));
  11825. }
  11826. //
  11827. // Now Attribute1 is pointing to the first attribute to move.
  11828. // Allocate a new file record and move the rest of our attributes
  11829. // over.
  11830. //
  11831. if (FoundListContext) {
  11832. Reference1 = MoveContext.AttributeList.Entry->SegmentReference;
  11833. }
  11834. FileRecord2 = NtfsCloneFileRecord( IrpContext, Fcb, FALSE, &Bcb, &Reference2 );
  11835. //
  11836. // Capture the file record number of the new file record.
  11837. //
  11838. MftFileRecord2 = NtfsFullSegmentNumber( &Reference2 );
  11839. Attribute2 = Add2Ptr( FileRecord2, FileRecord2->FirstAttributeOffset );
  11840. RtlCopyMemory( Attribute2, Attribute1, SizeToMove );
  11841. FileRecord2->FirstFreeByte = (ULONG)FileRecord2->FirstAttributeOffset +
  11842. SizeToMove;
  11843. //
  11844. // Loop to update all of the attribute instance codes
  11845. //
  11846. for (Attribute = Attribute2;
  11847. Attribute < (PATTRIBUTE_RECORD_HEADER)Add2Ptr(FileRecord2, FileRecord2->FirstFreeByte)
  11848. && Attribute->TypeCode != $END;
  11849. Attribute = NtfsGetNextRecord(Attribute)) {
  11850. NtfsCheckRecordBound( Attribute, FileRecord2, Vcb->BytesPerFileRecordSegment );
  11851. if (FoundListContext) {
  11852. UpdateAttributeListEntry( IrpContext,
  11853. Fcb,
  11854. &Reference1,
  11855. Attribute->Instance,
  11856. &Reference2,
  11857. FileRecord2->NextAttributeInstance,
  11858. &ListContext );
  11859. }
  11860. Attribute->Instance = FileRecord2->NextAttributeInstance++;
  11861. }
  11862. //
  11863. // Now log these changes and fix up the first file record.
  11864. //
  11865. FileRecord2->Lsn = NtfsWriteLog( IrpContext,
  11866. Vcb->MftScb,
  11867. Bcb,
  11868. InitializeFileRecordSegment,
  11869. FileRecord2,
  11870. FileRecord2->FirstFreeByte,
  11871. Noop,
  11872. NULL,
  11873. 0,
  11874. LlBytesFromFileRecords( Vcb, MftFileRecord2 ),
  11875. 0,
  11876. 0,
  11877. Vcb->BytesPerFileRecordSegment );
  11878. FileRecord1->Lsn = NtfsWriteLog( IrpContext,
  11879. Vcb->MftScb,
  11880. NtfsFoundBcb(Context),
  11881. WriteEndOfFileRecordSegment,
  11882. &EndCode,
  11883. sizeof(ATTRIBUTE_TYPE_CODE),
  11884. WriteEndOfFileRecordSegment,
  11885. Attribute1,
  11886. SizeToMove,
  11887. NtfsMftOffset( Context ),
  11888. CurrentOffset,
  11889. 0,
  11890. Vcb->BytesPerFileRecordSegment );
  11891. NtfsRestartWriteEndOfFileRecord( FileRecord1,
  11892. Attribute1,
  11893. (PATTRIBUTE_RECORD_HEADER)&EndCode,
  11894. sizeof(ATTRIBUTE_TYPE_CODE) );
  11895. //
  11896. // Finally, create the attribute list attribute if needed.
  11897. //
  11898. if (!FoundListContext) {
  11899. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11900. NtfsInitializeAttributeContext( &ListContext );
  11901. CreateAttributeList( IrpContext,
  11902. Fcb,
  11903. FileRecord1,
  11904. FileRecord2,
  11905. Reference2,
  11906. NULL,
  11907. NewListSize - SIZEOF_RESIDENT_ATTRIBUTE_HEADER,
  11908. &ListContext );
  11909. }
  11910. } finally {
  11911. NtfsUnpinBcb( IrpContext, &Bcb );
  11912. NtfsCleanupAttributeContext( IrpContext, &ListContext );
  11913. NtfsCleanupAttributeContext( IrpContext, &MoveContext );
  11914. }
  11915. }
  11916. VOID
  11917. NtfsRestartWriteEndOfFileRecord (
  11918. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  11919. IN PATTRIBUTE_RECORD_HEADER OldAttribute,
  11920. IN PATTRIBUTE_RECORD_HEADER NewAttributes,
  11921. IN ULONG SizeOfNewAttributes
  11922. )
  11923. /*++
  11924. Routine Description:
  11925. This routine is called both in the running system and at restart to
  11926. modify the end of a file record, such as after it was split in two.
  11927. Arguments:
  11928. FileRecord - Supplies the pointer to the file record.
  11929. OldAttribute - Supplies a pointer to the first attribute to be overwritten.
  11930. NewAttributes - Supplies a pointer to the new attribute(s) to be copied to
  11931. the spot above.
  11932. SizeOfNewAttributes - Supplies the size to be copied in bytes.
  11933. Return Value:
  11934. None.
  11935. --*/
  11936. {
  11937. PAGED_CODE();
  11938. RtlMoveMemory( OldAttribute, NewAttributes, SizeOfNewAttributes );
  11939. FileRecord->FirstFreeByte = PtrOffset(FileRecord, OldAttribute) +
  11940. SizeOfNewAttributes;
  11941. //
  11942. // The size coming in may not be quad aligned.
  11943. //
  11944. FileRecord->FirstFreeByte = QuadAlign( FileRecord->FirstFreeByte );
  11945. }
  11946. //
  11947. // Internal support routine
  11948. //
  11949. PFILE_RECORD_SEGMENT_HEADER
  11950. NtfsCloneFileRecord (
  11951. IN PIRP_CONTEXT IrpContext,
  11952. IN PFCB Fcb,
  11953. IN BOOLEAN MftData,
  11954. OUT PBCB *Bcb,
  11955. OUT PMFT_SEGMENT_REFERENCE FileReference
  11956. )
  11957. /*++
  11958. Routine Description:
  11959. This routine allocates an additional file record for an already existing
  11960. and open file, for the purpose of overflowing attributes to this record.
  11961. Arguments:
  11962. Fcb - Requested file.
  11963. MftData - TRUE if the file record is being cloned to describe the
  11964. $DATA attribute for the Mft.
  11965. Bcb - Returns a pointer to the Bcb for the new file record.
  11966. FileReference - returns the file reference for the new file record.
  11967. Return Value:
  11968. Pointer to the allocated file record.
  11969. --*/
  11970. {
  11971. LONGLONG FileRecordOffset;
  11972. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  11973. PVCB Vcb = Fcb->Vcb;
  11974. PAGED_CODE();
  11975. //
  11976. // First allocate the record.
  11977. //
  11978. *FileReference = NtfsAllocateMftRecord( IrpContext,
  11979. Vcb,
  11980. MftData );
  11981. //
  11982. // Read it in and pin it.
  11983. //
  11984. NtfsPinMftRecord( IrpContext,
  11985. Vcb,
  11986. FileReference,
  11987. TRUE,
  11988. Bcb,
  11989. &FileRecord,
  11990. &FileRecordOffset );
  11991. //
  11992. // Initialize it.
  11993. //
  11994. NtfsInitializeMftRecord( IrpContext,
  11995. Vcb,
  11996. FileReference,
  11997. FileRecord,
  11998. *Bcb,
  11999. BooleanIsDirectory( &Fcb->Info ));
  12000. FileRecord->BaseFileRecordSegment = Fcb->FileReference;
  12001. FileRecord->ReferenceCount = 0;
  12002. FileReference->SequenceNumber = FileRecord->SequenceNumber;
  12003. return FileRecord;
  12004. }
  12005. //
  12006. // Internal support routine
  12007. //
  12008. ULONG
  12009. GetSizeForAttributeList (
  12010. IN PFILE_RECORD_SEGMENT_HEADER FileRecord
  12011. )
  12012. /*++
  12013. Routine Description:
  12014. This routine is designed to calculate the size that will be required for
  12015. an attribute list attribute, for a base file record which is just about
  12016. to split into two file record segments.
  12017. Arguments:
  12018. FileRecord - Pointer to the file record which is just about to split.
  12019. Return Value:
  12020. Size in bytes of the attribute list attribute that will be required,
  12021. not including the attribute header size.
  12022. --*/
  12023. {
  12024. PATTRIBUTE_RECORD_HEADER Attribute;
  12025. ULONG Size = 0;
  12026. PAGED_CODE();
  12027. //
  12028. // Point to first attribute.
  12029. //
  12030. Attribute = Add2Ptr(FileRecord, FileRecord->FirstAttributeOffset);
  12031. //
  12032. // Loop to add up size of required attribute list entries.
  12033. //
  12034. while (Attribute->TypeCode != $END) {
  12035. Size += QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  12036. + ((ULONG) Attribute->NameLength << 1));
  12037. Attribute = Add2Ptr( Attribute, Attribute->RecordLength );
  12038. }
  12039. return Size;
  12040. }
  12041. //
  12042. // Internal support routine
  12043. //
  12044. VOID
  12045. CreateAttributeList (
  12046. IN PIRP_CONTEXT IrpContext,
  12047. IN PFCB Fcb,
  12048. IN PFILE_RECORD_SEGMENT_HEADER FileRecord1,
  12049. IN PFILE_RECORD_SEGMENT_HEADER FileRecord2 OPTIONAL,
  12050. IN MFT_SEGMENT_REFERENCE SegmentReference2,
  12051. IN PATTRIBUTE_RECORD_HEADER OldPosition OPTIONAL,
  12052. IN ULONG SizeOfList,
  12053. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT ListContext
  12054. )
  12055. /*++
  12056. Routine Description:
  12057. This routine is intended to be called to create the attribute list attribute
  12058. the first time. The caller must have already calculated the size required
  12059. for the list to pass into this routine. The caller must have already
  12060. removed any attributes from the base file record (FileRecord1) which are
  12061. not to remain there. He must then pass in a pointer to the base file record
  12062. and optionally a pointer to a second file record from which the new
  12063. attribute list is to be created.
  12064. Arguments:
  12065. Fcb - Requested file.
  12066. FileRecord1 - Pointer to the base file record, currently holding only those
  12067. attributes to be described there.
  12068. FileRecord2 - Optionally points to a second file record from which the
  12069. second half of the attribute list is to be constructed.
  12070. SegmentReference2 - The Mft segment reference of the second file record,
  12071. if one was supplied.
  12072. OldPosition - Should only be specified if FileRecord2 is specified. In this
  12073. case it must point to an attribute position in FileRecord1 from
  12074. which a single attribute was moved to file record 2. It will be
  12075. used as an indication of where the attribute list entry should
  12076. be inserted.
  12077. SizeOfList - Exact size of the attribute list which will be required.
  12078. ListContext - Context resulting from an attempt to look up the attribute
  12079. list attribute, which failed.
  12080. Return Value:
  12081. None
  12082. --*/
  12083. {
  12084. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  12085. PATTRIBUTE_RECORD_HEADER Attribute;
  12086. PATTRIBUTE_LIST_ENTRY AttributeList, ListEntry;
  12087. MFT_SEGMENT_REFERENCE SegmentReference;
  12088. PAGED_CODE();
  12089. //
  12090. // Allocate space to construct the attribute list. (The list
  12091. // cannot be constructed in place, because that would destroy error
  12092. // recovery.)
  12093. //
  12094. ListEntry =
  12095. AttributeList = (PATTRIBUTE_LIST_ENTRY) NtfsAllocatePool(PagedPool, SizeOfList );
  12096. //
  12097. // Use try-finally to deallocate on the way out.
  12098. //
  12099. try {
  12100. //
  12101. // Loop to fill in the attribute list from the two file records
  12102. //
  12103. for (FileRecord = FileRecord1, SegmentReference = Fcb->FileReference;
  12104. FileRecord != NULL;
  12105. FileRecord = ((FileRecord == FileRecord1) ? FileRecord2 : NULL),
  12106. SegmentReference = SegmentReference2) {
  12107. //
  12108. // Point to first attribute.
  12109. //
  12110. Attribute = Add2Ptr( FileRecord, FileRecord->FirstAttributeOffset );
  12111. //
  12112. // Loop to add up size of required attribute list entries.
  12113. //
  12114. while (Attribute->TypeCode != $END) {
  12115. PATTRIBUTE_RECORD_HEADER NextAttribute;
  12116. //
  12117. // See if we are at the remembered position. If so:
  12118. //
  12119. // Save this attribute to be the next one.
  12120. // Point to the single attribute in FileRecord2 instead
  12121. // Clear FileRecord2, as we will "consume" it here.
  12122. // Set the Segment reference in the ListEntry
  12123. //
  12124. if ((Attribute == OldPosition) && (FileRecord2 != NULL)) {
  12125. NextAttribute = Attribute;
  12126. Attribute = Add2Ptr(FileRecord2, FileRecord2->FirstAttributeOffset);
  12127. FileRecord2 = NULL;
  12128. ListEntry->SegmentReference = SegmentReference2;
  12129. //
  12130. // Otherwise, this is the normal loop case. So:
  12131. //
  12132. // Set the next attribute pointer accordingly.
  12133. // Set the Segment reference from the loop control
  12134. //
  12135. } else {
  12136. NextAttribute = Add2Ptr(Attribute, Attribute->RecordLength);
  12137. ListEntry->SegmentReference = SegmentReference;
  12138. }
  12139. //
  12140. // Now fill in the list entry.
  12141. //
  12142. ListEntry->AttributeTypeCode = Attribute->TypeCode;
  12143. ListEntry->RecordLength = (USHORT) QuadAlign( FIELD_OFFSET( ATTRIBUTE_LIST_ENTRY, AttributeName )
  12144. + ((ULONG) Attribute->NameLength << 1));
  12145. ListEntry->AttributeNameLength = Attribute->NameLength;
  12146. ListEntry->AttributeNameOffset =
  12147. (UCHAR)PtrOffset( ListEntry, &ListEntry->AttributeName[0] );
  12148. ListEntry->Instance = Attribute->Instance;
  12149. ListEntry->LowestVcn = 0;
  12150. if (Attribute->FormCode == NONRESIDENT_FORM) {
  12151. ListEntry->LowestVcn = Attribute->Form.Nonresident.LowestVcn;
  12152. }
  12153. if (Attribute->NameLength != 0) {
  12154. RtlCopyMemory( &ListEntry->AttributeName[0],
  12155. Add2Ptr(Attribute, Attribute->NameOffset),
  12156. Attribute->NameLength << 1 );
  12157. }
  12158. ListEntry = Add2Ptr(ListEntry, ListEntry->RecordLength);
  12159. Attribute = NextAttribute;
  12160. }
  12161. }
  12162. //
  12163. // Now create the attribute list attribute.
  12164. //
  12165. NtfsCreateAttributeWithValue( IrpContext,
  12166. Fcb,
  12167. $ATTRIBUTE_LIST,
  12168. NULL,
  12169. AttributeList,
  12170. SizeOfList,
  12171. 0,
  12172. NULL,
  12173. TRUE,
  12174. ListContext );
  12175. } finally {
  12176. NtfsFreePool( AttributeList );
  12177. }
  12178. }
  12179. //
  12180. // Internal support routine
  12181. //
  12182. VOID
  12183. UpdateAttributeListEntry (
  12184. IN PIRP_CONTEXT IrpContext,
  12185. IN PFCB Fcb,
  12186. IN PMFT_SEGMENT_REFERENCE OldFileReference,
  12187. IN USHORT OldInstance,
  12188. IN PMFT_SEGMENT_REFERENCE NewFileReference,
  12189. IN USHORT NewInstance,
  12190. IN OUT PATTRIBUTE_ENUMERATION_CONTEXT ListContext
  12191. )
  12192. /*++
  12193. Routine Description:
  12194. This routine may be called to update a range of the attribute list
  12195. as required by the movement of a range of attributes to a second record.
  12196. The caller must supply a pointer to the file record to which the attributes
  12197. have moved, along with the segment reference of that record.
  12198. Arguments:
  12199. Fcb - Requested file.
  12200. OldFileReference - Old File Reference for attribute
  12201. OldInstance - Old Instance number for attribute
  12202. NewFileReference - New File Reference for attribute
  12203. NewInstance - New Instance number for attribute
  12204. ListContext - The attribute enumeration context which was used to locate
  12205. the attribute list.
  12206. Return Value:
  12207. None
  12208. --*/
  12209. {
  12210. PATTRIBUTE_LIST_ENTRY AttributeList, ListEntry, BeyondList;
  12211. PBCB Bcb = NULL;
  12212. ULONG SizeOfList;
  12213. ATTRIBUTE_LIST_ENTRY NewEntry;
  12214. PATTRIBUTE_RECORD_HEADER Attribute;
  12215. PAGED_CODE();
  12216. //
  12217. // Map the attribute list if the attribute is non-resident. Otherwise the
  12218. // attribute is already mapped and we have a Bcb in the attribute context.
  12219. //
  12220. Attribute = NtfsFoundAttribute( ListContext );
  12221. if (!NtfsIsAttributeResident( Attribute )) {
  12222. NtfsMapAttributeValue( IrpContext,
  12223. Fcb,
  12224. (PVOID *) &AttributeList,
  12225. &SizeOfList,
  12226. &Bcb,
  12227. ListContext );
  12228. //
  12229. // Don't call the Map attribute routine because it NULLs the Bcb in the
  12230. // attribute list. This Bcb is needed for ChangeAttributeValue to mark
  12231. // the page dirty.
  12232. //
  12233. } else {
  12234. AttributeList = (PATTRIBUTE_LIST_ENTRY) NtfsAttributeValue( Attribute );
  12235. SizeOfList = Attribute->Form.Resident.ValueLength;
  12236. }
  12237. //
  12238. // Make sure we unpin the list.
  12239. //
  12240. try {
  12241. //
  12242. // Point beyond the end of the list.
  12243. //
  12244. BeyondList = (PATTRIBUTE_LIST_ENTRY)Add2Ptr( AttributeList, SizeOfList );
  12245. //
  12246. // Loop through all of the attribute list entries until we find the one
  12247. // we need to change.
  12248. //
  12249. for (ListEntry = AttributeList;
  12250. ListEntry < BeyondList;
  12251. ListEntry = NtfsGetNextRecord(ListEntry)) {
  12252. if ((ListEntry->Instance == OldInstance) &&
  12253. NtfsEqualMftRef(&ListEntry->SegmentReference, OldFileReference)) {
  12254. break;
  12255. }
  12256. }
  12257. //
  12258. // Check that an update the the mft preserves the self-describing property
  12259. //
  12260. ASSERT( (Fcb != Fcb->Vcb->MftScb->Fcb) ||
  12261. (ListEntry->AttributeTypeCode != $DATA) ||
  12262. ((ULONGLONG)(ListEntry->LowestVcn) > (NtfsFullSegmentNumber( NewFileReference ) >> Fcb->Vcb->MftToClusterShift)) );
  12263. //
  12264. // We better have found it!
  12265. //
  12266. ASSERT(ListEntry < BeyondList);
  12267. if (ListEntry >= BeyondList) {
  12268. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  12269. }
  12270. //
  12271. // Make a copy of the fixed portion of the attribute list entry,
  12272. // and update to describe the new attribute location.
  12273. //
  12274. RtlCopyMemory( &NewEntry, ListEntry, sizeof(ATTRIBUTE_LIST_ENTRY) );
  12275. NewEntry.SegmentReference = *NewFileReference;
  12276. NewEntry.Instance = NewInstance;
  12277. //
  12278. // Update the attribute list entry.
  12279. //
  12280. NtfsChangeAttributeValue( IrpContext,
  12281. Fcb,
  12282. PtrOffset(AttributeList, ListEntry),
  12283. &NewEntry,
  12284. sizeof(ATTRIBUTE_LIST_ENTRY),
  12285. FALSE,
  12286. TRUE,
  12287. FALSE,
  12288. TRUE,
  12289. ListContext );
  12290. } finally {
  12291. NtfsUnpinBcb( IrpContext, &Bcb );
  12292. }
  12293. }
  12294. //
  12295. // Local support routine
  12296. //
  12297. VOID
  12298. NtfsAddNameToParent (
  12299. IN PIRP_CONTEXT IrpContext,
  12300. IN PSCB ParentScb,
  12301. IN PFCB ThisFcb,
  12302. IN BOOLEAN IgnoreCase,
  12303. IN PBOOLEAN LogIt,
  12304. IN PFILE_NAME FileNameAttr,
  12305. OUT PUCHAR FileNameFlags,
  12306. OUT PQUICK_INDEX QuickIndex OPTIONAL,
  12307. IN PNAME_PAIR NamePair OPTIONAL,
  12308. IN PINDEX_CONTEXT IndexContext OPTIONAL
  12309. )
  12310. /*++
  12311. Routine Description:
  12312. This routine will create the filename attribute with the given name.
  12313. Depending on the IgnoreCase flag, this is either a link or an Ntfs
  12314. name. If it is an Ntfs name, we check if it is also the Dos name.
  12315. We build a file name attribute and then add it via ThisFcb, we then
  12316. add this entry to the parent.
  12317. If the name is a Dos name and we are given tunneling information on
  12318. the long name, we will add the long name attribute as well.
  12319. Arguments:
  12320. ParentScb - This is the parent directory for the file.
  12321. ThisFcb - This is the file to add the filename to.
  12322. IgnoreCase - Indicates if this name is case insensitive. Only for Posix
  12323. will this be FALSE.
  12324. LogIt - Indicates if we should log this operation. If FALSE and this is a large
  12325. name then log the file record and begin logging.
  12326. FileNameAttr - This contains a file name attribute structure to use.
  12327. FileNameFlags - We store a copy of the File name flags used in the file
  12328. name attribute.
  12329. QuickIndex - If specified, we store the information about the location of the
  12330. index entry added.
  12331. NamePair - If specified, we add the tunneled NTFS-only name if the name we are
  12332. directly adding is DOS-only.
  12333. IndexContext - Previous result of doing the lookup for the name in the index.
  12334. Return Value:
  12335. None - This routine will raise on error.
  12336. --*/
  12337. {
  12338. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  12339. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  12340. PAGED_CODE();
  12341. DebugTrace( +1, Dbg, ("NtfsAddNameToParent: Entered\n") );
  12342. NtfsInitializeAttributeContext( &AttrContext );
  12343. //
  12344. // Use a try-finally to facilitate cleanup.
  12345. //
  12346. try {
  12347. //
  12348. // Decide whether the name is a link, Ntfs-Only or Ntfs/8.3 combined name.
  12349. // Update the filename attribute to reflect this.
  12350. //
  12351. if (!IgnoreCase) {
  12352. *FileNameFlags = 0;
  12353. } else {
  12354. UNICODE_STRING FileName;
  12355. FileName.Length = FileName.MaximumLength = (USHORT)(FileNameAttr->FileNameLength * sizeof(WCHAR));
  12356. FileName.Buffer = FileNameAttr->FileName;
  12357. *FileNameFlags = FILE_NAME_NTFS;
  12358. if (NtfsIsFatNameValid( &FileName, FALSE )) {
  12359. *FileNameFlags |= FILE_NAME_DOS;
  12360. }
  12361. //
  12362. // If the name is DOS and there was a tunneled NTFS name, add it first if both names
  12363. // exist in the pair (there may only be one in the long side). Note that we
  12364. // really need to do this first so we lay down the correct filename flags.
  12365. //
  12366. if (NamePair &&
  12367. (NamePair->Long.Length > 0) &&
  12368. (NamePair->Short.Length > 0) &&
  12369. (*FileNameFlags == (FILE_NAME_NTFS | FILE_NAME_DOS))) {
  12370. if (NtfsAddTunneledNtfsOnlyName(IrpContext,
  12371. ParentScb,
  12372. ThisFcb,
  12373. &NamePair->Long,
  12374. LogIt )) {
  12375. //
  12376. // Name didn't conflict and was added, so fix up the FileNameFlags
  12377. //
  12378. *FileNameFlags = FILE_NAME_DOS;
  12379. //
  12380. // Make sure we reposition in the index for the actual insertion.
  12381. //
  12382. IndexContext = NULL;
  12383. //
  12384. // We also need to upcase the short DOS name since we don't know the
  12385. // case of what the user handed us and all DOS names are upcase. Note
  12386. // that prior to tunneling being supported it was not possible for a user
  12387. // to specify a short name, so this is a new situation.
  12388. //
  12389. RtlUpcaseUnicodeString(&FileName, &FileName, FALSE);
  12390. }
  12391. }
  12392. }
  12393. //
  12394. // Now update the file name attribute.
  12395. //
  12396. FileNameAttr->Flags = *FileNameFlags;
  12397. //
  12398. // If we haven't been logging and this is a large name then begin logging.
  12399. //
  12400. if (!(*LogIt) &&
  12401. (FileNameAttr->FileNameLength > 100)) {
  12402. //
  12403. // Look up the file record and log its current state.
  12404. //
  12405. if (!NtfsLookupAttributeByCode( IrpContext,
  12406. ThisFcb,
  12407. &ThisFcb->FileReference,
  12408. $STANDARD_INFORMATION,
  12409. &AttrContext )) {
  12410. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, ThisFcb );
  12411. }
  12412. NtfsPinMappedAttribute( IrpContext, ThisFcb->Vcb, &AttrContext );
  12413. FileRecord = NtfsContainingFileRecord( &AttrContext );
  12414. //
  12415. // Log the current state of the file record.
  12416. //
  12417. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  12418. ThisFcb->Vcb->MftScb,
  12419. NtfsFoundBcb( &AttrContext ),
  12420. InitializeFileRecordSegment,
  12421. FileRecord,
  12422. FileRecord->FirstFreeByte,
  12423. Noop,
  12424. NULL,
  12425. 0,
  12426. NtfsMftOffset( &AttrContext ),
  12427. 0,
  12428. 0,
  12429. ThisFcb->Vcb->BytesPerFileRecordSegment );
  12430. *LogIt = TRUE;
  12431. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12432. NtfsInitializeAttributeContext( &AttrContext );
  12433. }
  12434. //
  12435. // Put it in the file record.
  12436. //
  12437. NtfsCreateAttributeWithValue( IrpContext,
  12438. ThisFcb,
  12439. $FILE_NAME,
  12440. NULL,
  12441. FileNameAttr,
  12442. NtfsFileNameSize( FileNameAttr ),
  12443. 0,
  12444. &FileNameAttr->ParentDirectory,
  12445. *LogIt,
  12446. &AttrContext );
  12447. //
  12448. // Now put it in the index entry.
  12449. //
  12450. NtfsAddIndexEntry( IrpContext,
  12451. ParentScb,
  12452. FileNameAttr,
  12453. NtfsFileNameSize( FileNameAttr ),
  12454. &ThisFcb->FileReference,
  12455. IndexContext,
  12456. QuickIndex );
  12457. } finally {
  12458. DebugUnwind( NtfsAddNameToParent );
  12459. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12460. DebugTrace( -1, Dbg, ("NtfsAddNameToParent: Exit\n") );
  12461. }
  12462. return;
  12463. }
  12464. //
  12465. // Local support routine
  12466. //
  12467. VOID
  12468. NtfsAddDosOnlyName (
  12469. IN PIRP_CONTEXT IrpContext,
  12470. IN PSCB ParentScb,
  12471. IN PFCB ThisFcb,
  12472. IN UNICODE_STRING FileName,
  12473. IN BOOLEAN LogIt,
  12474. IN PUNICODE_STRING SuggestedDosName OPTIONAL
  12475. )
  12476. /*++
  12477. Routine Description:
  12478. This routine is called to build a Dos only name attribute an put it in
  12479. the file record and the parent index. We need to allocate pool large
  12480. enough to hold the name (easy for 8.3) and then check that the generated
  12481. names don't already exist in the parent. Use the suggested name first if
  12482. possible.
  12483. Arguments:
  12484. ParentScb - This is the parent directory for the file.
  12485. ThisFcb - This is the file to add the filename to.
  12486. FileName - This is the file name to add.
  12487. LogIt - Indicates if we should log this operation.
  12488. SuggestedDosName - If supplied, a name to try to use before auto-generation
  12489. Return Value:
  12490. None - This routine will raise on error.
  12491. --*/
  12492. {
  12493. GENERATE_NAME_CONTEXT NameContext;
  12494. PFILE_NAME FileNameAttr;
  12495. UNICODE_STRING Name8dot3;
  12496. PINDEX_ENTRY IndexEntry;
  12497. PBCB IndexEntryBcb;
  12498. UCHAR TrailingDotAdj;
  12499. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  12500. BOOLEAN TrySuggestedDosName = TRUE;
  12501. PAGED_CODE();
  12502. DebugTrace( +1, Dbg, ("NtfsAddDosOnlyName: Entered\n") );
  12503. IndexEntryBcb = NULL;
  12504. RtlZeroMemory( &NameContext, sizeof( GENERATE_NAME_CONTEXT ));
  12505. if (SuggestedDosName == NULL || SuggestedDosName->Length == 0) {
  12506. //
  12507. // The SuggestedDosName can be zero length if we have a tunneled
  12508. // link or a tunneled file which was created whilst short name
  12509. // generation was disabled. It is a bad thing to drop down null
  12510. // filenames ...
  12511. //
  12512. TrySuggestedDosName = FALSE;
  12513. }
  12514. //
  12515. // The maximum length is 24 bytes, but 2 are already defined with the
  12516. // FILE_NAME structure.
  12517. //
  12518. FileNameAttr = NtfsAllocatePool(PagedPool, sizeof( FILE_NAME ) + 22 );
  12519. //
  12520. // Use a try-finally to facilitate cleanup.
  12521. //
  12522. try {
  12523. NtfsInitializeAttributeContext( &AttrContext );
  12524. //
  12525. // Set up the string to hold the generated name. It will be part
  12526. // of the file name attribute structure.
  12527. //
  12528. Name8dot3.Buffer = FileNameAttr->FileName;
  12529. Name8dot3.MaximumLength = 24;
  12530. FileNameAttr->ParentDirectory = ParentScb->Fcb->FileReference;
  12531. FileNameAttr->Flags = FILE_NAME_DOS;
  12532. //
  12533. // Copy the info values into the filename attribute.
  12534. //
  12535. RtlCopyMemory( &FileNameAttr->Info,
  12536. &ThisFcb->Info,
  12537. sizeof( DUPLICATED_INFORMATION ));
  12538. //
  12539. // We will loop indefinitely. We generate a name, look in the parent
  12540. // for it. If found we continue generating. If not then we have the
  12541. // name we need. Attempt to use the suggested name first.
  12542. //
  12543. while( TRUE ) {
  12544. TrailingDotAdj = 0;
  12545. if (TrySuggestedDosName) {
  12546. Name8dot3.Length = SuggestedDosName->Length;
  12547. RtlCopyMemory(Name8dot3.Buffer, SuggestedDosName->Buffer, SuggestedDosName->Length);
  12548. Name8dot3.MaximumLength = SuggestedDosName->MaximumLength;
  12549. } else {
  12550. RtlGenerate8dot3Name( &FileName,
  12551. BooleanFlagOn(NtfsData.Flags,NTFS_FLAGS_ALLOW_EXTENDED_CHAR),
  12552. &NameContext,
  12553. &Name8dot3 );
  12554. if ((Name8dot3.Buffer[(Name8dot3.Length / sizeof( WCHAR )) - 1] == L'.') &&
  12555. (Name8dot3.Length > sizeof( WCHAR ))) {
  12556. TrailingDotAdj = 1;
  12557. }
  12558. }
  12559. FileNameAttr->FileNameLength = (UCHAR)(Name8dot3.Length / sizeof( WCHAR )) - TrailingDotAdj;
  12560. if (!NtfsFindIndexEntry( IrpContext,
  12561. ParentScb,
  12562. FileNameAttr,
  12563. TRUE,
  12564. NULL,
  12565. &IndexEntryBcb,
  12566. &IndexEntry,
  12567. NULL )) {
  12568. break;
  12569. }
  12570. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  12571. if (TrySuggestedDosName) {
  12572. //
  12573. // Failed to use the suggested name, so fix up the 8.3 space
  12574. //
  12575. Name8dot3.Buffer = FileNameAttr->FileName;
  12576. Name8dot3.MaximumLength = 24;
  12577. TrySuggestedDosName = FALSE;
  12578. }
  12579. }
  12580. //
  12581. // We add this entry to the file record.
  12582. //
  12583. NtfsCreateAttributeWithValue( IrpContext,
  12584. ThisFcb,
  12585. $FILE_NAME,
  12586. NULL,
  12587. FileNameAttr,
  12588. NtfsFileNameSize( FileNameAttr ),
  12589. 0,
  12590. &FileNameAttr->ParentDirectory,
  12591. LogIt,
  12592. &AttrContext );
  12593. //
  12594. // We add this entry to the parent.
  12595. //
  12596. NtfsAddIndexEntry( IrpContext,
  12597. ParentScb,
  12598. FileNameAttr,
  12599. NtfsFileNameSize( FileNameAttr ),
  12600. &ThisFcb->FileReference,
  12601. NULL,
  12602. NULL );
  12603. } finally {
  12604. DebugUnwind( NtfsAddDosOnlyName );
  12605. NtfsFreePool( FileNameAttr );
  12606. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  12607. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12608. DebugTrace( -1, Dbg, ("NtfsAddDosOnlyName: Exit -> %08lx\n") );
  12609. }
  12610. return;
  12611. }
  12612. //
  12613. // Local support routine
  12614. //
  12615. BOOLEAN
  12616. NtfsAddTunneledNtfsOnlyName (
  12617. IN PIRP_CONTEXT IrpContext,
  12618. IN PSCB ParentScb,
  12619. IN PFCB ThisFcb,
  12620. IN PUNICODE_STRING FileName,
  12621. IN PBOOLEAN LogIt
  12622. )
  12623. /*++
  12624. Routine Description:
  12625. This routine is called to attempt to insert a tunneled NTFS-only name
  12626. attribute and put it in the file record and the parent index. If the
  12627. name collides with an existing name nothing occurs.
  12628. Arguments:
  12629. ParentScb - This is the parent directory for the file.
  12630. ThisFcb - This is the file to add the filename to.
  12631. FileName - This is the file name to add.
  12632. LogIt - Indicates if we should log this operation. If FALSE and this is a large
  12633. name then log the file record and begin logging.
  12634. Return Value:
  12635. Boolean true if the name is added, false otherwise
  12636. --*/
  12637. {
  12638. PFILE_NAME FileNameAttr;
  12639. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  12640. PINDEX_ENTRY IndexEntry;
  12641. PBCB IndexEntryBcb;
  12642. BOOLEAN Added = FALSE;
  12643. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  12644. PAGED_CODE();
  12645. DebugTrace( +1, Dbg, ("NtfsAddTunneledNtfsOnlyName: Entered\n") );
  12646. IndexEntryBcb = NULL;
  12647. //
  12648. // One WCHAR is already defined with the FILE_NAME structure. It is unfortunate
  12649. // that we need to go to pool to do this ...
  12650. //
  12651. FileNameAttr = NtfsAllocatePool(PagedPool, sizeof( FILE_NAME ) + FileName->Length - sizeof(WCHAR) );
  12652. //
  12653. // Use a try-finally to facilitate cleanup.
  12654. //
  12655. try {
  12656. NtfsInitializeAttributeContext( &AttrContext );
  12657. RtlCopyMemory( FileNameAttr->FileName,
  12658. FileName->Buffer,
  12659. FileName->Length );
  12660. FileNameAttr->FileNameLength = (UCHAR)(FileName->Length / sizeof(WCHAR));
  12661. FileNameAttr->ParentDirectory = ParentScb->Fcb->FileReference;
  12662. FileNameAttr->Flags = FILE_NAME_NTFS;
  12663. //
  12664. // Copy the info values into the filename attribute.
  12665. //
  12666. RtlCopyMemory( &FileNameAttr->Info,
  12667. &ThisFcb->Info,
  12668. sizeof( DUPLICATED_INFORMATION ));
  12669. //
  12670. // Try out the name
  12671. //
  12672. if (!NtfsFindIndexEntry( IrpContext,
  12673. ParentScb,
  12674. FileNameAttr,
  12675. TRUE,
  12676. NULL,
  12677. &IndexEntryBcb,
  12678. &IndexEntry,
  12679. NULL )) {
  12680. //
  12681. // Restore the case of the tunneled name
  12682. //
  12683. RtlCopyMemory( FileNameAttr->FileName,
  12684. FileName->Buffer,
  12685. FileName->Length );
  12686. //
  12687. // If we haven't been logging and this is a large name then begin logging.
  12688. //
  12689. if (!(*LogIt) &&
  12690. (FileName->Length > 200)) {
  12691. //
  12692. // Look up the file record and log its current state.
  12693. //
  12694. if (!NtfsLookupAttributeByCode( IrpContext,
  12695. ThisFcb,
  12696. &ThisFcb->FileReference,
  12697. $STANDARD_INFORMATION,
  12698. &AttrContext )) {
  12699. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, ThisFcb );
  12700. }
  12701. NtfsPinMappedAttribute( IrpContext, ThisFcb->Vcb, &AttrContext );
  12702. FileRecord = NtfsContainingFileRecord( &AttrContext );
  12703. //
  12704. // Log the current state of the file record.
  12705. //
  12706. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  12707. ThisFcb->Vcb->MftScb,
  12708. NtfsFoundBcb( &AttrContext ),
  12709. InitializeFileRecordSegment,
  12710. FileRecord,
  12711. FileRecord->FirstFreeByte,
  12712. Noop,
  12713. NULL,
  12714. 0,
  12715. NtfsMftOffset( &AttrContext ),
  12716. 0,
  12717. 0,
  12718. ThisFcb->Vcb->BytesPerFileRecordSegment );
  12719. *LogIt = TRUE;
  12720. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12721. NtfsInitializeAttributeContext( &AttrContext );
  12722. }
  12723. //
  12724. // We add this entry to the file record.
  12725. //
  12726. NtfsCreateAttributeWithValue( IrpContext,
  12727. ThisFcb,
  12728. $FILE_NAME,
  12729. NULL,
  12730. FileNameAttr,
  12731. NtfsFileNameSize( FileNameAttr ),
  12732. 0,
  12733. &FileNameAttr->ParentDirectory,
  12734. *LogIt,
  12735. &AttrContext );
  12736. //
  12737. // We add this entry to the parent.
  12738. //
  12739. NtfsAddIndexEntry( IrpContext,
  12740. ParentScb,
  12741. FileNameAttr,
  12742. NtfsFileNameSize( FileNameAttr ),
  12743. &ThisFcb->FileReference,
  12744. NULL,
  12745. NULL );
  12746. //
  12747. // Flag the addition
  12748. //
  12749. Added = TRUE;
  12750. }
  12751. } finally {
  12752. DebugUnwind( NtfsAddTunneledNtfsOnlyName );
  12753. NtfsFreePool( FileNameAttr );
  12754. NtfsUnpinBcb( IrpContext, &IndexEntryBcb );
  12755. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  12756. DebugTrace( -1, Dbg, ("NtfsAddTunneledNtfsOnlyName: Exit -> %08lx\n", Added) );
  12757. }
  12758. return Added;
  12759. }
  12760. //
  12761. // Local support routine
  12762. //
  12763. USHORT
  12764. NtfsScanForFreeInstance (
  12765. IN PIRP_CONTEXT IrpContext,
  12766. IN PVCB Vcb,
  12767. IN PFILE_RECORD_SEGMENT_HEADER FileRecord
  12768. )
  12769. /*++
  12770. Routine Description:
  12771. This routine is called when we are adding a new attribute to this file record
  12772. but the instance number is significant. We don't want the instance numbers
  12773. to roll over so we will scan for a free instance number.
  12774. Arguments:
  12775. Vcb - Vcb for this volume.
  12776. FileRecord - This is the file record to look at.
  12777. Return Value:
  12778. USHORT - Return the lowest free instance in the file record.
  12779. --*/
  12780. {
  12781. PATTRIBUTE_RECORD_HEADER Attribute;
  12782. ULONG AttributeCount = 0;
  12783. ULONG CurrentIndex;
  12784. ULONG MinIndex;
  12785. ULONG LowIndex;
  12786. USHORT CurrentMinInstance;
  12787. USHORT CurrentInstances[0x80];
  12788. USHORT LastInstance = 0xffff;
  12789. PAGED_CODE();
  12790. //
  12791. // Insert the existing attributes into our array.
  12792. //
  12793. Attribute = NtfsFirstAttribute( FileRecord );
  12794. while (Attribute->TypeCode != $END) {
  12795. //
  12796. // Store this instance in the current position in the array.
  12797. //
  12798. CurrentInstances[AttributeCount] = Attribute->Instance;
  12799. AttributeCount += 1;
  12800. Attribute = NtfsGetNextRecord( Attribute );
  12801. NtfsCheckRecordBound( Attribute, FileRecord, Vcb->BytesPerFileRecordSegment );
  12802. }
  12803. //
  12804. // If there are no entries then return 0 as the instance to use.
  12805. //
  12806. if (AttributeCount == 0) {
  12807. return 0;
  12808. //
  12809. // If there is only one entry then either return 0 or 1.
  12810. //
  12811. } else if (AttributeCount == 1) {
  12812. if (CurrentInstances[0] == 0) {
  12813. return 1;
  12814. } else {
  12815. return 0;
  12816. }
  12817. }
  12818. //
  12819. // We will start sorting the array. We can stop as soon as we find a gap.
  12820. //
  12821. LowIndex = 0;
  12822. while (LowIndex < AttributeCount) {
  12823. //
  12824. // Walk through from our current position and find the lowest value.
  12825. //
  12826. MinIndex = LowIndex;
  12827. CurrentMinInstance = CurrentInstances[MinIndex];
  12828. CurrentIndex = LowIndex + 1;
  12829. while (CurrentIndex < AttributeCount) {
  12830. if (CurrentInstances[CurrentIndex] < CurrentMinInstance) {
  12831. CurrentMinInstance = CurrentInstances[CurrentIndex];
  12832. MinIndex = CurrentIndex;
  12833. }
  12834. CurrentIndex += 1;
  12835. }
  12836. //
  12837. // If there is a gap between the previous value and the current instance then
  12838. // we are done.
  12839. //
  12840. if ((USHORT) (LastInstance + 1) != CurrentMinInstance) {
  12841. return LastInstance + 1;
  12842. }
  12843. //
  12844. // Otherwise move to the next index.
  12845. //
  12846. CurrentInstances[MinIndex] = CurrentInstances[LowIndex];
  12847. CurrentInstances[LowIndex] = CurrentMinInstance;
  12848. LastInstance = CurrentMinInstance;
  12849. LowIndex += 1;
  12850. }
  12851. //
  12852. // We walked through all of the existing without finding a free entry. Go ahead and
  12853. // return the next known instance.
  12854. //
  12855. return (USHORT) AttributeCount;
  12856. }
  12857. //
  12858. // Local support routine
  12859. //
  12860. VOID
  12861. NtfsMergeFileRecords (
  12862. IN PIRP_CONTEXT IrpContext,
  12863. IN PSCB Scb,
  12864. IN BOOLEAN RestoreContext,
  12865. IN PATTRIBUTE_ENUMERATION_CONTEXT Context
  12866. )
  12867. /*++
  12868. Routine Description:
  12869. This routine is called to possibly merge two file records which each consist of a single hole.
  12870. We are given a context which points to either the first of second record. We always
  12871. remove the second and update the first if we can find the holes.
  12872. NOTE - We always want to remove the second attribute not the first. The first may have a
  12873. TotalAllocated field which we can't lose.
  12874. Arguments:
  12875. Scb - Scb for the stream being modified.
  12876. RestoreContext - Indicates if we should be pointing at the merged record on exit.
  12877. Context - This points to either the first or second record of the merge. On return it will
  12878. be in an indeterminant state unless our caller has specified that we should be pointing
  12879. to the combined record.
  12880. Return Value:
  12881. None
  12882. --*/
  12883. {
  12884. PATTRIBUTE_RECORD_HEADER NewAttribute = NULL;
  12885. PATTRIBUTE_RECORD_HEADER Attribute;
  12886. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  12887. ULONG MappingPairsSize;
  12888. VCN LastVcn;
  12889. VCN RestoreVcn;
  12890. ULONG PassCount = 0;
  12891. VCN NewFinalVcn;
  12892. VCN NewStartVcn;
  12893. PUCHAR NextMappingPairs;
  12894. UCHAR VLength;
  12895. UCHAR LLength;
  12896. ULONG BytesAvail;
  12897. BOOLEAN TryPrior = TRUE;
  12898. PAGED_CODE();
  12899. //
  12900. // Use a try finally to facilitate cleanup.
  12901. //
  12902. try {
  12903. //
  12904. // Capture the file record and attribute.
  12905. //
  12906. Attribute = NtfsFoundAttribute( Context );
  12907. FileRecord = NtfsContainingFileRecord( Context );
  12908. //
  12909. // Remember the end of the current file record and the space available.
  12910. //
  12911. NewFinalVcn = Attribute->Form.Nonresident.HighestVcn;
  12912. RestoreVcn = NewStartVcn = Attribute->Form.Nonresident.LowestVcn;
  12913. BytesAvail = FileRecord->BytesAvailable - FileRecord->FirstFreeByte;
  12914. //
  12915. // Start by checking if we can merge with the following file record.
  12916. //
  12917. if (NtfsLookupNextAttributeForScb( IrpContext, Scb, Context )) {
  12918. Attribute = NtfsFoundAttribute( Context );
  12919. //
  12920. // If this attribute also consists entirely of a hole then merge the
  12921. // previous hole.
  12922. //
  12923. NextMappingPairs = Add2Ptr( Attribute, Attribute->Form.Nonresident.MappingPairsOffset );
  12924. LLength = *NextMappingPairs >> 4;
  12925. VLength = *NextMappingPairs & 0x0f;
  12926. NextMappingPairs = Add2Ptr( NextMappingPairs, LLength + VLength + 1);
  12927. //
  12928. // Perform the merge if the current file record is a hole and
  12929. // there is space in the previous record. There is space if the
  12930. // prior record has at least 8 available bytes or we know that
  12931. // the mapping pairs will only take 8 bytes (6 bytes for the Vcn).
  12932. // We don't want to deal with the rare (if not nonexistent) case
  12933. // where we need to grow the attribute in a full file record.
  12934. // Also check that the next range is contiguous. In some cases we
  12935. // may split an existing filerecord into a hole and an allocated
  12936. // range. We don't want to look ahead if we haven't written the
  12937. // next range.
  12938. //
  12939. if ((Attribute->Form.Nonresident.LowestVcn == NewFinalVcn + 1) &&
  12940. (LLength == 0) &&
  12941. (*NextMappingPairs == 0) &&
  12942. ((BytesAvail >= 8) ||
  12943. (Attribute->Form.Nonresident.HighestVcn - NewStartVcn <= 0x7fffffffffff))) {
  12944. TryPrior = FALSE;
  12945. //
  12946. // Update the new highest vcn value.
  12947. //
  12948. NewFinalVcn = Attribute->Form.Nonresident.HighestVcn;
  12949. }
  12950. }
  12951. //
  12952. // If we couldn't find a following file record then check for a
  12953. // previous file record.
  12954. //
  12955. if (TryPrior) {
  12956. //
  12957. // Reinitialize the context and look up the attribute again if there
  12958. // is no previous or look up the previous attribute.
  12959. //
  12960. if (NewStartVcn != 0) {
  12961. //
  12962. // Back up to the previous file record.
  12963. //
  12964. NewStartVcn -= 1;
  12965. //
  12966. // If we were already at the first file record then there is
  12967. // nothing more to try.
  12968. //
  12969. } else {
  12970. try_return( NOTHING );
  12971. }
  12972. NtfsCleanupAttributeContext( IrpContext, Context );
  12973. NtfsInitializeAttributeContext( Context );
  12974. NtfsLookupAttributeForScb( IrpContext, Scb, &NewStartVcn, Context );
  12975. Attribute = NtfsFoundAttribute( Context );
  12976. FileRecord = NtfsContainingFileRecord( Context );
  12977. NextMappingPairs = Add2Ptr( Attribute, Attribute->Form.Nonresident.MappingPairsOffset );
  12978. LLength = *NextMappingPairs >> 4;
  12979. VLength = *NextMappingPairs & 0x0f;
  12980. NextMappingPairs = Add2Ptr( NextMappingPairs, LLength + VLength + 1);
  12981. BytesAvail = FileRecord->BytesAvailable - FileRecord->FirstFreeByte;
  12982. //
  12983. // Update the new lowest vcn value.
  12984. //
  12985. NewStartVcn = Attribute->Form.Nonresident.LowestVcn;
  12986. //
  12987. // Perform the merge if the current file record is a hole and
  12988. // there is space in the current record. There is space if the
  12989. // current record has at least 8 available bytes or we know that
  12990. // the mapping pairs will only take 8 bytes (6 bytes for the Vcn).
  12991. //
  12992. if ((LLength != 0) ||
  12993. (*NextMappingPairs != 0) ||
  12994. ((BytesAvail < 8) &&
  12995. (NewFinalVcn - NewStartVcn > 0x7fffffffffff))) {
  12996. try_return( NOTHING );
  12997. }
  12998. }
  12999. //
  13000. // Now update the NtfsMcb to reflect the merge. Start by unloading the existing
  13001. // ranges and then define a new range.
  13002. //
  13003. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  13004. NewStartVcn,
  13005. NewFinalVcn,
  13006. FALSE,
  13007. FALSE );
  13008. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  13009. NewStartVcn,
  13010. NewFinalVcn,
  13011. FALSE );
  13012. NtfsAddNtfsMcbEntry( &Scb->Mcb,
  13013. NewStartVcn,
  13014. UNUSED_LCN,
  13015. NewFinalVcn - NewStartVcn + 1,
  13016. FALSE );
  13017. //
  13018. // We need two passes through this loop, one for each record.
  13019. //
  13020. while (TRUE) {
  13021. //
  13022. // Update our pointers to point to the attribute and file record.
  13023. //
  13024. Attribute = NtfsFoundAttribute( Context );
  13025. //
  13026. // If we are at the first record then update the entry.
  13027. //
  13028. if (Attribute->Form.Nonresident.LowestVcn == NewStartVcn) {
  13029. FileRecord = NtfsContainingFileRecord( Context );
  13030. //
  13031. // Allocate a buffer to hold the new attribute. Copy the existing attribute
  13032. // into this buffer and update the final vcn field.
  13033. //
  13034. NewAttribute = NtfsAllocatePool( PagedPool, Attribute->RecordLength + 8 );
  13035. RtlCopyMemory( NewAttribute, Attribute, Attribute->RecordLength );
  13036. LastVcn = NewAttribute->Form.Nonresident.HighestVcn = NewFinalVcn;
  13037. //
  13038. // Now get the new mapping pairs size and build the mapping pairs.
  13039. // We could easily do it by hand but we want to always use the same
  13040. // routines to build these.
  13041. //
  13042. MappingPairsSize = NtfsGetSizeForMappingPairs( &Scb->Mcb,
  13043. 0x10,
  13044. NewStartVcn,
  13045. &LastVcn,
  13046. &LastVcn );
  13047. ASSERT( LastVcn > NewFinalVcn );
  13048. NtfsBuildMappingPairs( &Scb->Mcb,
  13049. NewStartVcn,
  13050. &LastVcn,
  13051. Add2Ptr( NewAttribute,
  13052. NewAttribute->Form.Nonresident.MappingPairsOffset ));
  13053. NewAttribute->RecordLength = QuadAlign( NewAttribute->Form.Nonresident.MappingPairsOffset + MappingPairsSize );
  13054. //
  13055. // Make sure the current attribute is pinned.
  13056. //
  13057. NtfsPinMappedAttribute( IrpContext, Scb->Vcb, Context );
  13058. //
  13059. // Now log the old and new attribute.
  13060. //
  13061. FileRecord->Lsn =
  13062. NtfsWriteLog( IrpContext,
  13063. Scb->Vcb->MftScb,
  13064. NtfsFoundBcb( Context ),
  13065. DeleteAttribute,
  13066. NULL,
  13067. 0,
  13068. CreateAttribute,
  13069. Attribute,
  13070. Attribute->RecordLength,
  13071. NtfsMftOffset( Context ),
  13072. PtrOffset( FileRecord, Attribute ),
  13073. 0,
  13074. Scb->Vcb->BytesPerFileRecordSegment );
  13075. //
  13076. // Now update the file record.
  13077. //
  13078. NtfsRestartRemoveAttribute( IrpContext, FileRecord, PtrOffset( FileRecord, Attribute ));
  13079. FileRecord->Lsn =
  13080. NtfsWriteLog( IrpContext,
  13081. Scb->Vcb->MftScb,
  13082. NtfsFoundBcb( Context ),
  13083. CreateAttribute,
  13084. NewAttribute,
  13085. NewAttribute->RecordLength,
  13086. DeleteAttribute,
  13087. NULL,
  13088. 0,
  13089. NtfsMftOffset( Context ),
  13090. PtrOffset( FileRecord, Attribute ),
  13091. 0,
  13092. Scb->Vcb->BytesPerFileRecordSegment );
  13093. NtfsRestartInsertAttribute( IrpContext,
  13094. FileRecord,
  13095. PtrOffset( FileRecord, Attribute ),
  13096. NewAttribute,
  13097. NULL,
  13098. NULL,
  13099. 0 );
  13100. //
  13101. // Now we want to move to the next attribute and remove it if we
  13102. // haven't already.
  13103. //
  13104. if (PassCount == 0) {
  13105. if (!NtfsLookupNextAttributeForScb( IrpContext, Scb, Context )) {
  13106. ASSERTMSG( "Could not find next attribute for Scb\n", FALSE );
  13107. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  13108. }
  13109. //
  13110. // We are pointing to the correct file record in this case.
  13111. //
  13112. } else {
  13113. RestoreContext = FALSE;
  13114. }
  13115. } else {
  13116. //
  13117. // Tell the delete routine to log the data and free the file record if
  13118. // possible but not to deallocate any clusters. Since there are no
  13119. // clusters we can save the overhead of calling DeleteAllocation.
  13120. //
  13121. NtfsDeleteAttributeRecord( IrpContext,
  13122. Scb->Fcb,
  13123. DELETE_LOG_OPERATION | DELETE_RELEASE_FILE_RECORD,
  13124. Context );
  13125. //
  13126. // If this is our first pass then move to the previous file record
  13127. //
  13128. if (PassCount == 0) {
  13129. NtfsCleanupAttributeContext( IrpContext, Context );
  13130. NtfsInitializeAttributeContext( Context );
  13131. NtfsLookupAttributeForScb( IrpContext, Scb, &NewStartVcn, Context );
  13132. }
  13133. }
  13134. if (PassCount == 1) { break; }
  13135. PassCount += 1;
  13136. }
  13137. try_exit: NOTHING;
  13138. //
  13139. // Restore the context if required.
  13140. //
  13141. if (RestoreContext) {
  13142. NtfsCleanupAttributeContext( IrpContext, Context );
  13143. NtfsInitializeAttributeContext( Context );
  13144. NtfsLookupAttributeForScb( IrpContext, Scb, &RestoreVcn, Context );
  13145. }
  13146. } finally {
  13147. DebugUnwind( NtfsMergeFileRecords );
  13148. if (NewAttribute != NULL) {
  13149. NtfsFreePool( NewAttribute );
  13150. }
  13151. }
  13152. return;
  13153. }
  13154. //
  13155. // Local support routine
  13156. //
  13157. NTSTATUS
  13158. NtfsCheckLocksInZeroRange (
  13159. IN PIRP_CONTEXT IrpContext,
  13160. IN PIRP Irp,
  13161. IN PSCB Scb,
  13162. IN PFILE_OBJECT FileObject,
  13163. IN PLONGLONG StartingOffset,
  13164. IN ULONG ByteCount
  13165. )
  13166. /*++
  13167. Routine Description:
  13168. This routine is called from ZeroRangeInStream to verify that we can modify the data
  13169. in the specified range. We check both oplocks and filelocks here.
  13170. Arguments:
  13171. Irp - This is the Irp for the request. We set the next stack location to look like
  13172. a write so that the file lock package has some context to use.
  13173. Scb - Scb for the stream being modified.
  13174. FileObject - File object used to originate the request.
  13175. StartingOffset - This is the offset for the start of the request.
  13176. ByteCount - This is the length of the current request.
  13177. Return Value:
  13178. NTSTATUS - STATUS_PENDING if the request is posted for an oplock operation, STATUS_SUCCESS
  13179. if the operation can proceed. Otherwise this is the status to fail the request with.
  13180. --*/
  13181. {
  13182. PIO_STACK_LOCATION IrpSp;
  13183. NTSTATUS Status;
  13184. PAGED_CODE();
  13185. Status = FsRtlCheckOplock( &Scb->ScbType.Data.Oplock,
  13186. Irp,
  13187. IrpContext,
  13188. NtfsOplockComplete,
  13189. NtfsPrePostIrp );
  13190. //
  13191. // Proceed if we have SUCCESS.
  13192. //
  13193. if (Status == STATUS_SUCCESS) {
  13194. //
  13195. // This oplock call can affect whether fast IO is possible.
  13196. // We may have broken an oplock to no oplock held. If the
  13197. // current state of the file is FastIoIsNotPossible then
  13198. // recheck the fast IO state.
  13199. //
  13200. if (Scb->Header.IsFastIoPossible == FastIoIsNotPossible) {
  13201. NtfsAcquireFsrtlHeader( Scb );
  13202. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  13203. NtfsReleaseFsrtlHeader( Scb );
  13204. }
  13205. //
  13206. // We have to check for write access according to the current
  13207. // state of the file locks.
  13208. //
  13209. if (Scb->ScbType.Data.FileLock != NULL) {
  13210. //
  13211. // Update the Irp to point to the next stack location.
  13212. //
  13213. try {
  13214. IoSetNextIrpStackLocation( Irp );
  13215. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  13216. IrpSp->MajorFunction = IRP_MJ_WRITE;
  13217. IrpSp->MinorFunction = 0;
  13218. IrpSp->Flags = 0;
  13219. IrpSp->Control = 0;
  13220. IrpSp->Parameters.Write.Length = ByteCount;
  13221. IrpSp->Parameters.Write.Key = 0;
  13222. IrpSp->Parameters.Write.ByteOffset.QuadPart = *StartingOffset;
  13223. IrpSp->DeviceObject = Scb->Vcb->Vpb->DeviceObject;
  13224. IrpSp->FileObject = FileObject;
  13225. if (!FsRtlCheckLockForWriteAccess( Scb->ScbType.Data.FileLock, Irp )) {
  13226. Status = STATUS_FILE_LOCK_CONFLICT;
  13227. }
  13228. //
  13229. // Always handle the exception initially in order to restore the Irp.
  13230. //
  13231. } except( EXCEPTION_EXECUTE_HANDLER ) {
  13232. //
  13233. // Zero out the current stack location and back up one position.
  13234. //
  13235. Status = GetExceptionCode();
  13236. }
  13237. //
  13238. // Restore the Irp to its previous state.
  13239. //
  13240. IoSkipCurrentIrpStackLocation( Irp );
  13241. //
  13242. // Raise any non-success status.
  13243. //
  13244. if (Status != STATUS_SUCCESS) {
  13245. NtfsRaiseStatus( IrpContext, Status, NULL, Scb->Fcb );
  13246. }
  13247. }
  13248. }
  13249. return Status;
  13250. }