Skip to content

Commit

Permalink
Improve FAT chain loop detection
Browse files Browse the repository at this point in the history
Improve loop detection time, but limit memory usage and HashSet
operations with binary exponential backoff.

Fixes: #242
  • Loading branch information
jeremy-visionaid committed Nov 20, 2024
1 parent 6d27a94 commit 7a80c56
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 74 deletions.
Binary file added OpenMcdf.Tests/FatChainLoop_v3.cfs
Binary file not shown.
74 changes: 1 addition & 73 deletions OpenMcdf.Tests/OpenMcdf.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,79 +36,7 @@
</ItemGroup>

<ItemGroup>
<None Update="MultipleStorage.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="MultipleStorage2.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="MultipleStorage3.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="MultipleStorage4.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_0.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_4095.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_4096.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_4097.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_511.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_512.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_513.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_63.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_64.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_65.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v3_65536.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_0.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_4095.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_4096.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_4097.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_511.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_512.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_513.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_63.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_64.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestStream_v4_65.cfs">
<None Update="*.cfs">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Expand Down
8 changes: 8 additions & 0 deletions OpenMcdf.Tests/StorageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ public void OpenStorage(string fileName)
Assert.AreEqual("MyStorage", storage.EntryInfo.Name);
}

[TestMethod]
[DataRow("FatChainLoop_v3.cfs")]
public void FatChainLoop(string fileName)
{
using var rootStorage = RootStorage.OpenRead(fileName);
Assert.ThrowsException<FileFormatException>(() => rootStorage.OpenStorage("Anything"));
}

[TestMethod]
[DataRow(Version.V3, 0)]
[DataRow(Version.V3, 1)]
Expand Down
17 changes: 16 additions & 1 deletion OpenMcdf/FatChainEnumerator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;

namespace OpenMcdf;

Expand All @@ -11,6 +12,7 @@ internal sealed class FatChainEnumerator : IEnumerator<uint>
{
private readonly Fat fat;
private readonly FatEnumerator fatEnumerator;
readonly HashSet<uint> visited = new();
private uint startId;
private bool start = true;
private uint index = uint.MaxValue;
Expand Down Expand Up @@ -63,6 +65,7 @@ public bool MoveNext()
index = 0;
current = startId;
start = false;
visited.Add(current);
return true;
}

Expand All @@ -83,9 +86,20 @@ public bool MoveNext()
// If the index is greater than the maximum, then the chain must contain a loop
index = uint.MaxValue;
current = uint.MaxValue;
throw new FileFormatException("FAT sector chain is corrupt.");
throw new FileFormatException("FAT chain index is greater than the sector count.");
}

if (visited.Contains(value))
{
// If the sector has already been visited, then the chain must contain a loop
index = uint.MaxValue;
current = uint.MaxValue;
throw new FileFormatException("FAT chain contains a loop.");
}

if (BitOperations.IsPow2(index))
visited.Add(value);

current = value;
return true;
}
Expand Down Expand Up @@ -232,6 +246,7 @@ public void Reset(uint startSectorId)
start = true;
index = uint.MaxValue;
current = uint.MaxValue;
visited.Clear();
}

[ExcludeFromCodeCoverage]
Expand Down
6 changes: 6 additions & 0 deletions OpenMcdf/System/BitOperations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace System.Numerics;

internal static class BitOperations
{
public static bool IsPow2(uint value) => (value & (value - 1)) == 0 && value != 0;
}

0 comments on commit 7a80c56

Please sign in to comment.