diff --git a/Library/DiscUtils.Ntfs/NtfsFileSystem.cs b/Library/DiscUtils.Ntfs/NtfsFileSystem.cs index 34e7d6ecb..abb0149fd 100644 --- a/Library/DiscUtils.Ntfs/NtfsFileSystem.cs +++ b/Library/DiscUtils.Ntfs/NtfsFileSystem.cs @@ -930,6 +930,13 @@ public override long GetFileLength(string path) var dirEntry = GetDirectoryEntry(dirEntryPath) ?? throw new FileNotFoundException("File not found", path); + // Ordinary file length request, use info from the directory entry for efficiency - if allowed. + if (NtfsOptions.FileLengthFromDirectoryEntries && attributeName == null && + attributeType == AttributeType.Data) + { + return (long)dirEntry.Details.RealSize; + } + var file = GetFile(dirEntry.Reference); var attr = file.GetAttribute(attributeType, attributeName) ?? throw new FileNotFoundException($"No such attribute '{attributeName}({attributeType})'"); diff --git a/Library/DiscUtils.Registry/Bin.cs b/Library/DiscUtils.Registry/Bin.cs index cb65d72c0..f5ddaeebd 100644 --- a/Library/DiscUtils.Registry/Bin.cs +++ b/Library/DiscUtils.Registry/Bin.cs @@ -187,7 +187,7 @@ internal bool WriteRawCellData(int cellIndex, ReadOnlySpan data) internal int AllocateCell(int size) { - if (size < 8 || (size % 0x7) != 0) + if (size < 8 || (size & 0x7) != 0) { throw new ArgumentException("Invalid cell size"); } diff --git a/Library/DiscUtils.Registry/SubKeyIndirectListCell.cs b/Library/DiscUtils.Registry/SubKeyIndirectListCell.cs index 69ceae2ea..28dece1ce 100644 --- a/Library/DiscUtils.Registry/SubKeyIndirectListCell.cs +++ b/Library/DiscUtils.Registry/SubKeyIndirectListCell.cs @@ -160,33 +160,22 @@ internal override IEnumerable EnumerateKeys() internal override int LinkSubKey(string name, int cellIndex) { - // Look for the first sublist that has a subkey name greater than name - if (ListType == "ri") + // As in UnlinkSubKey, a list's entries can be a mix of sublists ("ri") and key nodes ("li"), so dispatch + // on the actual cell type rather than on ListType (avoids a KeyNodeCell -> ListCell InvalidCastException). + // Look for the first sublist/key node whose name is greater than the new name. + for (var i = 0; i < CellIndexes.Count; ++i) { - if (CellIndexes.Count == 0) - { - throw new NotImplementedException("Empty indirect list"); - } - - for (var i = 0; i < CellIndexes.Count - 1; ++i) + var cell = _hive.GetCell(CellIndexes[i]); + if (cell is ListCell listCell) { - var cell = _hive.GetCell(CellIndexes[i]); - if (cell.FindKey(name, out var tempIndex) <= 0) + // Descend into the last sublist, or the first whose range can already hold the new name. + if (i == CellIndexes.Count - 1 || listCell.FindKey(name, out _) <= 0) { - CellIndexes[i] = cell.LinkSubKey(name, cellIndex); + CellIndexes[i] = listCell.LinkSubKey(name, cellIndex); return _hive.UpdateCell(this, false); } } - - var lastCell = _hive.GetCell(CellIndexes[CellIndexes.Count - 1]); - CellIndexes[CellIndexes.Count - 1] = lastCell.LinkSubKey(name, cellIndex); - return _hive.UpdateCell(this, false); - } - - for (var i = 0; i < CellIndexes.Count; ++i) - { - var cell = _hive.GetCell(CellIndexes[i]); - if (string.Compare(name, cell.Name, StringComparison.OrdinalIgnoreCase) < 0) + else if (string.Compare(name, ((KeyNodeCell)cell).Name, StringComparison.OrdinalIgnoreCase) < 0) { CellIndexes.Insert(i, cellIndex); return _hive.UpdateCell(this, true); @@ -199,20 +188,19 @@ internal override int LinkSubKey(string name, int cellIndex) internal override int UnlinkSubKey(string name) { - if (ListType == "ri") + // A "ri" list references sublists and a "li" list references key nodes, but in practice the entries of a + // single list can be a mix of both (the read paths - Count/EnumerateKeyNames/EnumerateKeys/DoFindKey - + // already tolerate this). Dispatch on the actual cell type instead of on ListType, otherwise deleting a + // key whose containing list has a mismatched entry throws InvalidCastException (KeyNodeCell -> ListCell). + for (var i = 0; i < CellIndexes.Count; ++i) { - if (CellIndexes.Count == 0) - { - throw new NotImplementedException("Empty indirect list"); - } - - for (var i = 0; i < CellIndexes.Count; ++i) + var cell = _hive.GetCell(CellIndexes[i]); + if (cell is ListCell listCell) { - var cell = _hive.GetCell(CellIndexes[i]); - if (cell.FindKey(name, out var tempIndex) <= 0) + if (listCell.FindKey(name, out _) <= 0) { - CellIndexes[i] = cell.UnlinkSubKey(name); - if (cell.Count == 0) + CellIndexes[i] = listCell.UnlinkSubKey(name); + if (listCell.Count == 0) { _hive.FreeCell(CellIndexes[i]); CellIndexes.RemoveAt(i); @@ -221,17 +209,10 @@ internal override int UnlinkSubKey(string name) return _hive.UpdateCell(this, false); } } - } - else - { - for (var i = 0; i < CellIndexes.Count; ++i) + else if (string.Equals(name, ((KeyNodeCell)cell).Name, StringComparison.OrdinalIgnoreCase)) { - var cell = _hive.GetCell(CellIndexes[i]); - if (string.Equals(name, cell.Name, StringComparison.OrdinalIgnoreCase)) - { - CellIndexes.RemoveAt(i); - return _hive.UpdateCell(this, true); - } + CellIndexes.RemoveAt(i); + return _hive.UpdateCell(this, true); } } diff --git a/Library/DiscUtils.Streams/SparseStream.cs b/Library/DiscUtils.Streams/SparseStream.cs index ca96bd1a2..1f281f0b8 100644 --- a/Library/DiscUtils.Streams/SparseStream.cs +++ b/Library/DiscUtils.Streams/SparseStream.cs @@ -656,7 +656,11 @@ private sealed class SynchronizedSparseStream(SparseStream content, Ownership ow public override long Length => content.Length; - public override long Position { get; set; } + public override long Position + { + get { lock (sync) { return content.Position; } } + set { lock (sync) { content.Position = value; } } + } public override void Flush() { diff --git a/Library/DiscUtils.Vhd/DynamicStream.cs b/Library/DiscUtils.Vhd/DynamicStream.cs index ed05fff85..021f69acf 100644 --- a/Library/DiscUtils.Vhd/DynamicStream.cs +++ b/Library/DiscUtils.Vhd/DynamicStream.cs @@ -174,7 +174,7 @@ public override IEnumerable MapContent(long start, long length) if (offsetInSector != 0 || toRead < Sizes.Sector) { - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((blockBitmap[sectorInBlock / 8] & mask) != 0) { var extentStart = (_blockAllocationTable[block] + sectorInBlock) * @@ -190,7 +190,7 @@ public override IEnumerable MapContent(long start, long length) // Processing at least one whole sector, read as many as possible var toReadSectors = toRead / Sizes.Sector; - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); var readFromParent = (blockBitmap[sectorInBlock / 8] & mask) == 0; var numSectors = 1; @@ -274,7 +274,7 @@ public override int Read(byte[] buffer, int offset, int count) if (offsetInSector != 0 || toRead < Sizes.Sector) { - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((blockBitmap[sectorInBlock / 8] & mask) != 0) { @@ -296,7 +296,7 @@ public override int Read(byte[] buffer, int offset, int count) // Processing at least one whole sector, read as many as possible var toReadSectors = toRead / Sizes.Sector; - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); var readFromParent = (blockBitmap[sectorInBlock / 8] & mask) == 0; var numSectors = 1; @@ -377,7 +377,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation if (offsetInSector != 0 || toRead < Sizes.Sector) { - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((blockBitmap[sectorInBlock / 8] & mask) != 0) { _fileStream.Position = (_blockAllocationTable[block] + sectorInBlock) * @@ -398,7 +398,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation // Processing at least one whole sector, read as many as possible var toReadSectors = toRead / Sizes.Sector; - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); var readFromParent = (blockBitmap[sectorInBlock / 8] & mask) == 0; var numSectors = 1; @@ -479,7 +479,7 @@ public override int Read(Span buffer) if (offsetInSector != 0 || toRead < Sizes.Sector) { - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((blockBitmap[sectorInBlock / 8] & mask) != 0) { _fileStream.Position = (_blockAllocationTable[block] + sectorInBlock) * @@ -500,7 +500,7 @@ public override int Read(Span buffer) // Processing at least one whole sector, read as many as possible var toReadSectors = toRead / Sizes.Sector; - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); var readFromParent = (blockBitmap[sectorInBlock / 8] & mask) == 0; var numSectors = 1; @@ -621,7 +621,7 @@ public override void Write(byte[] buffer, int offset, int count) // Reduce the write to just the end of the current sector toWrite = Math.Min(count - numWritten, Sizes.Sector - offsetInSector); - var sectorMask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var sectorMask = (byte)(1 << (7 - (sectorInBlock & 0x7))); var sectorStart = (_blockAllocationTable[block] + sectorInBlock) * Sizes.Sector + _blockBitmapSize; @@ -665,7 +665,7 @@ public override void Write(byte[] buffer, int offset, int count) // Update all of the bits in the block bitmap for (var i = offset; i < offset + toWrite; i += Sizes.Sector) { - var sectorMask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var sectorMask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((_blockBitmaps[block][sectorInBlock / 8] & sectorMask) == 0) { _blockBitmaps[block][sectorInBlock / 8] |= sectorMask; @@ -726,7 +726,7 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella // Reduce the write to just the end of the current sector toWrite = Math.Min(buffer.Length - numWritten, Sizes.Sector - offsetInSector); - var sectorMask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var sectorMask = (byte)(1 << (7 - (sectorInBlock & 0x7))); var sectorStart = (_blockAllocationTable[block] + sectorInBlock) * Sizes.Sector + _blockBitmapSize; @@ -778,7 +778,7 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella // Update all of the bits in the block bitmap for (var i = 0; i < toWrite; i += Sizes.Sector) { - var sectorMask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var sectorMask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((_blockBitmaps[block][sectorInBlock / 8] & sectorMask) == 0) { _blockBitmaps[block][sectorInBlock / 8] |= sectorMask; @@ -841,7 +841,7 @@ public override void Write(ReadOnlySpan buffer) // Reduce the write to just the end of the current sector toWrite = Math.Min(buffer.Length - numWritten, Sizes.Sector - offsetInSector); - var sectorMask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var sectorMask = (byte)(1 << (7 - (sectorInBlock & 0x7))); var sectorStart = (_blockAllocationTable[block] + sectorInBlock) * Sizes.Sector + _blockBitmapSize; @@ -885,7 +885,7 @@ public override void Write(ReadOnlySpan buffer) // Update all of the bits in the block bitmap for (var i = 0; i < toWrite; i += Sizes.Sector) { - var sectorMask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var sectorMask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((_blockBitmaps[block][sectorInBlock / 8] & sectorMask) == 0) { _blockBitmaps[block][sectorInBlock / 8] |= sectorMask; @@ -977,11 +977,11 @@ private long FindNextPresentSector(long pos, long maxPos) if (_blockBitmaps[block][sectorInBlock / 8] == 0) { - pos += (8 - sectorInBlock & 0x7) * Sizes.Sector; + pos += (8 - (sectorInBlock & 0x7)) * Sizes.Sector; } else { - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((_blockBitmaps[block][sectorInBlock / 8] & mask) != 0) { foundStart = true; @@ -1015,11 +1015,11 @@ private long FindNextAbsentSector(long pos, long maxPos) if (_blockBitmaps[block][sectorInBlock / 8] == 0xFF) { - pos += (8 - sectorInBlock & 0x7) * Sizes.Sector; + pos += (8 - (sectorInBlock & 0x7)) * Sizes.Sector; } else { - var mask = (byte)(1 << (7 - sectorInBlock & 0x7)); + var mask = (byte)(1 << (7 - (sectorInBlock & 0x7))); if ((_blockBitmaps[block][sectorInBlock / 8] & mask) == 0) { foundEnd = true;