Skip to content

Commit

Permalink
Merge pull request #181 from Visionaid-International-Ltd/fix-duplicat…
Browse files Browse the repository at this point in the history
…e-sid-check

Fix and simplify DirectoryEntry validation
  • Loading branch information
ironfede authored Oct 7, 2024
2 parents 29143c1 + e7919e3 commit d9fbc1f
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 129 deletions.
26 changes: 5 additions & 21 deletions sources/OpenMcdf/CFStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,7 @@ internal RBTree Children
get
{
// Lazy loading of children tree.
if (children == null)
{
//if (this.CompoundFile.HasSourceStream)
//{
children = LoadChildren(DirEntry.SID);
//}
//else
children ??= new RBTree();
}

children ??= LoadChildren(DirEntry);
return children;
}
}
Expand All @@ -81,15 +72,10 @@ internal CFStorage(CompoundFile compFile, IDirectoryEntry dirEntry)
DirEntry = dirEntry;
}

private RBTree LoadChildren(int SID)
private RBTree LoadChildren(IDirectoryEntry directoryEntry)
{
RBTree childrenTree = CompoundFile.GetChildrenTree(SID);

if (childrenTree.Root != null)
DirEntry.Child = (childrenTree.Root as IDirectoryEntry).SID;
else
DirEntry.Child = DirectoryEntry.NOSTREAM;

RBTree childrenTree = CompoundFile.GetChildrenTree(directoryEntry);
DirEntry.Child = childrenTree.Root == null ? DirectoryEntry.NOSTREAM : ((IDirectoryEntry)childrenTree.Root).SID;
return childrenTree;
}

Expand Down Expand Up @@ -566,9 +552,7 @@ public void RenameItem(string oldItemName, string newItemName)
else throw new CFItemNotFound("Item " + oldItemName + " not found in Storage");

children = null;
children = LoadChildren(DirEntry.SID); //Rethread

children ??= new RBTree();
children = LoadChildren(DirEntry); // Rethread
}
}
}
138 changes: 30 additions & 108 deletions sources/OpenMcdf/CompoundFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1500,141 +1500,63 @@ internal List<Sector> GetSectorChain(int secID, SectorType chainType)
// node.Value.DirEntry.RightSibling = from.DirEntry.RightSibling;
//}

internal RBTree GetChildrenTree(int sid)
internal RBTree GetChildrenTree(IDirectoryEntry entry)
{
RBTree bst = new RBTree();

// Load children from their original tree.
DoLoadChildren(bst, directoryEntries[sid]);
//bst = DoLoadChildrenTrusted(directoryEntries[sid]);

//bst.Print();
//bst.Print();
//Trace.WriteLine("#### After rethreading");

return bst;
}

private RBTree DoLoadChildrenTrusted(IDirectoryEntry de)
{
RBTree bst = null;

if (de.Child != DirectoryEntry.NOSTREAM)
{
bst = new RBTree(directoryEntries[de.Child]);
}

RBTree bst = new();
List<int> levelSIDs = new List<int>();
LoadChildren(bst, entry.Child, levelSIDs);
return bst;
}

private void DoLoadChildren(RBTree bst, IDirectoryEntry de)
{
if (de.Child != DirectoryEntry.NOSTREAM)
{
if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return;

LoadSiblings(bst, directoryEntries[de.Child]);
NullifyChildNodes(directoryEntries[de.Child]);
bst.Insert(directoryEntries[de.Child]);
}
}

private static void NullifyChildNodes(IDirectoryEntry de)
{
de.Parent = null;
de.Left = null;
de.Right = null;
}

// Doubling methods allows iterative behavior while avoiding
// to insert duplicate items
private void LoadSiblings(RBTree bst, IDirectoryEntry de)
private void LoadChildren(RBTree bst, IDirectoryEntry de, List<int> levelSIDs)
{
List<int> levelSIDs = new List<int>();

if (de.LeftSibling != DirectoryEntry.NOSTREAM)
{
// If there are more left siblings load them...
DoLoadSiblings(bst, directoryEntries[de.LeftSibling], levelSIDs);
}

if (de.RightSibling != DirectoryEntry.NOSTREAM)
{
levelSIDs.Add(de.RightSibling);
levelSIDs.Add(de.SID);

// If there are more right siblings load them...
DoLoadSiblings(bst, directoryEntries[de.RightSibling], levelSIDs);
}
}

private void DoLoadSiblings(RBTree bst, IDirectoryEntry de, List<int> levelSIDs)
{
if (ValidateSibling(de.LeftSibling, levelSIDs))
if (de.StgType == StgType.StgInvalid)
{
levelSIDs.Add(de.LeftSibling);

// If there are more left siblings load them...
DoLoadSiblings(bst, directoryEntries[de.LeftSibling], levelSIDs);
if (ValidationExceptionEnabled)
throw new CFCorruptedFileException($"A Directory Entry has a valid reference to an Invalid Storage Type directory [{de.SID}]");
return;
}

if (ValidateSibling(de.RightSibling, levelSIDs))
if (!Enum.IsDefined(typeof(StgType), de.StgType))
{
levelSIDs.Add(de.RightSibling);

// If there are more right siblings load them...
DoLoadSiblings(bst, directoryEntries[de.RightSibling], levelSIDs);
if (ValidationExceptionEnabled)
throw new CFCorruptedFileException("A Directory Entry has an invalid Storage Type");
return;
}

LoadChildren(bst, de.LeftSibling, levelSIDs);
LoadChildren(bst, de.RightSibling, levelSIDs);
NullifyChildNodes(de);
bst.Insert(de);
}

private bool ValidateSibling(int sid, List<int> levelSIDs)
private void LoadChildren(RBTree bst, int sid, List<int> levelSIDs)
{
if (sid != DirectoryEntry.NOSTREAM)
{
// if this siblings id does not overflow current list
if (sid >= directoryEntries.Count)
{
if (ValidationExceptionEnabled)
{
//this.Close();
throw new CFCorruptedFileException("A Directory Entry references the non-existent sid number " + sid.ToString());
}
else
return false;
}

//if this sibling is valid...
if (directoryEntries[sid].StgType == StgType.StgInvalid)
{
if (ValidationExceptionEnabled)
{
//this.Close();
throw new CFCorruptedFileException("A Directory Entry has a valid reference to an Invalid Storage Type directory [" + sid + "]");
}
else
return false;
}

if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType))
{
if (ValidationExceptionEnabled)
{
//this.Close();
throw new CFCorruptedFileException("A Directory Entry has an invalid Storage Type");
}
else
return false;
}

if (levelSIDs.Contains(sid))
throw new CFCorruptedFileException("Cyclic reference of directory item");
if (sid == DirectoryEntry.NOSTREAM)
return;

return true; //No fault condition encountered for sid being validated
// if this siblings id does not overflow current list
if (sid >= directoryEntries.Count)
{
if (ValidationExceptionEnabled)
throw new CFCorruptedFileException($"A Directory Entry references the non-existent sid number {sid}");
return;
}

return false;
if (levelSIDs.Contains(sid))
throw new CFCorruptedFileException("Cyclic reference of directory item");

IDirectoryEntry de = directoryEntries[sid];
LoadChildren(bst, de, levelSIDs);
}

/// <summary>
Expand Down

0 comments on commit d9fbc1f

Please sign in to comment.