Skip to content

Commit

Permalink
Fix and simplify DirectoryEntry validation
Browse files Browse the repository at this point in the history
Ensure both the root and left sibilings are considered when checking
for duplicate SIDs.

Also, remove redundant lookups.
  • Loading branch information
jeremy-visionaid committed Oct 6, 2024
1 parent 7ee006f commit ba57c3a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 100 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
}
}
}
115 changes: 36 additions & 79 deletions sources/OpenMcdf/CompoundFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1500,12 +1500,12 @@ 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]);
LoadChildren(bst, entry);
//bst = DoLoadChildrenTrusted(directoryEntries[sid]);

//bst.Print();
Expand All @@ -1527,15 +1527,16 @@ private RBTree DoLoadChildrenTrusted(IDirectoryEntry de)
return bst;
}

private void DoLoadChildren(RBTree bst, IDirectoryEntry de)
private void LoadChildren(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]);
IDirectoryEntry child = directoryEntries[de.Child];
if (child.StgType != StgType.StgInvalid)
{
List<int> levelSIDs = new List<int>();
LoadChildren(bst, child, levelSIDs);
}
}
}

Expand All @@ -1546,95 +1547,51 @@ private static void NullifyChildNodes(IDirectoryEntry de)
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>();
levelSIDs.Add(de.SID);

if (de.LeftSibling != DirectoryEntry.NOSTREAM)
if (de.StgType == StgType.StgInvalid)
{
// 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}]");
}

if (de.RightSibling != DirectoryEntry.NOSTREAM)
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");
}
}

private void DoLoadSiblings(RBTree bst, IDirectoryEntry de, List<int> levelSIDs)
{
if (ValidateSibling(de.LeftSibling, levelSIDs))
{
levelSIDs.Add(de.LeftSibling);
// If there are more left siblings load them...
if (SiblingIsValid(de.LeftSibling, levelSIDs))
LoadChildren(bst, directoryEntries[de.LeftSibling], levelSIDs);

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

if (ValidateSibling(de.RightSibling, levelSIDs))
{
levelSIDs.Add(de.RightSibling);

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

NullifyChildNodes(de);
bst.Insert(de);
}

private bool ValidateSibling(int sid, List<int> levelSIDs)
private bool SiblingIsValid(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 false;

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

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

return true; // No fault condition encountered for sid being validated
}

/// <summary>
Expand Down

0 comments on commit ba57c3a

Please sign in to comment.