/// <summary> /// Removes redundant nodes (that contain only an 'End' entry). /// </summary> /// <param name="entryIndex">The index of the entry that may have a redundant child.</param> /// <returns>An entry that needs to be promoted to the parent node (if any).</returns> private IndexEntry LiftNode(int entryIndex) { if ((_entries[entryIndex].Flags & IndexEntryFlags.Node) != 0) { IndexNode childNode = _index.GetSubBlock(_entries[entryIndex]).Node; if (childNode._entries.Count == 1) { long freeBlock = _entries[entryIndex].ChildrenVirtualCluster; _entries[entryIndex].Flags = (_entries[entryIndex].Flags & ~IndexEntryFlags.Node) | (childNode._entries[0].Flags & IndexEntryFlags.Node); _entries[entryIndex].ChildrenVirtualCluster = childNode._entries[0].ChildrenVirtualCluster; _index.FreeBlock(freeBlock); } if ((_entries[entryIndex].Flags & (IndexEntryFlags.Node | IndexEntryFlags.End)) == 0) { IndexEntry entry = _entries[entryIndex]; _entries.RemoveAt(entryIndex); IndexNode nextNode = _index.GetSubBlock(_entries[entryIndex]).Node; return(nextNode.AddEntry(entry)); } } return(null); }
public IndexNode(IndexNodeSaveFn store, int storeOverhead, Index index, IndexNode parent, byte[] buffer, int offset) { _store = store; _storageOverhead = storeOverhead; _index = index; _parent = parent; _header = new IndexHeader(buffer, offset + 0); _totalSpaceAvailable = _header.AllocatedSizeOfEntries; _entries = new List<IndexEntry>(); int pos = (int)_header.OffsetToFirstEntry; while (pos < _header.TotalSizeOfEntries) { IndexEntry entry = new IndexEntry(index.IsFileIndex); entry.Read(buffer, offset + pos); _entries.Add(entry); if ((entry.Flags & IndexEntryFlags.End) != 0) { break; } pos += entry.Size; } }
public bool TryFindEntry(byte[] key, out IndexEntry entry, out IndexNode node) { foreach (IndexEntry focus in _entries) { if ((focus.Flags & IndexEntryFlags.End) != 0) { if ((focus.Flags & IndexEntryFlags.Node) != 0) { IndexBlock subNode = _index.GetSubBlock(focus); return(subNode.Node.TryFindEntry(key, out entry, out node)); } break; } int compVal = _index.Compare(key, focus.KeyBuffer); if (compVal == 0) { entry = focus; node = this; return(true); } if (compVal < 0 && (focus.Flags & (IndexEntryFlags.End | IndexEntryFlags.Node)) != 0) { IndexBlock subNode = _index.GetSubBlock(focus); return(subNode.Node.TryFindEntry(key, out entry, out node)); } } entry = null; node = null; return(false); }
public Index(File file, string name, BiosParameterBlock bpb, UpperCase upCase) { _file = file; _name = name; _bpb = bpb; _isFileIndex = name == "$I30"; _blockCache = new ObjectCache <long, IndexBlock>(); _root = _file.GetStream(AttributeType.IndexRoot, _name).GetContent <IndexRoot>(); _comparer = _root.GetCollator(upCase); using (Stream s = _file.OpenStream(AttributeType.IndexRoot, _name, FileAccess.Read)) { byte[] buffer = Utilities.ReadFully(s, (int)s.Length); _rootNode = new IndexNode(WriteRootNodeToDisk, 0, this, true, buffer, IndexRoot.HeaderOffset); // Give the attribute some room to breathe, so long as it doesn't squeeze others out // BROKEN, BROKEN, BROKEN - how to figure this out? Query at the point of adding entries to the root node? _rootNode.TotalSpaceAvailable += _file.MftRecordFreeSpace(AttributeType.IndexRoot, _name) - 100; } if (_file.StreamExists(AttributeType.IndexAllocation, _name)) { _indexStream = _file.OpenStream(AttributeType.IndexAllocation, _name, FileAccess.ReadWrite); } if (_file.StreamExists(AttributeType.Bitmap, _name)) { _indexBitmap = new Bitmap(_file.OpenStream(AttributeType.Bitmap, _name, FileAccess.ReadWrite), long.MaxValue); } }
public Index(File file, string name, BiosParameterBlock bpb, UpperCase upCase) { _file = file; _name = name; _bpb = bpb; _isFileIndex = name == "$I30"; _blockCache = new ObjectCache<long, IndexBlock>(); _root = _file.GetStream(AttributeType.IndexRoot, _name).GetContent<IndexRoot>(); _comparer = _root.GetCollator(upCase); using (Stream s = _file.OpenStream(AttributeType.IndexRoot, _name, FileAccess.Read)) { byte[] buffer = Utilities.ReadFully(s, (int)s.Length); _rootNode = new IndexNode(WriteRootNodeToDisk, 0, this, true, buffer, IndexRoot.HeaderOffset); // Give the attribute some room to breathe, so long as it doesn't squeeze others out // BROKEN, BROKEN, BROKEN - how to figure this out? Query at the point of adding entries to the root node? _rootNode.TotalSpaceAvailable += _file.MftRecordFreeSpace(AttributeType.IndexRoot, _name) - 100; } if (_file.StreamExists(AttributeType.IndexAllocation, _name)) { _indexStream = _file.OpenStream(AttributeType.IndexAllocation, _name, FileAccess.ReadWrite); } if (_file.StreamExists(AttributeType.Bitmap, _name)) { _indexBitmap = new Bitmap(_file.OpenStream(AttributeType.Bitmap, _name, FileAccess.ReadWrite), long.MaxValue); } }
/// <summary> /// Removes redundant nodes (that contain only an 'End' entry). /// </summary> /// <param name="entryIndex">The index of the entry that may have a redundant child</param> private void LiftNode(int entryIndex) { if ((_entries[entryIndex].Flags & IndexEntryFlags.Node) != 0) { IndexNode childNode = _index.GetSubBlock(this, _entries[entryIndex]).Node; if (childNode._entries.Count == 1) { long freeBlock = _entries[entryIndex].ChildrenVirtualCluster; _entries[entryIndex].Flags = (_entries[entryIndex].Flags & ~IndexEntryFlags.Node) | (childNode._entries[0].Flags & IndexEntryFlags.Node); _entries[entryIndex].ChildrenVirtualCluster = childNode._entries[0].ChildrenVirtualCluster; if ((_entries[entryIndex].Flags & IndexEntryFlags.Node) != 0) { IndexNode newChildNode = _index.GetSubBlock(this, _entries[entryIndex]).Node; childNode._parent = this; } _index.FreeBlock(freeBlock); } if ((_entries[entryIndex].Flags & (IndexEntryFlags.Node | IndexEntryFlags.End)) == 0) { IndexEntry entry = _entries[entryIndex]; _entries.RemoveAt(entryIndex); AddEntry(entry, false); } } }
public IndexNode(IndexNodeSaveFn store, int storeOverhead, Index index, IndexNode parent, byte[] buffer, int offset) { _store = store; _storageOverhead = storeOverhead; _index = index; _parent = parent; _header = new IndexHeader(buffer, offset + 0); _totalSpaceAvailable = _header.AllocatedSizeOfEntries; _entries = new List <IndexEntry>(); int pos = (int)_header.OffsetToFirstEntry; while (pos < _header.TotalSizeOfEntries) { IndexEntry entry = new IndexEntry(index.IsFileIndex); entry.Read(buffer, offset + pos); _entries.Add(entry); if ((entry.Flags & IndexEntryFlags.End) != 0) { break; } pos += entry.Size; } }
protected override void Read(byte[] buffer, int offset) { // Skip FixupRecord fields... LogSequenceNumber = Utilities.ToUInt64LittleEndian(buffer, offset + 0x08); IndexBlockVcn = Utilities.ToUInt64LittleEndian(buffer, offset + 0x10); _node = new IndexNode(WriteToDisk, UpdateSequenceSize, _index, _parentNode, buffer, offset + FieldSize); }
protected override void Read(byte[] buffer, int offset) { // Skip FixupRecord fields... LogSequenceNumber = Utilities.ToUInt64LittleEndian(buffer, offset + 0x08); IndexBlockVcn = Utilities.ToUInt64LittleEndian(buffer, offset + 0x10); _node = new IndexNode(WriteToDisk, UpdateSequenceSize, _index, _parentNode, buffer, offset + FieldSize); }
internal IndexBlock GetSubBlock(IndexNode parentNode, IndexEntry parentEntry) { IndexBlock block = _blockCache[parentEntry.ChildrenVirtualCluster]; if (block == null) { block = new IndexBlock(this, parentNode, parentEntry, _bpb); _blockCache[parentEntry.ChildrenVirtualCluster] = block; } return(block); }
public IndexBlock(Index index, IndexNode parentNode, IndexEntry parentEntry, BiosParameterBlock bpb) : base("INDX", bpb.BytesPerSector) { _index = index; _parentNode = parentNode; Stream stream = index.AllocationStream; _streamPosition = parentEntry.ChildrenVirtualCluster * bpb.BytesPerSector * bpb.SectorsPerCluster; stream.Position = _streamPosition; byte[] buffer = Utilities.ReadFully(stream, (int)index.IndexBufferSize); FromBytes(buffer, 0); }
public IndexBlock(Index index, IndexNode parentNode, IndexEntry parentEntry, BiosParameterBlock bpb) : base("INDX", bpb.BytesPerSector) { _index = index; _parentNode = parentNode; Stream stream = index.AllocationStream; _streamPosition = parentEntry.ChildrenVirtualCluster * bpb.BytesPerSector * bpb.SectorsPerCluster; stream.Position = _streamPosition; byte[] buffer = Utilities.ReadFully(stream, (int)index.IndexBufferSize); FromBytes(buffer, 0); }
private IndexBlock(Index index, bool isRoot, long vcn, BiosParameterBlock bpb) : base("INDX", bpb.BytesPerSector, bpb.IndexBufferSize) { _index = index; _isRoot = isRoot; _indexBlockVcn = (ulong)vcn; _streamPosition = vcn * bpb.BytesPerSector * bpb.SectorsPerCluster; _node = new IndexNode(WriteToDisk, UpdateSequenceSize, _index, isRoot, (uint)bpb.IndexBufferSize - FieldSize); WriteToDisk(); }
private IndexBlock(Index index, bool isRoot, long vcn, BiosParameterBlock bpb) : base("INDX", bpb.BytesPerSector, bpb.IndexBufferSize) { _index = index; _isRoot = isRoot; _indexBlockVcn = (ulong)vcn; _streamPosition = vcn * bpb.BytesPerSector * bpb.SectorsPerCluster; _node = new IndexNode(WriteToDisk, UpdateSequenceSize, _index, isRoot, (uint)bpb.IndexBufferSize - FieldSize); WriteToDisk(); }
private IEnumerable <IndexEntry> FindAllIn(IComparable <byte[]> query, IndexNode node) { lock (_lock) { foreach (IndexEntry focus in node.Entries) { bool searchChildren = true; bool matches = false; bool keepIterating = true; if ((focus.Flags & IndexEntryFlags.End) == 0) { int compVal = query.CompareTo(focus.KeyBuffer); if (compVal == 0) { matches = true; } else if (compVal > 0) { searchChildren = false; } else if (compVal < 0) { keepIterating = false; } } if (searchChildren && (focus.Flags & IndexEntryFlags.Node) != 0) { IndexBlock block = GetSubBlock(focus); foreach (IndexEntry entry in FindAllIn(query, block.Node)) { yield return(entry); } } if (matches) { yield return(focus); } if (!keepIterating) { yield break; } } } }
public IndexNode(IndexNodeSaveFn store, int storeOverhead, Index index, IndexNode parent, uint allocatedSize) { _store = store; _storageOverhead = storeOverhead; _index = index; _parent = parent; _header = new IndexHeader(allocatedSize); _totalSpaceAvailable = allocatedSize; IndexEntry endEntry = new IndexEntry(_index.IsFileIndex); endEntry.Flags |= IndexEntryFlags.End; _entries = new List<IndexEntry>(); _entries.Add(endEntry); _header.OffsetToFirstEntry = (uint)(IndexHeader.Size + storeOverhead); _header.TotalSizeOfEntries = (uint)(_header.OffsetToFirstEntry + endEntry.Size); }
public IndexNode(IndexNodeSaveFn store, int storeOverhead, Index index, IndexNode parent, uint allocatedSize) { _store = store; _storageOverhead = storeOverhead; _index = index; _parent = parent; _header = new IndexHeader(allocatedSize); _totalSpaceAvailable = allocatedSize; IndexEntry endEntry = new IndexEntry(_index.IsFileIndex); endEntry.Flags |= IndexEntryFlags.End; _entries = new List <IndexEntry>(); _entries.Add(endEntry); _header.OffsetToFirstEntry = (uint)(IndexHeader.Size + storeOverhead); _header.TotalSizeOfEntries = (uint)(_header.OffsetToFirstEntry + endEntry.Size); }
protected IEnumerable <IndexEntry> Enumerate(IndexNode node) { foreach (IndexEntry focus in node.Entries) { if ((focus.Flags & IndexEntryFlags.Node) != 0) { IndexBlock block = GetSubBlock(focus); foreach (IndexEntry subEntry in Enumerate(block.Node)) { yield return(subEntry); } } if ((focus.Flags & IndexEntryFlags.End) == 0) { yield return(focus); } } }
private void NodeAsString(TextWriter writer, string prefix, IndexNode node, string id) { writer.WriteLine(prefix + id + ":"); foreach (var entry in node.Entries) { if ((entry.Flags & IndexEntryFlags.End) != 0) { writer.WriteLine(prefix + " E"); } else { writer.WriteLine(prefix + " " + EntryAsString(entry, _file.BestName, _name)); } if ((entry.Flags & IndexEntryFlags.Node) != 0) { NodeAsString(writer, prefix + " ", GetSubBlock(node, entry).Node, ":i" + entry.ChildrenVirtualCluster); } } }
private Index(AttributeType attrType, AttributeCollationRule collationRule, File file, string name, BiosParameterBlock bpb, UpperCase upCase) { _file = file; _name = name; _bpb = bpb; _isFileIndex = (name == "$I30"); _blockCache = new ObjectCache <long, IndexBlock>(); _file.CreateStream(AttributeType.IndexRoot, _name); _root = new IndexRoot() { AttributeType = (uint)attrType, CollationRule = collationRule, IndexAllocationSize = (uint)bpb.IndexBufferSize, RawClustersPerIndexRecord = bpb.RawIndexBufferSize }; _comparer = _root.GetCollator(upCase); _rootNode = new IndexNode(WriteRootNodeToDisk, 0, this, null, 32); }
internal IndexBlock AllocateBlock(IndexNode parentNode, IndexEntry parentEntry) { if (_indexStream == null) { _file.CreateStream(AttributeType.IndexAllocation, _name); _indexStream = _file.OpenStream(AttributeType.IndexAllocation, _name, FileAccess.ReadWrite); } if (_indexBitmap == null) { _file.CreateStream(AttributeType.Bitmap, _name); _indexBitmap = new Bitmap(_file.OpenStream(AttributeType.Bitmap, _name, FileAccess.ReadWrite), long.MaxValue); } long idx = _indexBitmap.AllocateFirstAvailable(0); parentEntry.ChildrenVirtualCluster = idx * Utilities.Ceil(_bpb.IndexBufferSize, _bpb.SectorsPerCluster * _bpb.BytesPerSector); parentEntry.Flags |= IndexEntryFlags.Node; IndexBlock block = IndexBlock.Initialize(this, parentNode, parentEntry, _bpb); _blockCache[parentEntry.ChildrenVirtualCluster] = block; return(block); }
internal static IndexBlock Initialize(Index index, IndexNode parentNode, IndexEntry parentEntry, BiosParameterBlock bpb) { return(new IndexBlock(index, parentNode, parentEntry.ChildrenVirtualCluster, bpb)); }
public bool RemoveEntry(byte[] key) { bool exactMatch; int entryIndex = GetEntry(key, out exactMatch); IndexEntry entry = _entries[entryIndex]; if (exactMatch) { if ((entry.Flags & IndexEntryFlags.Node) != 0) { // Get the next biggest entry in the index, which may be sibling or descendant of sibling IndexEntry replacementLeaf = _entries[entryIndex + 1]; if ((replacementLeaf.Flags & (IndexEntryFlags.End | IndexEntryFlags.Node)) == IndexEntryFlags.End) { entry.KeyBuffer = null; entry.DataBuffer = null; entry.Flags |= IndexEntryFlags.End; _entries.RemoveAt(entryIndex + 1); } else { if ((replacementLeaf.Flags & IndexEntryFlags.Node) != 0) { IndexNode giftingNode = _index.GetSubBlock(this, replacementLeaf).Node; replacementLeaf = giftingNode.FindSmallestLeaf(); } // Take a reference to the byte arrays because in the recursive case, these arrays // may be changed as a new node is promoted. byte[] newKey = replacementLeaf.KeyBuffer; byte[] newData = replacementLeaf.DataBuffer; RemoveEntry(newKey); // Just over-write our key & data with the replacement entry.KeyBuffer = newKey; entry.DataBuffer = newData; LiftNode(entryIndex + 1); } } else { _entries.RemoveAt(entryIndex); } _store(); return(true); } else { if ((entry.Flags & IndexEntryFlags.Node) != 0) { IndexNode childNode = _index.GetSubBlock(this, entry).Node; if (childNode.RemoveEntry(key)) { LiftNode(entryIndex); _store(); return(true); } } else { return(false); } } return(false); }
internal static IndexBlock Initialize(Index index, IndexNode parentNode, IndexEntry parentEntry, BiosParameterBlock bpb) { return new IndexBlock(index, parentNode, parentEntry.ChildrenVirtualCluster, bpb); }
private Index(AttributeType attrType, AttributeCollationRule collationRule, File file, string name, BiosParameterBlock bpb, UpperCase upCase) { _file = file; _name = name; _bpb = bpb; _isFileIndex = name == "$I30"; _blockCache = new ObjectCache<long, IndexBlock>(); _file.CreateStream(AttributeType.IndexRoot, _name); _root = new IndexRoot() { AttributeType = (uint)attrType, CollationRule = collationRule, IndexAllocationSize = (uint)bpb.IndexBufferSize, RawClustersPerIndexRecord = bpb.RawIndexBufferSize }; _comparer = _root.GetCollator(upCase); _rootNode = new IndexNode(WriteRootNodeToDisk, 0, this, true, 32); }
private void NodeAsString(TextWriter writer, string prefix, IndexNode node, string id) { writer.WriteLine(prefix + id + ":"); foreach (var entry in node.Entries) { if ((entry.Flags & IndexEntryFlags.End) != 0) { writer.WriteLine(prefix + " E"); } else { writer.WriteLine(prefix + " " + EntryAsString(entry, _file.BestName, _name)); } if ((entry.Flags & IndexEntryFlags.Node) != 0) { NodeAsString(writer, prefix + " ", GetSubBlock(entry).Node, ":i" + entry.ChildrenVirtualCluster); } } }
private IEnumerable<IndexEntry> FindAllIn(IComparable<byte[]> query, IndexNode node) { foreach (var focus in node.Entries) { bool searchChildren = true; bool matches = false; bool keepIterating = true; if ((focus.Flags & IndexEntryFlags.End) == 0) { int compVal = query.CompareTo(focus.KeyBuffer); if (compVal == 0) { matches = true; } else if (compVal > 0) { searchChildren = false; } else if (compVal < 0) { keepIterating = false; } } if (searchChildren && (focus.Flags & IndexEntryFlags.Node) != 0) { IndexBlock block = GetSubBlock(focus); foreach (var entry in FindAllIn(query, block.Node)) { yield return entry; } } if (matches) { yield return focus; } if (!keepIterating) { yield break; } } }
protected IEnumerable<IndexEntry> Enumerate(IndexNode node) { foreach (var focus in node.Entries) { if ((focus.Flags & IndexEntryFlags.Node) != 0) { IndexBlock block = GetSubBlock(focus); foreach (var subEntry in Enumerate(block.Node)) { yield return subEntry; } } if ((focus.Flags & IndexEntryFlags.End) == 0) { yield return focus; } } }
public bool RemoveEntry(byte[] key, out IndexEntry newParentEntry) { bool exactMatch; int entryIndex = GetEntry(key, out exactMatch); IndexEntry entry = _entries[entryIndex]; if (exactMatch) { if ((entry.Flags & IndexEntryFlags.Node) != 0) { IndexNode childNode = _index.GetSubBlock(entry).Node; IndexEntry rLeaf = childNode.FindLargestLeaf(); byte[] newKey = rLeaf.KeyBuffer; byte[] newData = rLeaf.DataBuffer; IndexEntry newEntry; childNode.RemoveEntry(newKey, out newEntry); entry.KeyBuffer = newKey; entry.DataBuffer = newData; if (newEntry != null) { InsertEntryThisNode(newEntry); } newEntry = LiftNode(entryIndex); if (newEntry != null) { InsertEntryThisNode(newEntry); } newEntry = PopulateEnd(); if (newEntry != null) { InsertEntryThisNode(newEntry); } // New entry could be larger than old, so may need // to divide this node... newParentEntry = EnsureNodeSize(); } else { _entries.RemoveAt(entryIndex); newParentEntry = null; } _store(); return(true); } else if ((entry.Flags & IndexEntryFlags.Node) != 0) { IndexNode childNode = _index.GetSubBlock(entry).Node; IndexEntry newEntry; if (childNode.RemoveEntry(key, out newEntry)) { if (newEntry != null) { InsertEntryThisNode(newEntry); } newEntry = LiftNode(entryIndex); if (newEntry != null) { InsertEntryThisNode(newEntry); } newEntry = PopulateEnd(); if (newEntry != null) { InsertEntryThisNode(newEntry); } // New entry could be larger than old, so may need // to divide this node... newParentEntry = EnsureNodeSize(); _store(); return(true); } } newParentEntry = null; return(false); }
public bool TryFindEntry(byte[] key, out IndexEntry entry, out IndexNode node) { foreach (var focus in _entries) { if ((focus.Flags & IndexEntryFlags.End) != 0) { if ((focus.Flags & IndexEntryFlags.Node) != 0) { IndexBlock subNode = _index.GetSubBlock(focus); return subNode.Node.TryFindEntry(key, out entry, out node); } break; } else { int compVal = _index.Compare(key, focus.KeyBuffer); if (compVal == 0) { entry = focus; node = this; return true; } else if (compVal < 0 && (focus.Flags & (IndexEntryFlags.End | IndexEntryFlags.Node)) != 0) { IndexBlock subNode = _index.GetSubBlock(focus); return subNode.Node.TryFindEntry(key, out entry, out node); } } } entry = null; node = null; return false; }