public virtual KeyValuePairList <MftSegmentReference, FileNameRecord> GetFileNameRecordsInDirectory(MftSegmentReference directoryReference) { FileRecord directoryRecord = GetFileRecord(directoryReference); KeyValuePairList <MftSegmentReference, FileNameRecord> result = null; if (directoryRecord != null && directoryRecord.IsDirectory) { m_mftLock.AcquireReaderLock(Timeout.Infinite); IndexData indexData = new IndexData(this, directoryRecord, AttributeType.FileName); result = indexData.GetAllFileNameRecords(); m_mftLock.ReleaseReaderLock(); for (int index = 0; index < result.Count; index++) { bool isMetaFile = (result[index].Key.SegmentNumber < MasterFileTable.FirstUserSegmentNumber); if (result[index].Value.Flags == FileNameFlags.DOS || isMetaFile) { // The same FileRecord can have multiple FileNameRecord entries, each with its own namespace result.RemoveAt(index); index--; } } } return(result); }
// Note: The PC is simply a BTH with cbKey set to 2 and cbEnt set to 6 public List <T> GetAll() { List <T> result = new List <T>(); List <byte[]> leaves = new List <byte[]>(); if (BTreeHeader.bIdxLevels > 0) { KeyValuePairList <byte[], byte> parents = new KeyValuePairList <byte[], byte>(); parents.Add(GetHeapItem(BTreeHeader.hidRoot), BTreeHeader.bIdxLevels); while (parents.Count > 0) { byte[] parentBytes = parents[0].Key; byte level = parents[0].Value; int offset = 0; while (offset < parentBytes.Length) { HeapID hid = new HeapID(parentBytes, offset + BTreeHeader.cbKey); byte[] bytes = GetHeapItem(hid); if (level == 1) { leaves.Add(bytes); } else { parents.Add(bytes, (byte)(level - 1)); } offset += BTreeHeader.cbKey + HeapID.Length; } parents.RemoveAt(0); } } else { leaves.Add(GetHeapItem(BTreeHeader.hidRoot)); } foreach (byte[] leafBytes in leaves) { int offset = 0; while (offset < leafBytes.Length) { T record = BTreeOnHeapDataRecord.CreateInstance <T>(leafBytes, offset); result.Add(record); offset += BTreeHeader.cbKey + BTreeHeader.cbEnt; } } return(result); }
/// <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); } }
private KeyValuePairList <MftSegmentReference, FileNameRecord> GetFileNameRecordsInDirectory(long directoryBaseSegmentNumber) { FileRecord record = m_mft.GetFileRecord(directoryBaseSegmentNumber); KeyValuePairList <MftSegmentReference, FileNameRecord> result = null; if (record != null && record.IsDirectory) { IndexRootRecord indexRoot = (IndexRootRecord)record.GetAttributeRecord(AttributeType.IndexRoot, IndexRootRecord.FileNameIndexName); IndexAllocationRecord indexAllocation = (IndexAllocationRecord)record.GetAttributeRecord(AttributeType.IndexAllocation, IndexRootRecord.FileNameIndexName); if (indexRoot.IsLargeIndex) { if (indexAllocation != null) { result = indexAllocation.GetAllEntries(this, indexRoot); } } else { result = indexRoot.GetSmallIndexEntries(); } if (result != null) { for (int index = 0; index < result.Count; index++) { if (result[index].Value.Namespace == FilenameNamespace.DOS) { // The same FileRecord can have multiple entries, each with it's own namespace result.RemoveAt(index); index--; } } } } return(result); }
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); } } } }
/// <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); } } }