public IndexNode(IndexNodeSaveFn store, int storeOverhead, Index index, bool isRoot, byte[] buffer, int offset) { _store = store; _storageOverhead = storeOverhead; _index = index; _isRoot = isRoot; _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 IndexEntry(IndexEntry toCopy, byte[] newKey, byte[] newData) { _isFileIndexEntry = toCopy._isFileIndexEntry; _flags = toCopy._flags; _vcn = toCopy._vcn; _keyBuffer = newKey; _dataBuffer = newData; }
public IndexBlock(Index index, bool isRoot, IndexEntry parentEntry, BiosParameterBlock bpb) : base("INDX", bpb.BytesPerSector) { _index = index; _isRoot = isRoot; 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 IndexNode(IndexNodeSaveFn store, int storeOverhead, Index index, bool isRoot, uint allocatedSize) { _store = store; _storageOverhead = storeOverhead; _index = index; _isRoot = isRoot; _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 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; }
private void SetEntries(IList<IndexEntry> newEntries, int offset, int count) { _entries.Clear(); for (int i = 0; i < count; ++i) { _entries.Add(newEntries[i + offset]); } // Add an end entry, if not present if (count == 0 || (_entries[_entries.Count - 1].Flags & IndexEntryFlags.End) == 0) { IndexEntry end = new IndexEntry(_index.IsFileIndex); end.Flags = IndexEntryFlags.End; _entries.Add(end); } // Ensure the node isn't over-filled if (SpaceFree < 0) { throw new IOException("Error setting node entries - oversized for node"); } // Persist the new entries to disk _store(); }
/// <summary> /// Only valid on non-root nodes, this method divides the node in two, /// adding the new node to the current parent. /// </summary> /// <returns>An entry that needs to be promoted to the parent node (if any)</returns> private IndexEntry Divide() { int midEntryIdx = _entries.Count / 2; IndexEntry midEntry = _entries[midEntryIdx]; // The terminating entry (aka end) for the new node IndexEntry newTerm = new IndexEntry(_index.IsFileIndex); newTerm.Flags |= IndexEntryFlags.End; // The set of entries in the new node List<IndexEntry> newEntries = new List<IndexEntry>(midEntryIdx + 1); for (int i = 0; i < midEntryIdx; ++i) { newEntries.Add(_entries[i]); } newEntries.Add(newTerm); // Copy the node pointer from the elected 'mid' entry to the new node if ((midEntry.Flags & IndexEntryFlags.Node) != 0) { newTerm.ChildrenVirtualCluster = midEntry.ChildrenVirtualCluster; newTerm.Flags |= IndexEntryFlags.Node; } // Set the new entries into the new node IndexBlock newBlock = _index.AllocateBlock(midEntry); // Set the entries into the new node. Note we updated the parent // pointers first, because it's possible SetEntries may need to further // divide the entries to fit into nodes. We mustn't overwrite any changes. newBlock.Node.SetEntries(newEntries, 0, newEntries.Count); // Forget about the entries moved into the new node, and the entry about // to be promoted as the new node's pointer _entries.RemoveRange(0, midEntryIdx + 1); // Promote the old mid entry return midEntry; }
private IndexEntry AddEntry(IndexEntry newEntry) { bool exactMatch; int index = GetEntry(newEntry.KeyBuffer, out exactMatch); if (exactMatch) { throw new InvalidOperationException("Entry already exists"); } if ((_entries[index].Flags & IndexEntryFlags.Node) != 0) { IndexEntry ourNewEntry = _index.GetSubBlock(_entries[index]).Node.AddEntry(newEntry); if (ourNewEntry == null) { // No change to this node return null; } InsertEntryThisNode(ourNewEntry); } else { _entries.Insert(index, newEntry); } // If there wasn't enough space, we may need to // divide this node IndexEntry newParentEntry = EnsureNodeSize(); _store(); return newParentEntry; }
/// <summary> /// Only valid on the root node, this method moves all entries into a /// single child node. /// </summary> /// <returns>Whether any changes were made</returns> internal bool Depose() { if (!_isRoot) { throw new InvalidOperationException("Only valid on root node"); } if (_entries.Count == 1) { return false; } IndexEntry newRootEntry = new IndexEntry(_index.IsFileIndex); newRootEntry.Flags = IndexEntryFlags.End; IndexBlock newBlock = _index.AllocateBlock(newRootEntry); // Set the deposed entries into the new node. Note we updated the parent // pointers first, because it's possible SetEntries may need to further // divide the entries to fit into nodes. We mustn't overwrite any changes. newBlock.Node.SetEntries(_entries, 0, _entries.Count); _entries.Clear(); _entries.Add(newRootEntry); return true; }
private void InsertEntryThisNode(IndexEntry newEntry) { bool exactMatch; int index = GetEntry(newEntry.KeyBuffer, out exactMatch); if (exactMatch) { throw new InvalidOperationException("Entry already exists"); } else { _entries.Insert(index, newEntry); } }
internal static IndexBlock Initialize(Index index, bool isRoot, IndexEntry parentEntry, BiosParameterBlock bpb) { return new IndexBlock(index, isRoot, parentEntry.ChildrenVirtualCluster, bpb); }
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 void UpdateEntry(byte[] key, byte[] data) { for (int i = 0; i < _entries.Count; ++i) { var focus = _entries[i]; int compVal = _index.Compare(key, focus.KeyBuffer); if (compVal == 0) { IndexEntry newEntry = new IndexEntry(focus, key, data); if (_entries[i].Size != newEntry.Size) { throw new NotImplementedException("Changing index entry sizes"); } _entries[i] = newEntry; _store(); return; } } throw new IOException("No such index entry"); }
internal IndexBlock AllocateBlock(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, false, parentEntry, _bpb); _blockCache[parentEntry.ChildrenVirtualCluster] = block; return block; }
internal IndexBlock GetSubBlock(IndexEntry parentEntry) { IndexBlock block = _blockCache[parentEntry.ChildrenVirtualCluster]; if (block == null) { block = new IndexBlock(this, false, parentEntry, _bpb); _blockCache[parentEntry.ChildrenVirtualCluster] = block; } return block; }
internal static string EntryAsString(IndexEntry entry, string fileName, string indexName) { IByteArraySerializable keyValue = null; IByteArraySerializable dataValue = null; // Try to guess the type of data in the key and data fields from the filename and index name if (indexName == "$I30") { keyValue = new FileNameRecord(); dataValue = new FileRecordReference(); } else if (fileName == "$ObjId" && indexName == "$O") { keyValue = new ObjectIds.IndexKey(); dataValue = new ObjectIdRecord(); } else if (fileName == "$Reparse" && indexName == "$R") { keyValue = new ReparsePoints.Key(); dataValue = new ReparsePoints.Data(); } else if (fileName == "$Quota") { if (indexName == "$O") { keyValue = new Quotas.OwnerKey(); dataValue = new Quotas.OwnerRecord(); } else if (indexName == "$Q") { keyValue = new Quotas.OwnerRecord(); dataValue = new Quotas.QuotaRecord(); } } else if (fileName == "$Secure") { if (indexName == "$SII") { keyValue = new SecurityDescriptors.IdIndexKey(); dataValue = new SecurityDescriptors.IdIndexData(); } else if (indexName == "$SDH") { keyValue = new SecurityDescriptors.HashIndexKey(); dataValue = new SecurityDescriptors.IdIndexData(); } } try { if (keyValue != null && dataValue != null) { keyValue.ReadFrom(entry.KeyBuffer, 0); dataValue.ReadFrom(entry.DataBuffer, 0); return "{" + keyValue + "-->" + dataValue + "}"; } } catch { return "{Parsing-Error}"; } return "{Unknown-Index-Type}"; }
private bool SelfCheckIndexNode(byte[] buffer, int offset, Bitmap bitmap, IndexRoot root, string fileName, string indexName) { bool ok = true; IndexHeader header = new IndexHeader(buffer, offset); IndexEntry lastEntry = null; IComparer<byte[]> collator = root.GetCollator(_context.UpperCase); int pos = (int)header.OffsetToFirstEntry; while (pos < header.TotalSizeOfEntries) { IndexEntry entry = new IndexEntry(indexName == "$I30"); entry.Read(buffer, offset + pos); pos += entry.Size; if ((entry.Flags & IndexEntryFlags.Node) != 0) { long bitmapIdx = entry.ChildrenVirtualCluster / Utilities.Ceil(root.IndexAllocationSize, _context.BiosParameterBlock.SectorsPerCluster * _context.BiosParameterBlock.BytesPerSector); if (!bitmap.IsPresent(bitmapIdx)) { ReportError("Index entry {0} is non-leaf, but child vcn {1} is not in bitmap at index {2}", Index.EntryAsString(entry, fileName, indexName), entry.ChildrenVirtualCluster, bitmapIdx); } } if ((entry.Flags & IndexEntryFlags.End) != 0) { if (pos != header.TotalSizeOfEntries) { ReportError("Found END index entry {0}, but not at end of node", Index.EntryAsString(entry, fileName, indexName)); ok = false; } } if (lastEntry != null && collator.Compare(lastEntry.KeyBuffer, entry.KeyBuffer) >= 0) { ReportError("Found entries out of order {0} was before {1}", Index.EntryAsString(lastEntry, fileName, indexName), Index.EntryAsString(entry, fileName, indexName)); ok = false; } lastEntry = entry; } return ok; }