private KeyValuePairList <int, IndexRecord> FindRemovalPath(byte[] key, out int indexOfEntryToRemove) { bool isParentNode = true; List <IndexEntry> entries = m_rootRecord.IndexEntries; KeyValuePairList <int, IndexRecord> path = new KeyValuePairList <int, IndexRecord>(); while (isParentNode) { int index = CollationHelper.FindIndexInParentNode(entries, key, m_rootRecord.CollationRule); if (!entries[index].IsLastEntry && CollationHelper.Compare(entries[index].Key, key, m_rootRecord.CollationRule) == 0) { indexOfEntryToRemove = index; return(path); } long subnodeVBN = entries[index].SubnodeVBN; IndexRecord indexRecord = ReadIndexRecord(subnodeVBN); isParentNode = indexRecord.IsParentNode; entries = indexRecord.IndexEntries; path.Add(index, indexRecord); } indexOfEntryToRemove = CollationHelper.FindIndexInLeafNode(entries, key, m_rootRecord.CollationRule); if (indexOfEntryToRemove >= 0) { return(path); } else { return(null); } }
private void WriteIndexRecord(long recordIndex, IndexRecord indexRecord) { long sectorsPerIndexRecord = m_rootRecord.BytesPerIndexRecord / m_volume.BytesPerSector; long sectorIndex = recordIndex * sectorsPerIndexRecord; m_indexAllocationData.WriteSectors(sectorIndex, indexRecord.GetBytes((int)m_rootRecord.BytesPerIndexRecord, true)); }
private IndexRecord ReadIndexRecord(long subnodeVBN) { long sectorIndex = ConvertToSectorIndex(subnodeVBN); byte[] recordBytes = m_indexAllocationData.ReadSectors(sectorIndex, this.SectorsPerIndexRecord); IndexRecord record = new IndexRecord(recordBytes, 0); return(record); }
internal IndexRecord ReadIndexRecord(long subnodeVBN) { long sectorIndex = ConvertToSectorIndex(subnodeVBN); byte[] recordBytes = m_indexAllocationData.ReadSectors(sectorIndex, this.SectorsPerIndexRecord); MultiSectorHelper.RevertUsaProtection(recordBytes, 0); IndexRecord record = new IndexRecord(recordBytes, 0); return(record); }
public KeyValuePairList <MftSegmentReference, FileNameRecord> GetAllEntries(NTFSVolume volume, IndexRootRecord rootRecord) { KeyValuePairList <MftSegmentReference, FileNameRecord> result = new KeyValuePairList <MftSegmentReference, FileNameRecord>(); List <IndexNodeEntry> parents = new List <IndexNodeEntry>(rootRecord.IndexEntries); List <IndexRecord> leaves = new List <IndexRecord>(); int parentIndex = 0; while (parentIndex < parents.Count) { IndexNodeEntry parent = parents[parentIndex]; byte[] clusters = this.ReadDataClusters(volume, parent.SubnodeVCN, rootRecord.ClustersPerIndexRecord); IndexRecord record = new IndexRecord(clusters, 0); if (record.HasChildren) { foreach (IndexNodeEntry node in record.IndexEntries) { parents.Add(node); } } else { leaves.Add(record); } parentIndex++; } foreach (IndexNodeEntry node in parents) { if (!node.IsLastEntry) { // Some of the tree data in NTFS is contained in non-leaf keys FileNameRecord parentRecord = new FileNameRecord(node.Key, 0); result.Add(node.SegmentReference, parentRecord); } } foreach (IndexRecord record in leaves) { foreach (FileNameIndexEntry entry in record.FileNameEntries) { result.Add(entry.FileReference, entry.Record); } } result.Sort(Compare); return(result); }
public KeyValuePair <MftSegmentReference, byte[]>?FindEntry(byte[] key) { if (!m_rootRecord.IsParentNode) { int index = CollationHelper.FindIndexInLeafNode(m_rootRecord.IndexEntries, key, m_rootRecord.CollationRule); if (index >= 0) { IndexEntry entry = m_rootRecord.IndexEntries[index]; return(new KeyValuePair <MftSegmentReference, byte[]>(entry.FileReference, entry.Key)); } else { return(null); } } else { bool isParentNode = true; List <IndexEntry> entries = m_rootRecord.IndexEntries; int index; while (isParentNode) { index = CollationHelper.FindIndexInParentNode(entries, key, m_rootRecord.CollationRule); IndexEntry entry = entries[index]; if (!entry.IsLastEntry && CollationHelper.Compare(entry.Key, key, m_rootRecord.CollationRule) == 0) { return(new KeyValuePair <MftSegmentReference, byte[]>(entry.FileReference, entry.Key)); } else { long subnodeVBN = entry.SubnodeVBN; IndexRecord indexRecord = ReadIndexRecord(subnodeVBN); isParentNode = indexRecord.IsParentNode; entries = indexRecord.IndexEntries; } } index = CollationHelper.FindIndexInLeafNode(entries, key, m_rootRecord.CollationRule); if (index >= 0) { IndexEntry entry = entries[index]; return(new KeyValuePair <MftSegmentReference, byte[]>(entry.FileReference, entry.Key)); } else { return(null); } } }
public KeyValuePairList <MftSegmentReference, byte[]> GetAllEntries() { KeyValuePairList <MftSegmentReference, byte[]> result = new KeyValuePairList <MftSegmentReference, byte[]>(); if (!m_rootRecord.IsParentNode) { foreach (IndexEntry entry in m_rootRecord.IndexEntries) { result.Add(entry.FileReference, entry.Key); } } else { List <IndexEntry> parents = new List <IndexEntry>(m_rootRecord.IndexEntries); SortedList <long> subnodesVisited = new SortedList <long>(); while (parents.Count > 0) { IndexEntry parent = parents[0]; if (!subnodesVisited.Contains(parent.SubnodeVBN)) { IndexRecord record = ReadIndexRecord(parent.SubnodeVBN); if (record.IsParentNode) { parents.InsertRange(0, record.IndexEntries); } else { foreach (IndexEntry entry in record.IndexEntries) { result.Add(entry.FileReference, entry.Key); } } subnodesVisited.Add(parent.SubnodeVBN); } else { if (!parent.IsLastEntry) { // Some of the tree data in NTFS is contained in non-leaf keys result.Add(parent.FileReference, parent.Key); } parents.RemoveAt(0); } } } return(result); }
private KeyValuePairList <int, IndexRecord> FindPathToRightmostLeaf(KeyValuePairList <int, IndexRecord> startPath) { KeyValuePairList <int, IndexRecord> path = new KeyValuePairList <int, IndexRecord>(startPath); int indexInParentRecord = startPath[startPath.Count - 1].Key; IndexRecord indexRecord = startPath[startPath.Count - 1].Value; while (indexRecord.IsParentNode) { indexInParentRecord = indexRecord.IndexEntries.Count - 1; long subnodeVBN = indexRecord.IndexEntries[indexInParentRecord].SubnodeVBN; indexRecord = ReadIndexRecord(subnodeVBN); path.Add(indexInParentRecord, indexRecord); } return(path); }
private KeyValuePairList <int, IndexRecord> FindInsertPath(byte[] key) { bool isParentNode = true; List <IndexEntry> entries = m_rootRecord.IndexEntries; KeyValuePairList <int, IndexRecord> path = new KeyValuePairList <int, IndexRecord>(); while (isParentNode) { int index = CollationHelper.FindIndexInParentNode(entries, key, m_rootRecord.CollationRule); long subnodeVBN = entries[index].SubnodeVBN; IndexRecord indexRecord = ReadIndexRecord(subnodeVBN); isParentNode = indexRecord.IsParentNode; entries = indexRecord.IndexEntries; path.Add(index, indexRecord); } return(path); }
public void AddEntry(MftSegmentReference fileReference, byte[] key) { IndexEntry entry = new IndexEntry(); entry.FileReference = fileReference; entry.Key = key; if (!m_rootRecord.IsParentNode) { int insertIndex = CollationHelper.FindIndexForSortedInsert(m_rootRecord.IndexEntries, key, m_rootRecord.CollationRule); m_rootRecord.IndexEntries.Insert(insertIndex, entry); if (m_rootRecord.RecordLength >= m_volume.AttributeRecordLengthToMakeNonResident) { if (m_indexAllocationRecord == null) { m_indexAllocationRecord = (IndexAllocationRecord)m_fileRecord.CreateAttributeRecord(AttributeType.IndexAllocation, m_indexName); m_indexAllocationData = new NonResidentAttributeData(m_volume, m_fileRecord, m_indexAllocationRecord); m_bitmapRecord = m_fileRecord.CreateAttributeRecord(AttributeType.Bitmap, m_indexName); m_bitmapData = new BitmapData(m_volume, m_fileRecord, m_bitmapRecord, 0); } SplitRootIndexRecord(); } else { m_volume.UpdateFileRecord(m_fileRecord); } } else { KeyValuePairList <int, IndexRecord> path = FindInsertPath(key); IndexRecord leafRecord = path[path.Count - 1].Value; long leafRecordVBN = leafRecord.RecordVBN; int insertIndex = CollationHelper.FindIndexForSortedInsert(leafRecord.IndexEntries, key, m_rootRecord.CollationRule); leafRecord.IndexEntries.Insert(insertIndex, entry); long leafRecordIndex = ConvertToRecordIndex(leafRecordVBN); if (leafRecord.DoesFit((int)m_rootRecord.BytesPerIndexRecord)) { WriteIndexRecord(leafRecordIndex, leafRecord); } else { // Split index record SplitIndexRecord(path); } } }
private KeyValuePairList <int, IndexRecord> FindPathToRightmostLeaf(KeyValuePairList <int, IndexRecord> startPath, int indexInLastEntry) { KeyValuePairList <int, IndexRecord> path = new KeyValuePairList <int, IndexRecord>(startPath); List <IndexEntry> entries; if (startPath.Count == 0) { entries = m_rootRecord.IndexEntries; } else { entries = startPath[startPath.Count - 1].Value.IndexEntries; } IndexRecord indexRecord = ReadIndexRecord(entries[indexInLastEntry].SubnodeVBN); path.Add(indexInLastEntry, indexRecord); return(FindPathToRightmostLeaf(path)); }
/// <summary> /// Will remove the pointer while preserving the entry (if present) /// </summary> private void RemovePointer(KeyValuePairList <int, IndexRecord> path, int indexOfEntryToRemove) { int indexInParentRecord = path[path.Count - 1].Key; IndexRecord indexRecord = path[path.Count - 1].Value; long recordIndex = ConvertToRecordIndex(indexRecord.RecordVBN); IndexEntry pointer = indexRecord.IndexEntries[indexOfEntryToRemove]; if (pointer.IsLastEntry) { if (indexRecord.IndexEntries.Count == 1) { if (path.Count > 1) { path.RemoveAt(path.Count - 1); RemovePointer(path, indexInParentRecord); } else { RemovePointerFromRoot(indexInParentRecord); } DeallocateIndexRecord(recordIndex); } else { MftSegmentReference fileReferenceToReinsert = indexRecord.IndexEntries[indexOfEntryToRemove - 1].FileReference; byte[] keyToReinsert = indexRecord.IndexEntries[indexOfEntryToRemove - 1].Key; indexRecord.IndexEntries.RemoveAt(indexOfEntryToRemove); indexRecord.IndexEntries[indexOfEntryToRemove - 1].FileReference = MftSegmentReference.NullReference; indexRecord.IndexEntries[indexOfEntryToRemove - 1].Key = new byte[0]; WriteIndexRecord(recordIndex, indexRecord); AddEntry(fileReferenceToReinsert, keyToReinsert); } } else { MftSegmentReference fileReferenceToReinsert = indexRecord.IndexEntries[indexOfEntryToRemove].FileReference; byte[] keyToReinsert = indexRecord.IndexEntries[indexOfEntryToRemove].Key; indexRecord.IndexEntries.RemoveAt(indexOfEntryToRemove); WriteIndexRecord(recordIndex, indexRecord); AddEntry(fileReferenceToReinsert, keyToReinsert); } }
/// <remarks> /// The root node can contain a limited number of entries compare to an IndexRecord, /// so there is no point splitting it to two child nodes, a single one would be sufficient. /// </remarks> private void SplitRootIndexRecord() { IndexRecord childRecord = new IndexRecord(); childRecord.IsParentNode = m_rootRecord.IsParentNode; childRecord.IndexEntries = new List <IndexEntry>(m_rootRecord.IndexEntries); long childRecordIndex = AllocateIndexRecord(); childRecord.RecordVBN = ConvertToVirtualBlockNumber(childRecordIndex); WriteIndexRecord(childRecordIndex, childRecord); IndexEntry rootEntry = new IndexEntry(); rootEntry.SubnodeVBN = childRecord.RecordVBN; rootEntry.ParentNodeForm = true; m_rootRecord.IndexEntries.Clear(); m_rootRecord.IsParentNode = true; m_rootRecord.IndexEntries.Add(rootEntry); m_volume.UpdateFileRecord(m_fileRecord); }
/// <param name="path">Key is index in parent node</param> private void SplitIndexRecord(KeyValuePairList <int, IndexRecord> path) { int indexInParentRecord = path[path.Count - 1].Key; // We will treat the record we want to split as the right node, and create a left node IndexRecord rightNode = path[path.Count - 1].Value; long rightNodeVBN = rightNode.RecordVBN; long rightNodeIndex = ConvertToRecordIndex(rightNodeVBN); List <IndexEntry> rightNodeEntries = rightNode.IndexEntries; int splitIndex = rightNodeEntries.Count / 2; IndexEntry middleEntry = rightNodeEntries[splitIndex]; IndexRecord leftNode = new IndexRecord(); leftNode.IsParentNode = rightNode.IsParentNode; leftNode.IndexEntries = rightNodeEntries.GetRange(0, splitIndex); rightNodeEntries.RemoveRange(0, splitIndex + 1); if (rightNode.IsParentNode) { // A parent node has n keys and points to (n + 1) subnodes, // When splitting it to two nodes we will take the pointer from the entry we wish to push to the parent node, // and use it as the last pointer in the left node. IndexEntry leftNodeLastEntry = new IndexEntry(); leftNodeLastEntry.ParentNodeForm = true; leftNodeLastEntry.IsLastEntry = true; leftNodeLastEntry.SubnodeVBN = middleEntry.SubnodeVBN; leftNode.IndexEntries.Add(leftNodeLastEntry); } long leftNodeIndex = AllocateIndexRecord(); leftNode.RecordVBN = ConvertToVirtualBlockNumber(leftNodeIndex); IndexEntry newParentEntry = new IndexEntry(middleEntry.FileReference, middleEntry.Key); newParentEntry.ParentNodeForm = true; newParentEntry.SubnodeVBN = leftNode.RecordVBN; WriteIndexRecord(rightNodeIndex, rightNode); WriteIndexRecord(leftNodeIndex, leftNode); if (path.Count > 1) { IndexRecord parentRecord = path[path.Count - 2].Value; long parentRecordIndex = ConvertToRecordIndex(parentRecord.RecordVBN); List <IndexEntry> parentEntries = parentRecord.IndexEntries; parentEntries.Insert(indexInParentRecord, newParentEntry); if (parentRecord.DoesFit((int)m_rootRecord.BytesPerIndexRecord)) { WriteIndexRecord(parentRecordIndex, parentRecord); } else { // Split parent index record path.RemoveAt(path.Count - 1); SplitIndexRecord(path); } } else { m_rootRecord.IndexEntries.Insert(indexInParentRecord, newParentEntry); if (m_rootRecord.RecordLength >= m_volume.AttributeRecordLengthToMakeNonResident) { SplitRootIndexRecord(); } else { m_volume.UpdateFileRecord(m_fileRecord); } } }
public bool UpdateFileNameRecord(FileNameRecord fileNameRecord) { byte[] key = fileNameRecord.GetBytes(); if (!m_rootRecord.IsParentNode) { int index = CollationHelper.FindIndexInLeafNode(m_rootRecord.IndexEntries, key, m_rootRecord.CollationRule); if (index >= 0) { m_rootRecord.IndexEntries[index].Key = key; m_volume.UpdateFileRecord(m_fileRecord); return(true); } } else { IndexRecord indexRecord = null; bool isParentNode = true; List <IndexEntry> entries = m_rootRecord.IndexEntries; int index; while (isParentNode) { index = CollationHelper.FindIndexInParentNode(entries, key, m_rootRecord.CollationRule); IndexEntry entry = entries[index]; if (!entry.IsLastEntry && CollationHelper.Compare(entry.Key, key, m_rootRecord.CollationRule) == 0) { entries[index].Key = key; if (indexRecord == null) { m_volume.UpdateFileRecord(m_fileRecord); } else { long recordIndex = ConvertToRecordIndex(indexRecord.RecordVBN); WriteIndexRecord(recordIndex, indexRecord); } return(true); } else { long subnodeVBN = entry.SubnodeVBN; indexRecord = ReadIndexRecord(subnodeVBN); isParentNode = indexRecord.IsParentNode; entries = indexRecord.IndexEntries; } } index = CollationHelper.FindIndexInLeafNode(entries, key, m_rootRecord.CollationRule); if (index >= 0) { entries[index].Key = key; if (indexRecord == null) { m_volume.UpdateFileRecord(m_fileRecord); } else { long recordIndex = ConvertToRecordIndex(indexRecord.RecordVBN); WriteIndexRecord(recordIndex, indexRecord); } return(true); } } return(false); }
public void RemoveEntry(byte[] key) { if (!m_rootRecord.IsParentNode) { int index = CollationHelper.FindIndexInLeafNode(m_rootRecord.IndexEntries, key, m_rootRecord.CollationRule); if (index >= 0) { m_rootRecord.IndexEntries.RemoveAt(index); } m_volume.UpdateFileRecord(m_fileRecord); } else { int indexOfEntryToRemove; KeyValuePairList <int, IndexRecord> path = FindRemovalPath(key, out indexOfEntryToRemove); if (path == null) { return; } if ((path.Count > 0 && path[path.Count - 1].Value.IsParentNode) || path.Count == 0) { // We find the rightmost leaf entry in the left branch and put it instead. // Note: Excluding the root of the branch, the rightmost leaf entry in the branch collates last. KeyValuePairList <int, IndexRecord> pathToLeaf = FindPathToRightmostLeaf(path, indexOfEntryToRemove); IndexRecord leaf = pathToLeaf[pathToLeaf.Count - 1].Value; IndexEntry entryToRemoveFromLeaf = leaf.IndexEntries[leaf.IndexEntries.Count - 1]; leaf.IndexEntries.RemoveAt(leaf.IndexEntries.Count - 1); long leafRecordIndex = ConvertToRecordIndex(leaf.RecordVBN); // Note: CHKDSK does not accept an empty IndexRecord, however, we must not call RemovePointer just yet because it might affect the parent as well. WriteIndexRecord(leafRecordIndex, leaf); if (path.Count == 0) { m_rootRecord.IndexEntries[indexOfEntryToRemove].FileReference = entryToRemoveFromLeaf.FileReference; m_rootRecord.IndexEntries[indexOfEntryToRemove].Key = entryToRemoveFromLeaf.Key; m_volume.UpdateFileRecord(m_fileRecord); } else { path[path.Count - 1].Value.IndexEntries[indexOfEntryToRemove].FileReference = entryToRemoveFromLeaf.FileReference; path[path.Count - 1].Value.IndexEntries[indexOfEntryToRemove].Key = entryToRemoveFromLeaf.Key; long recordIndex = ConvertToRecordIndex(path[path.Count - 1].Value.RecordVBN); WriteIndexRecord(recordIndex, path[path.Count - 1].Value); } if (leaf.IndexEntries.Count == 0) { int indexOfLeafPointer = pathToLeaf[pathToLeaf.Count - 1].Key; RemovePointer(pathToLeaf.GetRange(0, pathToLeaf.Count - 1), indexOfLeafPointer); DeallocateIndexRecord(leafRecordIndex); } } else { int indexInParentRecord = path[path.Count - 1].Key; IndexRecord leaf = path[path.Count - 1].Value; leaf.IndexEntries.RemoveAt(indexOfEntryToRemove); long recordIndex = ConvertToRecordIndex(leaf.RecordVBN); if (leaf.IndexEntries.Count > 0) { WriteIndexRecord(recordIndex, leaf); } else { path.RemoveAt(path.Count - 1); RemovePointer(path, indexInParentRecord); DeallocateIndexRecord(recordIndex); } } } }
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)); }
private static void WriteIndexRecord(Volume volume, long indexRecordStartSector, IndexRecord indexRecord, int bytesPerIndexRecord) { byte[] indexRecordBytes = indexRecord.GetBytes(bytesPerIndexRecord, true); volume.WriteSectors(indexRecordStartSector, indexRecordBytes); }