/// <remarks> /// https://blogs.technet.microsoft.com/askcore/2009/10/16/the-four-stages-of-ntfs-file-growth/ /// </remarks> public void UpdateSegments(int bytesPerFileRecordSegment, byte minorNTFSVersion) { m_segments[0].ReferenceCount = ReferenceCount; m_segments[0].IsInUse = IsInUse; m_segments[0].IsDirectory = IsDirectory; List <AttributeRecord> attributes = this.Attributes; foreach (FileRecordSegment segment in m_segments) { segment.ImmediateAttributes.Clear(); segment.NextAttributeInstance = 0; } int segmentLength = bytesPerFileRecordSegment - FileRecordSegment.GetNumberOfBytesAvailable(bytesPerFileRecordSegment, minorNTFSVersion); foreach (AttributeRecord attribute in attributes) { segmentLength += (int)attribute.RecordLength; } if (segmentLength <= bytesPerFileRecordSegment) { // A single record segment is needed foreach (AttributeRecord attribute in attributes) { m_segments[0].AddAttributeRecord(attribute.Clone()); } // Free the rest of the segments, if there are any for (int index = 1; index < m_segments.Count; index++) { m_segments[index].IsInUse = false; } } else { // We slice the attributes and put them in segments, the attribute list will be built by the caller after the new segments will be allocated FileRecordHelper.SliceAttributes(m_segments, attributes, bytesPerFileRecordSegment, minorNTFSVersion); } }
/// <remarks> /// An attribute list MUST be sorted by AttributeType with a secondary sort by AttributeName. /// </remarks> public static List <AttributeListEntry> BuildAttributeList(List <FileRecordSegment> segments, int bytesPerFileRecordSegment, ushort minorNTFSVersion) { int bytesAvailableInSegment = FileRecordSegment.GetNumberOfBytesAvailable(bytesPerFileRecordSegment, minorNTFSVersion); List <AttributeListEntry> result = new List <AttributeListEntry>(); foreach (FileRecordSegment segment in segments) { foreach (AttributeRecord attribute in segment.ImmediateAttributes) { AttributeListEntry entry = new AttributeListEntry(); entry.AttributeType = attribute.AttributeType; if (attribute is NonResidentAttributeRecord) { entry.LowestVCN = ((NonResidentAttributeRecord)attribute).LowestVCN; } entry.SegmentReference = segment.SegmentReference; entry.Instance = attribute.Instance; entry.AttributeName = attribute.Name; result.Add(entry); } } return(result); }
public static void SliceAttributes(List <FileRecordSegment> segments, List <AttributeRecord> attributes, int bytesPerFileRecordSegment, ushort minorNTFSVersion) { int bytesAvailableInSegment = FileRecordSegment.GetNumberOfBytesAvailable(bytesPerFileRecordSegment, minorNTFSVersion); LinkedList <KeyValuePair <AttributeRecord, bool> > remainingAttributes = new LinkedList <KeyValuePair <AttributeRecord, bool> >(); FileRecordSegment baseFileRecordSegment = segments[0]; long segmentNumber = baseFileRecordSegment.SegmentNumber; bool isMftFileRecord = (segmentNumber == MasterFileTable.MasterFileTableSegmentNumber || segmentNumber == MasterFileTable.MftMirrorSegmentNumber); foreach (AttributeRecord attribute in attributes) { if (attribute.AttributeType == AttributeType.StandardInformation || attribute.AttributeType == AttributeType.FileName) { baseFileRecordSegment.ImmediateAttributes.Add(attribute); } else if (isMftFileRecord && attribute.AttributeType == AttributeType.Data) { List <NonResidentAttributeRecord> slices = SliceAttributeRecord((NonResidentAttributeRecord)attribute, bytesPerFileRecordSegment / 2, bytesAvailableInSegment); baseFileRecordSegment.ImmediateAttributes.Add(slices[0]); slices.RemoveAt(0); foreach (NonResidentAttributeRecord slice in slices) { remainingAttributes.AddLast(new KeyValuePair <AttributeRecord, bool>(slice, true)); } } else { remainingAttributes.AddLast(new KeyValuePair <AttributeRecord, bool>(attribute, false)); } } int segmentIndex = 1; int remainingLengthInCurrentSegment = bytesAvailableInSegment; while (remainingAttributes.Count > 0) { AttributeRecord attribute = remainingAttributes.First.Value.Key; bool isSlice = remainingAttributes.First.Value.Value; if (segmentIndex == segments.Count) { MftSegmentReference newSegmentReference = MftSegmentReference.NullReference; FileRecordSegment newFileRecordSegment = new FileRecordSegment(newSegmentReference.SegmentNumber, newSegmentReference.SequenceNumber, baseFileRecordSegment.SegmentReference); newFileRecordSegment.IsInUse = true; segments.Add(newFileRecordSegment); } if (attribute.RecordLength <= remainingLengthInCurrentSegment) { remainingLengthInCurrentSegment -= (int)attribute.RecordLength; segments[segmentIndex].ImmediateAttributes.Add(attribute); remainingAttributes.RemoveFirst(); // Instead of renumbering each attribute slice in the new FileRecordSegment, we use the original Instance number. if (segments[segmentIndex].NextAttributeInstance <= attribute.Instance) { segments[segmentIndex].NextAttributeInstance = (ushort)(attribute.Instance + 1); } } else { if (attribute is ResidentAttributeRecord || isSlice) { segmentIndex++; remainingLengthInCurrentSegment = bytesAvailableInSegment; } else { NonResidentAttributeRecord nonResidentAttribute = ((NonResidentAttributeRecord)attribute); List <NonResidentAttributeRecord> slices = SliceAttributeRecord((NonResidentAttributeRecord)attribute, remainingLengthInCurrentSegment, bytesAvailableInSegment); remainingAttributes.RemoveFirst(); slices.Reverse(); foreach (NonResidentAttributeRecord slice in slices) { remainingAttributes.AddFirst(new KeyValuePair <AttributeRecord, bool>(slice, true)); } } } } }