public override AttributeRecord Clone() { NonResidentAttributeRecord clone = (NonResidentAttributeRecord)this.MemberwiseClone(); clone.m_dataRunSequence = m_dataRunSequence.Clone(); return(clone); }
public NonResidentAttributeData(NTFSVolume volume, FileRecord fileRecord, NonResidentAttributeRecord attributeRecord) { m_volume = volume; m_fileRecord = fileRecord; m_attributeRecord = attributeRecord; m_contentType = GetContentType(fileRecord, attributeRecord.AttributeType); }
public void Extend(ulong additionalLengthInBytes) { ulong currentSize = this.Length; if (m_attributeRecord is NonResidentAttributeRecord) { NonResidentAttributeData attributeData = new NonResidentAttributeData(m_volume, m_fileRecord, (NonResidentAttributeRecord)m_attributeRecord); attributeData.Extend(additionalLengthInBytes); } else { byte[] data = ((ResidentAttributeRecord)m_attributeRecord).Data; ulong finalDataLength = (uint)data.Length + additionalLengthInBytes; ulong finalRecordLength = (uint)m_attributeRecord.RecordLength + additionalLengthInBytes; if (finalRecordLength >= (ulong)m_volume.AttributeRecordLengthToMakeNonResident && m_attributeRecord.AttributeType != AttributeType.AttributeList) // We will create an attribute list with the right attribute form in advance. { // Convert the attribute to non-resident long clustersToAllocate = (long)Math.Ceiling((double)finalDataLength / m_volume.BytesPerCluster); if (clustersToAllocate > m_volume.NumberOfFreeClusters) { throw new DiskFullException(); } NonResidentAttributeRecord attributeRecord = NonResidentAttributeRecord.Create(m_attributeRecord.AttributeType, m_attributeRecord.Name); NonResidentAttributeData attributeData = new NonResidentAttributeData(m_volume, null, attributeRecord); attributeData.Extend(finalDataLength); if (data.Length % m_volume.BytesPerCluster > 0) { int fillDataLength = m_volume.BytesPerCluster - (data.Length % m_volume.BytesPerCluster); if ((uint)data.Length + (uint)fillDataLength < finalDataLength) { // Only the last cluster can be partially used, if this is not the last cluster, zero-fill it data = ByteUtils.Concatenate(data, new byte[fillDataLength]); } } attributeData.WriteClusters(0, data); // Note that we overwrite the old attribute only after writing the non-resident data if (m_fileRecord != null) { m_fileRecord.RemoveAttributeRecord(m_attributeRecord.AttributeType, m_attributeRecord.Name); FileRecordHelper.InsertSorted(m_fileRecord.Attributes, attributeRecord); } m_attributeRecord = attributeRecord; } else { int currentLength = data.Length; byte[] temp = new byte[currentLength + (int)additionalLengthInBytes]; Array.Copy(data, temp, data.Length); ((ResidentAttributeRecord)m_attributeRecord).Data = temp; } if (m_fileRecord != null) { m_volume.UpdateFileRecord(m_fileRecord); } } }
private static int CompareNonResidentAttributes(NonResidentAttributeRecord attribute1, NonResidentAttributeRecord attribute2) { int result = CompareAttributeTypes(attribute1, attribute2); if (result == 0) { result = attribute1.LowestVCN.CompareTo(attribute2.LowestVCN); } return(result); }
public static AttributeRecord Create(AttributeType type, string name, bool isResident) { if (isResident) { return(ResidentAttributeRecord.Create(type, name)); } else { return(NonResidentAttributeRecord.Create(type, name)); } }
/// <remarks> /// Only non-resident attributes can be fragmented. /// References: /// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc976808(v=technet.10) /// https://blogs.technet.microsoft.com/askcore/2009/10/16/the-four-stages-of-ntfs-file-growth/ /// </remarks> public static List <AttributeRecord> GetAssembledAttributes(List <FileRecordSegment> segments) { List <AttributeRecord> result = new List <AttributeRecord>(); // If two non-resident attributes have the same AttributeType and Name, then we need to assemble them back together. // Additional fragments immediately follow after the initial fragment. AttributeType currentAttributeType = AttributeType.None; string currentAttributeName = String.Empty; List <NonResidentAttributeRecord> fragments = new List <NonResidentAttributeRecord>(); foreach (FileRecordSegment segment in segments) { foreach (AttributeRecord attribute in segment.ImmediateAttributes) { if (attribute.AttributeType == AttributeType.AttributeList) { continue; } bool additionalFragment = (attribute is NonResidentAttributeRecord) && (fragments.Count > 0) && (attribute.AttributeType == currentAttributeType) && (attribute.Name == currentAttributeName); if (!additionalFragment && fragments.Count > 0) { NonResidentAttributeRecord assembledAttribute = AssembleFragments(fragments, segments[0].NextAttributeInstance); segments[0].NextAttributeInstance++; result.Add(assembledAttribute); fragments.Clear(); } if (attribute is ResidentAttributeRecord) { result.Add(attribute); } else { fragments.Add((NonResidentAttributeRecord)attribute); if (!additionalFragment) { currentAttributeType = attribute.AttributeType; currentAttributeName = attribute.Name; } } } } if (fragments.Count > 0) { NonResidentAttributeRecord assembledAttribute = AssembleFragments(fragments, segments[0].NextAttributeInstance); segments[0].NextAttributeInstance++; result.Add(assembledAttribute); } return(result); }
private static NonResidentAttributeRecord FitMaxNumberOfRuns(NonResidentAttributeRecord record, int runIndex, int availableLength) { // Each attribute record is aligned to 8-byte boundary, we must have enough room for padding availableLength = (int)Math.Floor((double)availableLength / 8) * 8; // Note that we're using the original record Instance instead of using the FileRecordSegment.NextAttributeInstance NonResidentAttributeRecord slice = new NonResidentAttributeRecord(record.AttributeType, record.Name, record.Instance); DataRunSequence dataRuns = record.DataRunSequence; long clusterCount = 0; for (int index = 0; index < runIndex; index++) { clusterCount += dataRuns[index].RunLength; } slice.LowestVCN = clusterCount; slice.DataRunSequence.Add(dataRuns[runIndex]); if (runIndex == 0) { slice.CompressionUnit = record.CompressionUnit; slice.AllocatedLength = record.AllocatedLength; slice.FileSize = record.FileSize; slice.ValidDataLength = record.ValidDataLength; } else { // The DataRunSequence of each NonResidentDataRecord fragment starts at absolute LCN long runLength = dataRuns[runIndex].RunLength; long runStartLCN = dataRuns.GetDataClusterLCN(clusterCount); slice.DataRunSequence[0] = new DataRun(runLength, runStartLCN); } clusterCount += dataRuns[runIndex].RunLength; int sliceRecordLength = NonResidentAttributeRecord.HeaderLength + record.Name.Length * 2 + slice.DataRunSequence.RecordLength; if (sliceRecordLength > availableLength) { return(null); } runIndex++; while (runIndex < dataRuns.Count && sliceRecordLength + dataRuns[runIndex].RecordLength <= availableLength) { slice.DataRunSequence.Add(record.DataRunSequence[runIndex]); sliceRecordLength += dataRuns[runIndex].RecordLength; clusterCount += dataRuns[runIndex].RunLength; runIndex++; } slice.HighestVCN = clusterCount - 1; return(slice); }
public AttributeRecord CreateAttributeListRecord(bool isResident) { AttributeRecord attribute; if (isResident) { attribute = AttributeRecord.Create(AttributeType.AttributeList, String.Empty, NextAttributeInstance); } else { attribute = NonResidentAttributeRecord.Create(AttributeType.AttributeList, String.Empty, NextAttributeInstance); } NextAttributeInstance++; FileRecordHelper.InsertSorted(m_immediateAttributes, attribute); return(attribute); }
private static NonResidentAttributeRecord AssembleFragments(List <NonResidentAttributeRecord> attributeFragments, ushort nextAttributeInstance) { // Attribute fragments are written to disk sorted by LowestVCN NonResidentAttributeRecord firstFragment = attributeFragments[0]; if (firstFragment.LowestVCN != 0) { string message = String.Format("Attribute fragments must be sorted. Attribute type: {0}", firstFragment.AttributeType); throw new InvalidDataException(message); } NonResidentAttributeRecord attribute = NonResidentAttributeRecord.Create(firstFragment.AttributeType, firstFragment.Name, nextAttributeInstance); attribute.Flags = firstFragment.Flags; attribute.LowestVCN = 0; attribute.HighestVCN = -1; attribute.CompressionUnit = firstFragment.CompressionUnit; attribute.AllocatedLength = firstFragment.AllocatedLength; attribute.FileSize = firstFragment.FileSize; attribute.ValidDataLength = firstFragment.ValidDataLength; foreach (NonResidentAttributeRecord attributeFragment in attributeFragments) { if (attributeFragment.LowestVCN == attribute.HighestVCN + 1) { // The DataRunSequence of each NonResidentDataRecord fragment starts at absolute LCN, // We need to convert it to relative offset before adding it to the base DataRunSequence long runLength = attributeFragment.DataRunSequence[0].RunLength; long absoluteOffset = attributeFragment.DataRunSequence[0].RunOffset; long previousLCN = attribute.DataRunSequence.LastDataRunStartLCN; long relativeOffset = absoluteOffset - previousLCN; int runIndex = attribute.DataRunSequence.Count; attribute.DataRunSequence.AddRange(attributeFragment.DataRunSequence); attribute.DataRunSequence[runIndex] = new DataRun(runLength, relativeOffset); attribute.HighestVCN = attributeFragment.HighestVCN; } else { throw new InvalidDataException("Invalid attribute fragments order"); } } return(attribute); }
private static List <NonResidentAttributeRecord> SliceAttributeRecord(NonResidentAttributeRecord record, int remainingLengthInCurrentSegment, int bytesAvailableInSegment) { List <NonResidentAttributeRecord> result = new List <NonResidentAttributeRecord>(); int numberOfRunsFitted = 0; int availableLength = remainingLengthInCurrentSegment; while (numberOfRunsFitted < record.DataRunSequence.Count) { NonResidentAttributeRecord slice = FitMaxNumberOfRuns(record, numberOfRunsFitted, availableLength); if (slice != null) { result.Add(slice); numberOfRunsFitted += slice.DataRunSequence.Count; } availableLength = bytesAvailableInSegment; } return(result); }
public static List <AttributeRecord> GetAssembledAttributes(List <FileRecordSegment> segments) { List <AttributeRecord> result = new List <AttributeRecord>(); // we need to assemble fragmented attributes (if there are any) // if two attributes have the same AttributeType and Name, then we need to assemble them back together. // Note: only non-resident attributes can be fragmented // Reference: http://technet.microsoft.com/en-us/library/cc976808.aspx Dictionary <KeyValuePair <AttributeType, string>, List <NonResidentAttributeRecord> > fragments = new Dictionary <KeyValuePair <AttributeType, string>, List <NonResidentAttributeRecord> >(); foreach (FileRecordSegment segment in segments) { foreach (AttributeRecord attribute in segment.ImmediateAttributes) { if (attribute is ResidentAttributeRecord) { result.Add(attribute); } else { KeyValuePair <AttributeType, string> key = new KeyValuePair <AttributeType, string>(attribute.AttributeType, attribute.Name); if (fragments.ContainsKey(key)) { fragments[key].Add((NonResidentAttributeRecord)attribute); } else { List <NonResidentAttributeRecord> attributeFragments = new List <NonResidentAttributeRecord>(); attributeFragments.Add((NonResidentAttributeRecord)attribute); fragments.Add(key, attributeFragments); } } } } // assemble all non-resident attributes foreach (List <NonResidentAttributeRecord> attributeFragments in fragments.Values) { // we assume attribute fragments are written to disk sorted by LowestVCN NonResidentAttributeRecord baseAttribute = attributeFragments[0]; if (baseAttribute.LowestVCN != 0) { string message = String.Format("Attribute fragments must be sorted, MftSegmentNumber: {0}, attribute type: {1}", segments[0].MftSegmentNumber, baseAttribute.AttributeType); throw new InvalidDataException(message); } if (baseAttribute.DataRunSequence.DataClusterCount != baseAttribute.HighestVCN + 1) { string message = String.Format("Cannot properly assemble data run sequence 0, expected length: {0}, sequence length: {1}", baseAttribute.HighestVCN + 1, baseAttribute.DataRunSequence.DataClusterCount); throw new InvalidDataException(message); } for (int index = 1; index < attributeFragments.Count; index++) { NonResidentAttributeRecord attributeFragment = attributeFragments[index]; if (attributeFragment.LowestVCN == baseAttribute.HighestVCN + 1) { // The DataRunSequence of each additional file record segment starts at absolute LCN, // so we need to convert it to relative offset before adding it to the base DataRunSequence long absoluteOffset = attributeFragment.DataRunSequence[0].RunOffset; long previousLCN = baseAttribute.DataRunSequence.LastDataRunStartLCN; long relativeOffset = absoluteOffset - previousLCN; attributeFragment.DataRunSequence[0].RunOffset = relativeOffset; baseAttribute.DataRunSequence.AddRange(attributeFragment.DataRunSequence); baseAttribute.HighestVCN = attributeFragment.HighestVCN; if (baseAttribute.DataRunSequence.DataClusterCount != baseAttribute.HighestVCN + 1) { string message = String.Format("Cannot properly assemble data run sequence, expected length: {0}, sequence length: {1}", baseAttribute.HighestVCN + 1, baseAttribute.DataRunSequence.DataClusterCount); throw new InvalidDataException(message); } } else { throw new InvalidDataException("Invalid attribute fragments order"); } } result.Add(baseAttribute); } return(result); }
public static NTFSVolume Format(Volume volume, byte majorNTFSVersion, byte minorNTFSVersion, int bytesPerCluster, string volumeLabel) { if (volumeLabel.Length > VolumeNameRecord.MaxVolumeNameLength) { throw new InvalidNameException(); } if (bytesPerCluster % volume.BytesPerSector > 0) { throw new ArgumentException("bytesPerCluster must be a multiple of volume.BytesPerSector"); } if (majorNTFSVersion != 3 || (minorNTFSVersion != 0 && minorNTFSVersion != 1)) { throw new NotSupportedException(); } long volumeClusterCount = (volume.Size - NTFSBootRecord.Length) / bytesPerCluster; // We wish to make WriteVolumeBitmap() as simple as possible so we use a multiple of ExtendGranularity to avoid having to set bits at the end of the bitmap volumeClusterCount = (long)Math.Floor((double)volumeClusterCount / (VolumeBitmap.ExtendGranularity * 8)) * (VolumeBitmap.ExtendGranularity * 8); int sectorsPerCluster = bytesPerCluster / volume.BytesPerSector; int bytesPerFileRecordSegment = 1024; // Supported values are 1024 or 4096 (when formatted with /L) int bytesPerIndexRecord = 4096; // Legal values are 1024, 2048 or 4096. NTFS v5.1 driver will always use 4096. int bootSegmentFileSize = 8192; int bootSegmentAllocatedLength = (int)Math.Ceiling((double)bootSegmentFileSize / bytesPerCluster) * bytesPerCluster; FileNameRecord bootFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$Boot", false, DateTime.Now); bootFileNameRecord.AllocatedLength = (ulong)bootSegmentAllocatedLength; bootFileNameRecord.FileSize = (ulong)bootSegmentFileSize; bootFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment bootSegment = CreateBaseRecordSegment(MasterFileTable.BootSegmentNumber, (ushort)MasterFileTable.BootSegmentNumber, bootFileNameRecord); bootSegment.ReferenceCount = 1; NonResidentAttributeRecord bootDataRecord = (NonResidentAttributeRecord)bootSegment.CreateAttributeRecord(AttributeType.Data, String.Empty, false); bootDataRecord.AllocatedLength = (ulong)bootSegmentAllocatedLength; bootDataRecord.FileSize = (ulong)bootSegmentFileSize; bootDataRecord.ValidDataLength = (ulong)bootSegmentFileSize; int bootDataClusterCount = (int)Math.Ceiling((double)8192 / bytesPerCluster); long bootDataStartLCN = 0; bootDataRecord.DataRunSequence.Add(new DataRun(bootDataClusterCount, bootDataStartLCN)); bootDataRecord.HighestVCN = bootDataClusterCount - 1; long volumeBitmapFileSize = (long)Math.Ceiling((double)volumeClusterCount / (VolumeBitmap.ExtendGranularity * 8)) * VolumeBitmap.ExtendGranularity; long numberOfVolumeBitmapClusters = (long)Math.Ceiling((double)volumeBitmapFileSize / bytesPerCluster); long volumeBitmapAllocatedLength = numberOfVolumeBitmapClusters * bytesPerCluster; FileNameRecord volumeBitmapFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$Bitmap", false, DateTime.Now); volumeBitmapFileNameRecord.AllocatedLength = (ulong)volumeBitmapAllocatedLength; volumeBitmapFileNameRecord.FileSize = (ulong)volumeBitmapFileSize; volumeBitmapFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment volumeBitmapSegment = CreateBaseRecordSegment(MasterFileTable.BitmapSegmentNumber, (ushort)MasterFileTable.BitmapSegmentNumber, volumeBitmapFileNameRecord); volumeBitmapSegment.ReferenceCount = 1; NonResidentAttributeRecord volumeBitmapDataRecord = (NonResidentAttributeRecord)volumeBitmapSegment.CreateAttributeRecord(AttributeType.Data, String.Empty, false); long volumeBitmapStartLCN = bootDataClusterCount; volumeBitmapDataRecord.AllocatedLength = (ulong)volumeBitmapAllocatedLength; volumeBitmapDataRecord.FileSize = (ulong)volumeBitmapFileSize; volumeBitmapDataRecord.ValidDataLength = (ulong)volumeBitmapFileSize; volumeBitmapDataRecord.DataRunSequence.Add(new DataRun(numberOfVolumeBitmapClusters, volumeBitmapStartLCN)); volumeBitmapDataRecord.HighestVCN = numberOfVolumeBitmapClusters - 1; int numberOfMftRecords = 64; int mftDataLength = numberOfMftRecords * bytesPerFileRecordSegment; FileNameRecord mftFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$MFT", false, DateTime.Now); mftFileNameRecord.AllocatedLength = (ulong)mftDataLength; mftFileNameRecord.FileSize = (ulong)mftDataLength; mftFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment mftSegment = CreateBaseRecordSegment(MasterFileTable.MasterFileTableSegmentNumber, 1, mftFileNameRecord); mftSegment.ReferenceCount = 1; NonResidentAttributeRecord mftDataRecord = (NonResidentAttributeRecord)mftSegment.CreateAttributeRecord(AttributeType.Data, String.Empty, false); mftDataRecord.AllocatedLength = (ulong)mftDataLength; mftDataRecord.FileSize = (ulong)mftDataLength; mftDataRecord.ValidDataLength = (ulong)mftDataLength; int mftDataClusterCount = (int)Math.Ceiling((double)mftDataLength / bytesPerCluster); long mftDataStartLCN = volumeBitmapStartLCN + numberOfVolumeBitmapClusters; mftDataRecord.DataRunSequence.Add(new DataRun(mftDataClusterCount, mftDataStartLCN)); mftDataRecord.HighestVCN = mftDataClusterCount - 1; NonResidentAttributeRecord mftBitmapRecord = (NonResidentAttributeRecord)mftSegment.CreateAttributeRecord(AttributeType.Bitmap, String.Empty, false); int mftBitmapLength = (int)Math.Ceiling((double)numberOfMftRecords / (BitmapData.ExtendGranularity * 8)) * BitmapData.ExtendGranularity; int mftBitmapClusterCount = (int)Math.Ceiling((double)mftBitmapLength / bytesPerCluster); mftBitmapRecord.AllocatedLength = (ulong)(mftBitmapClusterCount * bytesPerCluster); mftBitmapRecord.FileSize = (ulong)mftBitmapLength; mftBitmapRecord.ValidDataLength = (ulong)mftBitmapLength; long mftBitmapStartLCN = mftDataStartLCN + mftDataClusterCount; mftBitmapRecord.DataRunSequence.Add(new DataRun(mftBitmapClusterCount, mftBitmapStartLCN)); mftBitmapRecord.HighestVCN = 0; int bytesPerLogPage = 4096; int logFileDataLength = 512 * bytesPerLogPage; FileNameRecord logFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$LogFile", false, DateTime.Now); logFileNameRecord.AllocatedLength = (ulong)logFileDataLength; logFileNameRecord.FileSize = (ulong)logFileDataLength; logFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment logFileSegment = CreateBaseRecordSegment(MasterFileTable.LogFileSegmentNumber, (ushort)MasterFileTable.LogFileSegmentNumber, logFileNameRecord); logFileSegment.ReferenceCount = 1; NonResidentAttributeRecord logFileDataRecord = (NonResidentAttributeRecord)logFileSegment.CreateAttributeRecord(AttributeType.Data, String.Empty, false); logFileDataRecord.AllocatedLength = (ulong)logFileDataLength; logFileDataRecord.FileSize = (ulong)logFileDataLength; logFileDataRecord.ValidDataLength = (ulong)logFileDataLength; int logFileClusterCount = (int)Math.Ceiling((double)logFileDataLength / bytesPerCluster); long logFileStartLCN = mftBitmapStartLCN + mftBitmapClusterCount; logFileDataRecord.DataRunSequence.Add(new DataRun(logFileClusterCount, logFileStartLCN)); logFileDataRecord.HighestVCN = logFileClusterCount - 1; FileNameRecord volumeFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$Volume", false, DateTime.Now); volumeFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment volumeSegment = CreateBaseRecordSegment(MasterFileTable.VolumeSegmentNumber, (ushort)MasterFileTable.VolumeSegmentNumber, volumeFileNameRecord); volumeSegment.ReferenceCount = 1; VolumeNameRecord volumeName = (VolumeNameRecord)volumeSegment.CreateAttributeRecord(AttributeType.VolumeName, String.Empty); volumeName.VolumeName = volumeLabel; VolumeInformationRecord volumeInformation = (VolumeInformationRecord)volumeSegment.CreateAttributeRecord(AttributeType.VolumeInformation, String.Empty); volumeInformation.MajorVersion = majorNTFSVersion; volumeInformation.MinorVersion = minorNTFSVersion; volumeSegment.CreateAttributeRecord(AttributeType.Data, String.Empty); long logFileDataStartSector = logFileStartLCN * sectorsPerCluster; WriteLogFile(volume, logFileDataStartSector, logFileDataLength, bytesPerLogPage); long attributeDefinitionStartLCN = logFileStartLCN + logFileClusterCount; long attributeDefinitionStartSector = attributeDefinitionStartLCN * sectorsPerCluster; int attributeDefinitionLength = WriteAttributeDefinition(volume, attributeDefinitionStartSector, bytesPerCluster); int attributeDefinitionClusterCount = (int)Math.Ceiling((double)attributeDefinitionLength / bytesPerCluster); int attributeDefinitionAllocatedLength = attributeDefinitionClusterCount * bytesPerCluster; FileNameRecord attributeDefinitionFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$AttrDef", false, DateTime.Now); attributeDefinitionFileNameRecord.AllocatedLength = (ulong)attributeDefinitionAllocatedLength; attributeDefinitionFileNameRecord.FileSize = (ulong)attributeDefinitionLength; attributeDefinitionFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment attributeDefinitionSegment = CreateBaseRecordSegment(MasterFileTable.AttrDefSegmentNumber, (ushort)MasterFileTable.AttrDefSegmentNumber, attributeDefinitionFileNameRecord); attributeDefinitionSegment.ReferenceCount = 1; NonResidentAttributeRecord attributeDefinitionDataRecord = (NonResidentAttributeRecord)attributeDefinitionSegment.CreateAttributeRecord(AttributeType.Data, String.Empty, false); attributeDefinitionDataRecord.AllocatedLength = (ulong)attributeDefinitionAllocatedLength; attributeDefinitionDataRecord.FileSize = (ulong)attributeDefinitionLength; attributeDefinitionDataRecord.ValidDataLength = (ulong)attributeDefinitionLength; attributeDefinitionDataRecord.DataRunSequence.Add(new DataRun(attributeDefinitionClusterCount, attributeDefinitionStartLCN)); attributeDefinitionDataRecord.HighestVCN = attributeDefinitionClusterCount - 1; FileNameRecord badClustersFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$BadClus", false, DateTime.Now); badClustersFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment badClustersSegment = CreateBaseRecordSegment(MasterFileTable.BadClusSegmentNumber, (ushort)MasterFileTable.BadClusSegmentNumber, badClustersFileNameRecord); badClustersSegment.ReferenceCount = 1; badClustersSegment.CreateAttributeRecord(AttributeType.Data, String.Empty); NonResidentAttributeRecord badClustersData = (NonResidentAttributeRecord)badClustersSegment.CreateAttributeRecord(AttributeType.Data, "$Bad", false); DataRun volumeDataRun = new DataRun(); volumeDataRun.RunLength = volumeClusterCount; volumeDataRun.IsSparse = true; badClustersData.DataRunSequence.Add(volumeDataRun); badClustersData.HighestVCN = volumeClusterCount - 1; badClustersData.AllocatedLength = (ulong)(volumeClusterCount * bytesPerCluster); badClustersData.FileSize = 0; badClustersData.ValidDataLength = 0; FileNameRecord secureFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$Secure", false, DateTime.Now); secureFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment secureSegment = CreateBaseRecordSegment(MasterFileTable.SecureSegmentNumber, (ushort)MasterFileTable.SecureSegmentNumber, secureFileNameRecord); secureSegment.IsSpecialIndex = true; secureSegment.ReferenceCount = 1; secureSegment.CreateAttributeRecord(AttributeType.Data, "$SDS"); IndexRootRecord sdh = (IndexRootRecord)secureSegment.CreateAttributeRecord(AttributeType.IndexRoot, "$SDH"); IndexHelper.InitializeIndexRoot(sdh, AttributeType.None, CollationRule.SecurityHash, bytesPerIndexRecord, bytesPerCluster); IndexRootRecord sii = (IndexRootRecord)secureSegment.CreateAttributeRecord(AttributeType.IndexRoot, "$SII"); IndexHelper.InitializeIndexRoot(sii, AttributeType.None, CollationRule.UnsignedLong, bytesPerIndexRecord, bytesPerCluster); int upcaseDataClusterCount = (int)Math.Ceiling((double)UpcaseFileLength / bytesPerCluster); FileNameRecord upcaseFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$UpCase", false, DateTime.Now); upcaseFileNameRecord.AllocatedLength = UpcaseFileLength; upcaseFileNameRecord.FileSize = UpcaseFileLength; upcaseFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment upcaseSegment = CreateBaseRecordSegment(MasterFileTable.UpCaseSegmentNumber, (ushort)MasterFileTable.UpCaseSegmentNumber, upcaseFileNameRecord); upcaseSegment.ReferenceCount = 1; NonResidentAttributeRecord upcaseFileDataRecord = (NonResidentAttributeRecord)upcaseSegment.CreateAttributeRecord(AttributeType.Data, String.Empty, false); upcaseFileDataRecord.AllocatedLength = UpcaseFileLength; upcaseFileDataRecord.FileSize = UpcaseFileLength; upcaseFileDataRecord.ValidDataLength = UpcaseFileLength; long upcaseDataStartLCN = attributeDefinitionStartLCN + attributeDefinitionClusterCount; upcaseFileDataRecord.DataRunSequence.Add(new DataRun(upcaseDataClusterCount, upcaseDataStartLCN)); upcaseFileDataRecord.HighestVCN = upcaseDataClusterCount - 1; FileNameRecord extendFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$Extend", true, DateTime.Now); extendFileNameRecord.FileAttributes = FileAttributes.System | FileAttributes.Hidden | FileAttributes.FileNameIndexPresent; FileRecordSegment extendSegment = CreateBaseRecordSegment(MasterFileTable.ExtendSegmentNumber, (ushort)MasterFileTable.ExtendSegmentNumber, extendFileNameRecord); extendSegment.IsDirectory = true; extendSegment.ReferenceCount = 1; IndexRootRecord extendIndexRoot = (IndexRootRecord)extendSegment.CreateAttributeRecord(AttributeType.IndexRoot, IndexHelper.GetIndexName(AttributeType.FileName)); IndexHelper.InitializeIndexRoot(extendIndexRoot, AttributeType.FileName, CollationRule.Filename, bytesPerIndexRecord, bytesPerCluster); FileNameRecord mftMirrorFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, "$MFTMirr", false, DateTime.Now); int mftMirrorDataLength = 4 * bytesPerFileRecordSegment; int mftMirrorDataClusterCount = (int)Math.Ceiling((double)mftMirrorDataLength / bytesPerCluster); int mftMirrorAllocatedLength = mftMirrorDataClusterCount * bytesPerCluster; mftMirrorFileNameRecord.AllocatedLength = (ulong)mftMirrorAllocatedLength; mftMirrorFileNameRecord.FileSize = (ulong)mftMirrorDataLength; mftMirrorFileNameRecord.FileAttributes = FileAttributes.Hidden | FileAttributes.System; FileRecordSegment mftMirrorSegment = CreateBaseRecordSegment(MasterFileTable.MftMirrorSegmentNumber, (ushort)MasterFileTable.MftMirrorSegmentNumber, mftMirrorFileNameRecord); mftMirrorSegment.ReferenceCount = 1; NonResidentAttributeRecord mftMirrorDataRecord = (NonResidentAttributeRecord)mftMirrorSegment.CreateAttributeRecord(AttributeType.Data, String.Empty, false); mftMirrorDataRecord.AllocatedLength = (ulong)mftMirrorAllocatedLength; mftMirrorDataRecord.FileSize = (ulong)mftMirrorDataLength; mftMirrorDataRecord.ValidDataLength = (ulong)mftMirrorDataLength; long mftMirrorDataStartLCN = upcaseDataStartLCN + upcaseDataClusterCount; mftMirrorDataRecord.DataRunSequence.Add(new DataRun(mftMirrorDataClusterCount, mftMirrorDataStartLCN)); mftMirrorDataRecord.HighestVCN = mftMirrorDataClusterCount - 1; FileNameRecord rootDirFileNameRecord = new FileNameRecord(MasterFileTable.RootDirSegmentReference, ".", true, DateTime.Now); rootDirFileNameRecord.FileAttributes = FileAttributes.System | FileAttributes.Hidden | FileAttributes.FileNameIndexPresent; FileRecordSegment rootDirSegment = CreateBaseRecordSegment(MasterFileTable.RootDirSegmentNumber, (ushort)MasterFileTable.RootDirSegmentNumber, rootDirFileNameRecord); rootDirSegment.IsDirectory = true; rootDirSegment.ReferenceCount = 1; IndexRecord rootDirIndexRecord = new IndexRecord(); rootDirIndexRecord.RecordVBN = 0; // Note that we add the index entries according to collation rules rootDirIndexRecord.IndexEntries.Add(new IndexEntry(attributeDefinitionSegment.SegmentReference, attributeDefinitionFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(badClustersSegment.SegmentReference, badClustersFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(volumeBitmapSegment.SegmentReference, volumeBitmapFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(bootSegment.SegmentReference, bootFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(extendSegment.SegmentReference, extendFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(logFileSegment.SegmentReference, logFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(mftSegment.SegmentReference, mftFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(mftMirrorSegment.SegmentReference, mftMirrorFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(secureSegment.SegmentReference, secureFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(upcaseSegment.SegmentReference, upcaseFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(volumeSegment.SegmentReference, volumeFileNameRecord.GetBytes())); rootDirIndexRecord.IndexEntries.Add(new IndexEntry(rootDirSegment.SegmentReference, rootDirFileNameRecord.GetBytes())); long rootDirIndexRecordStartLCN = mftMirrorDataStartLCN + mftMirrorDataClusterCount; int rootDirIndexRecordClusterCount = (int)Math.Ceiling((double)bytesPerIndexRecord / bytesPerCluster); int rootDirIndexRecordAllocatedLength = rootDirIndexRecordClusterCount * bytesPerCluster; string rootDirIndexName = IndexHelper.GetIndexName(AttributeType.FileName); IndexRootRecord rootDirIndexRoot = (IndexRootRecord)rootDirSegment.CreateAttributeRecord(AttributeType.IndexRoot, rootDirIndexName); IndexHelper.InitializeIndexRoot(rootDirIndexRoot, AttributeType.FileName, CollationRule.Filename, bytesPerIndexRecord, bytesPerCluster); rootDirIndexRoot.IsParentNode = true; IndexEntry rootEntry = new IndexEntry(); rootEntry.ParentNodeForm = true; rootEntry.SubnodeVBN = 0; rootDirIndexRoot.IndexEntries.Add(rootEntry); IndexAllocationRecord rootDirIndexAllocation = (IndexAllocationRecord)rootDirSegment.CreateAttributeRecord(AttributeType.IndexAllocation, rootDirIndexName, false); rootDirIndexAllocation.AllocatedLength = (uint)rootDirIndexRecordAllocatedLength; rootDirIndexAllocation.FileSize = (uint)bytesPerIndexRecord; rootDirIndexAllocation.ValidDataLength = (uint)bytesPerIndexRecord; rootDirIndexAllocation.DataRunSequence.Add(new DataRun(rootDirIndexRecordClusterCount, rootDirIndexRecordStartLCN)); rootDirIndexAllocation.HighestVCN = rootDirIndexRecordClusterCount - 1; ResidentAttributeRecord rootDirBitmap = (ResidentAttributeRecord)rootDirSegment.CreateAttributeRecord(AttributeType.Bitmap, rootDirIndexName); rootDirBitmap.Data = new byte[BitmapData.ExtendGranularity]; BitmapData.SetBit(rootDirBitmap.Data, 0); long numberOfClustersInUse = rootDirIndexRecordStartLCN + rootDirIndexRecordClusterCount; long volumeBitmapStartSector = volumeBitmapStartLCN * sectorsPerCluster; WriteVolumeBitmap(volume, volumeBitmapStartSector, volumeClusterCount, numberOfClustersInUse); long mftBitmapStartSector = mftBitmapStartLCN * sectorsPerCluster; WriteMftBitmap(volume, mftBitmapStartSector, numberOfMftRecords, mftBitmapLength); // Write MFT data byte[] mftData = new byte[mftDataLength]; long mftDataStartSector = mftDataStartLCN * sectorsPerCluster; WriteFileRecordSegment(mftData, mftSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, mftMirrorSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, logFileSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, volumeSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, attributeDefinitionSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, rootDirSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, volumeBitmapSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, bootSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, badClustersSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, secureSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, upcaseSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftData, extendSegment, bytesPerFileRecordSegment, minorNTFSVersion); for (long segmentNumber = MasterFileTable.ExtendSegmentNumber + 1; segmentNumber < MasterFileTable.FirstReservedSegmentNumber; segmentNumber++) { FileRecordSegment systemSegment = CreateSystemReservedSegment(segmentNumber); WriteFileRecordSegment(mftData, systemSegment, bytesPerFileRecordSegment, minorNTFSVersion); } volume.WriteSectors(mftDataStartSector, mftData); long upcaseDataStartSector = upcaseDataStartLCN * sectorsPerCluster; WriteUpCaseFile(volume, upcaseDataStartSector); long rootDirIndexRecordStartSector = rootDirIndexRecordStartLCN * sectorsPerCluster; WriteIndexRecord(volume, rootDirIndexRecordStartSector, rootDirIndexRecord, bytesPerIndexRecord); // Write MFT mirror data byte[] mftMirrorData = new byte[mftMirrorDataLength]; long mftMirrorDataStartSector = mftMirrorDataStartLCN * sectorsPerCluster; WriteFileRecordSegment(mftMirrorData, mftSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftMirrorData, mftMirrorSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftMirrorData, logFileSegment, bytesPerFileRecordSegment, minorNTFSVersion); WriteFileRecordSegment(mftMirrorData, volumeSegment, bytesPerFileRecordSegment, minorNTFSVersion); volume.WriteSectors(mftMirrorDataStartSector, mftMirrorData); NTFSBootRecord bootRecord = CreateNTFSBootRecord(volumeClusterCount, sectorsPerCluster, volume.BytesPerSector, bytesPerFileRecordSegment, bytesPerIndexRecord, mftDataStartLCN, mftMirrorDataStartLCN); volume.WriteSectors(0, bootRecord.GetBytes()); volume.WriteSectors(volume.TotalSectors - 1, bootRecord.GetBytes()); return(new NTFSVolume(volume)); }
public NonResidentAttributeData(NTFSVolume volume, FileRecord fileRecord, NonResidentAttributeRecord attributeRecord) { m_volume = volume; m_fileRecord = fileRecord; m_attributeRecord = attributeRecord; }
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)); } } } } }
/// <remarks> /// Only non-resident attributes can be fragmented. /// References: /// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc976808(v=technet.10) /// https://blogs.technet.microsoft.com/askcore/2009/10/16/the-four-stages-of-ntfs-file-growth/ /// </remarks> public static List <AttributeRecord> GetAssembledAttributes(List <FileRecordSegment> segments) { List <AttributeRecord> result = new List <AttributeRecord>(); // If two non-resident attributes have the same AttributeType and Name, then we need to assemble them back together. List <NonResidentAttributeRecord> fragments = new List <NonResidentAttributeRecord>(); foreach (FileRecordSegment segment in segments) { foreach (AttributeRecord attribute in segment.ImmediateAttributes) { if (attribute.AttributeType == AttributeType.AttributeList) { continue; } if (attribute is ResidentAttributeRecord) { result.Add(attribute.Clone()); } else { fragments.Add((NonResidentAttributeRecord)attribute); } } } // Windows NTFS v5.1 driver will sometimes put in the base record segment resident attributes that sort after an attribute from the second segment, // and will sometimes put non-resident fragment with a non-zero LowestVCN in a segment with a lower segment number than the segment containing the first fragment (LowestVCN == 0), // (while keeping the attribute sorting rules within each segment). fragments.Sort(CompareNonResidentAttributes); AttributeType currentAttributeType = AttributeType.None; string currentAttributeName = String.Empty; List <NonResidentAttributeRecord> currentAttributeFragments = new List <NonResidentAttributeRecord>(); foreach (NonResidentAttributeRecord fragment in fragments) { bool additionalFragment = (currentAttributeFragments.Count > 0) && (fragment.AttributeType == currentAttributeType) && (fragment.Name == currentAttributeName); if (!additionalFragment && currentAttributeFragments.Count > 0) { NonResidentAttributeRecord assembledAttribute = AssembleFragments(currentAttributeFragments); result.Add(assembledAttribute); currentAttributeFragments.Clear(); } currentAttributeFragments.Add(fragment); if (!additionalFragment) { currentAttributeType = fragment.AttributeType; currentAttributeName = fragment.Name; } } if (currentAttributeFragments.Count > 0) { NonResidentAttributeRecord assembledAttribute = AssembleFragments(currentAttributeFragments); result.Add(assembledAttribute); } result.Sort(CompareAttributeTypes); return(result); }