Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and simplify DirectoryEntry validation #181

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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