secureswitools/swianalysistoolkit/source/dumpswicertstoretool/pfsdump.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:22:02 +0100
branchRCL_3
changeset 26 8b7f4e561641
parent 25 7333d7932ef7
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
* Copy of pfsdump.h from syslibs code (syslibs/development/common/syslibs/store/HTOOLS/).Added CheckFileValidity() method.
*
*/


#include "pfsdump.h"

// Class StoreFile
int StoreFile::OutWidth;

StoreFile::StoreFile()
	{
	}

bool StoreFile::CheckFileValidity(char const* aFile,const Options& aOptions)
	{
	DumpSWICertstoreTool Tool;                  
	iFile.open(aFile, ios::binary);
	if(!iFile)
		{
		throw ECannotOpenFile;
		}
	else
		{
		unsigned long uid;
		iFile.read((char*)&uid,sizeof(uid));
		iFile.seekg(0,ios::end);
		int size = iFile.tellg();
		if(size <= 0)
			{
			if(!aOptions.CreateCCIFile() && !aOptions.CreateDetailCCIFile())
				{
				cout << aFile <<" : File is Empty" << endl << endl;
				}
			return false;
			}

		if (size<FrameDes::First || uid!=PermanentFileStoreUid)
			{
			if(aOptions.CBasedWritableCertstore() || aOptions.ROMCertstore())
				{
				if(!aOptions.CreateCCIFile() && !aOptions.CreateDetailCCIFile())
					{
					cout << aFile <<": Is not a dat file \nor is a corrupt file" << endl << endl;
					}
				return false;
				}
			}

		else
			{
			if(aOptions.CBasedWritableCertstore() || aOptions.ROMCertstore())
				{
				if(!aOptions.CreateCCIFile() && !aOptions.CreateDetailCCIFile())
					{
					cout << aFile <<":" << endl;
					}
				}
			}

		iSize = size;
		int width = 0;
		while (size)
			{
			++width;
			size >>= 4;
			}
		OutWidth = width;
		iFile.seekg(Header::Offset,ios::beg);
		iFile.read((char*)&iHeader,Header::Size);
		if (Empty())
			{
			return false;
			}
		LoadFrames();
		LoadToc();
		}
	return true;
	}

StoreFile::~StoreFile()
	{
	iFile.close();
	}

void StoreFile::LoadFrames()
	{
	FrameDes frame;
	int offset = FrameDes::First;
	int full = FrameDes::First+FrameDes::Interval;
	int diff = FrameDes::First;
	while (offset-FrameDes::Size<iSize)
		{
		if (offset==full)
			{
			full+=FrameDes::Interval;
			diff+=FrameDes::Size;
			}
		if (offset>iSize)
			{
			DumpSWICertstoreTool::Warning() << "in-complete link at " << FramePos(offset-diff) << endl;
			break;
			}
		iFile.seekg(offset-FrameDes::Size,ios::beg);
		iFile >> frame;
		iFrames.Add(FramePos(offset-diff),frame);
		int length = frame.Length();
		if (length == 0)
			{
			if (full>iSize && offset>iSize)
				{
				DumpSWICertstoreTool::Warning() << "in-complete frame at " << FramePos(offset-diff) << endl;
				}
			offset = full;
			}
		else
			{
			int newoffset = offset+length+FrameDes::Size;
			if (newoffset>=full || newoffset-FrameDes::Size>iSize)
				{
				DumpSWICertstoreTool::Error() << "bad link at " << FramePos(offset-diff) << ", skipping to next anchor link" << endl;
				offset=full;
				}
			else
				{
				offset = newoffset;
				if (full-offset<=FrameDes::Size)
					{
					offset=full;
					}
				}
			}
		}
	iFrames.Complete();
	}

void StoreFile::LoadToc()
	{
	FramePos toc=iHeader.Toc();
	Stream stream(iFrames,toc);
	if (!stream.IsGood())
		{
		DumpSWICertstoreTool::Error() << "invalid toc address " << toc << endl;
		return;
		}
	if (stream.Type()!=FrameDes::Toc)
		{
		DumpSWICertstoreTool::Error() << "toc address " << toc << ": refers to non-toc frame"<< endl;
		return;
		}
	// find the requested store revision
	Frames::Iterator f=stream.Frame();
	Frames::Iterator const first=iFrames.Begin();
	for (int rev=Tool.TocRevision();rev;--rev)
		{
		do{
			if (--f<first)
				{
				throw ENotAPermanentFileStore;
				}
			} while (f->iDes.Type()!=FrameDes::Toc);
		}
	iToc.Load(iFile,iFrames,f,iHeader.GetReloc());

	// verify the Toc stream references
	Toc::Iterator const end=iToc.End();
	for (Toc::Iterator iter=iToc.Begin();iter<end;++iter)
		{
		if (iter->iHandle.IsNull())
			{
			DumpSWICertstoreTool::Error() << "missing entry in toc-delta for index " << (1+iter-iToc.Begin()) << endl;
			}
		else if (!iter->iHandle.Avail() && iter->Pos().Pos()>=0)
			{
			f=iFrames.Find(iter->Pos());
			if (f==NULL)
				{
				DumpSWICertstoreTool::Error() << "invalid stream reference in toc entry " << iter->iHandle << endl;
				}
			else if (iter->Pos().Pos()>=toc.Pos())
				{
				DumpSWICertstoreTool::Error() << "virtual stream reference in toc entry " << iter->iHandle << endl;
				}
			else if (f->iDes.Type()!=FrameDes::Data)
				{
				DumpSWICertstoreTool::Error() << "toc entry " << iter->iHandle << ": refers to non-data frame" << endl;
				}
			}
		}
	}

Stream StoreFile::RootStream()
	{
	Toc::Head const& head=iToc.Header();
	return FindStream(head.Root());
	}

Stream StoreFile::FindStream(Handle& aHandle)
	{
	Toc::Iterator const end=iToc.End();
	Toc::Head const& head=iToc.Header();
	for (Toc::Iterator iter=iToc.Begin();iter<end;++iter)
		{
		if (!iter->iHandle.Avail())
			{
			if (iter->Pos().Pos() == -1)
				{
				continue;
				}
			if (iter->iHandle == aHandle)
				{
				return Stream(iFrames, iter->Pos());
				}
			}
		}
	throw ENotAPermanentFileStore;
	}

ifstream& StoreFile::File() 
	{
	return iFile;
	}

// Class Toc
Toc::Toc()
	: iPos(0), iRep(NULL), iAvail(0)
	{
	memset(&iHeader,0,sizeof(iHeader));
	}

Toc::~Toc()
	{
	free(iRep);
	}

void Toc::Base(const char* aPtr,int aCount)
	{
	Entry* e=iRep;
	for (int i=1;i<=aCount;++e,++i)
		{
		e->iHandle=i;	// set the index part
		memcpy((char*)e+Entry::BaseRedundant,aPtr,Entry::BaseSize);
		aPtr+=Entry::BaseSize;
		}
	}

void Toc::Load(istream& aStream,Frames const& aFrames,Frames::Iterator aFrame,Header::Reloc const* aReloc)
	{
	iPos = aFrame->iPos;
	Stream toc1(aFrame);
	void* toc = toc1.Load(aStream);
	const char* p = reinterpret_cast<char*>(toc);
	memcpy(&iHeader,p,Head::Size);
	p+=Head::Size;
	int n = iHeader.iCount;
	if (n < 0)
		{
		memset(&iHeader,0,Head::Size);
		DumpSWICertstoreTool::Error() << "corrupt toc" << endl;
		return;
		}
	iRep=static_cast<Entry*>(malloc(n*sizeof(Entry)));
	if (iRep==NULL)
		{
		throw ENotAPermanentFileStore;
		}
	if (iHeader.IsDelta())
		{
		// verify the delta header
		memcpy(&iDelta,p,DeltaHead::Size);
		p+=DeltaHead::Size;
		int dn = iDelta.iCount;
		if (toc1.Length() != Head::Size + DeltaHead::Size + dn * Entry::DeltaSize)
			{
			memset(&iHeader,0,Head::Size);
			DumpSWICertstoreTool::Error() << "in-complete toc" << endl;
			return;
			}
		// find the toc-base
		FramePos tocbase(iDelta.iBase + Header::tocoffset);
		if (aReloc && aReloc->iHandle.IsTocBase())
			{
			tocbase = aReloc->iPos;
			}
		Stream toc2(aFrames,tocbase);
		if (!toc2.IsGood())
			{
			memset(&iHeader,0,Head::Size);
			DumpSWICertstoreTool::Error() << "invalid toc-base address " << tocbase << endl;
			return;
			}
		if (toc2.Type()!=FrameDes::Toc)
			{
			memset(&iHeader,0,Head::Size);
			DumpSWICertstoreTool::Error() << "toc-base address " << tocbase << ": refers to non-toc frame"<< endl;
			return;
			}
		// validate and load the toc-base
		void* tocb = toc2.Load(aStream);
		const char* p2 = reinterpret_cast<char*>(tocb);
		Head headbase;
		memcpy(&headbase,p2,Head::Size);
		p2+=Head::Size;
		if (headbase.IsDelta())
			{
			memset(&iHeader,0,Head::Size);
			DumpSWICertstoreTool::Error() << "toc-base is a toc-delta"<< endl;
			return;
			}
		int bn = headbase.iCount;
		if (bn > n)
			{
			memset(&iHeader,0,Head::Size);
			DumpSWICertstoreTool::Error() << "toc-base is larger than toc"<< endl;
			return;
			}
		Base(p2,bn);
		free(tocb);
		// validate and update with the toc-delta
		int last = 0;
		while (--dn>=0)
			{
			Entry e;
			memcpy(&e,p,Entry::DeltaSize);
			p+=Entry::DeltaSize;
			int ix = e.iHandle.Index();
			if (ix<=0 || ix > n)
				{
				memset(&iHeader,0,Head::Size);
				DumpSWICertstoreTool::Error() << "toc-delta entry " << e.iHandle << " is outside toc"<< endl;
				return;
				}
			if (ix <= last)
				{
				memset(&iHeader,0,Head::Size);
				DumpSWICertstoreTool::Error() << "toc-delta entry " << e.iHandle << " is out of order"<< endl;
				return;
				}
			iRep[ix-1] = e;
			last = ix;
			}
		}
	else
		{
		if (toc1.Length() != Head::Size + n * Entry::BaseSize)
			{
			memset(&iHeader,0,Head::Size);
			DumpSWICertstoreTool::Error() << "in-complete toc" << endl;
			return;
			}
		Base(p,n);
		}
	free(toc);

	// apply the relocation
	if (aReloc && !aReloc->iHandle.IsTocBase())
		{
		int ix=aReloc->iHandle.Index();
		if (ix<=0 || ix>n)
			{
			throw ENotAPermanentFileStore;
			}
		Entry& e=iRep[ix-1];
		if (e.iHandle.Generation()!=aReloc->iHandle.Generation())
			{
			throw ENotAPermanentFileStore;
			}
		e.iPos=aReloc->iPos.Pos();
		}
	// count the available entries
	int avail=0;
	for (int i=0;i<n;++i)
		{
		if (iRep[i].iHandle.Avail())
		++avail;
		}
	iAvail=avail;
	// verify the available list
	Handle link=iHeader.iAvail;
	if (!link.IsNull())
		{
		int ix=link.Index();
		if (!link.Avail() || ix<=0 || ix >iHeader.iCount)
			{
			DumpSWICertstoreTool::Error() << "corrupt available link in toc header " << link << endl;
			return;
			}
		Entry const* en=&(*this)[ix];
		if (en->iHandle!=link)
			{
			DumpSWICertstoreTool::Error() << "corrupt available link in toc header " << link << endl;
			return;
			}
		for (;;)
			{
			if (--avail<0)
				{
				DumpSWICertstoreTool::Error() << "corrupt available list, possible circular reference" << endl;
				return;
				}
			Handle next=en->Link();
			if (next.IsNull())
				{
				break;
				}
			ix=next.Index();
			if (!next.Avail() || ix<=0 || ix >iHeader.iCount)
				{
				DumpSWICertstoreTool::Error() << "corrupt link in toc entry " << link << endl;
				return;
				}
			en=&(*this)[ix];
			if (en->iHandle!=next)
				{
				DumpSWICertstoreTool::Error() << "corrupt link in toc entry " << link << endl;
				return;
				}
			link=next;
			}
		}
	if (avail!=0)
		{
		DumpSWICertstoreTool::Error() << "corrupt available list: free index leakage" << endl;
		}
	}

ostream& operator<<(ostream& aStream,Toc const& aToc)
	{
	Toc::Head const& head=aToc.iHeader;
	aStream << "Toc at " << aToc.iPos << " with ";
	aStream << dec << head.iCount << (head.iCount==1 ? " entry: " : " entries: ") \
		<< head.iCount-aToc.iAvail  << " allocated, " << aToc.iAvail << " free\n" << hex;
	return aStream << flush;
	}

// Class Stream
int Stream::Length() const
	{
	int total=0;
	Frames::Iterator f=iFrame;
	do	{
		int len=f->iDes.Length();
		if (len==0)
		len=f[1].iPos.Pos()-f[0].iPos.Pos();
		total+=len;
		} while ((++f)->iDes.Type()==FrameDes::Continuation);
	return total;
	}

void* Stream::Load(istream& aStream) const
	{
	int size = Length();
	void* data = malloc(size);
	if (data==NULL)
		{
		throw ENotAPermanentFileStore;
		}
	char* read=reinterpret_cast<char*>(data);
	Frames::Iterator f = iFrame;
	do
		{
		FramePos pos=f->iPos;
		int len=f++->iDes.Length();
		if (len==0)
		{
		len=f->iPos.Pos()-pos.Pos();
		}
		aStream.seekg(FileOffset(pos).Offset(),ios::beg);
		aStream.read(read,len);
		read+=len;
		} while (f->iDes.Type()==FrameDes::Continuation);
	return data;
	}

// Class Frames
Frames::Frames()
	: iSize(0),iElements(0),iRep(NULL)
	{}

Frames::~Frames()
	{
	free(iRep);
	}

void Frames::Add(FramePos aPos,FrameDes aDes)
	{
	if (iElements==iSize)
		{
		iSize=iSize==0 ? 128 : iSize+iSize;
		void* rep=realloc(iRep,iSize*sizeof(*iRep));
		if (rep==NULL)
			{
			throw ENotAPermanentFileStore;
			}
		iRep=(Element*)rep;
		}
	Element& element=iRep[iElements++];
	element.iPos=aPos;
	element.iDes=aDes;
	}

void Frames::Complete()
// add a terminating entry
	{
	Add(0,0);
	--iElements;
	}

Frames::Iterator Frames::Find(FramePos aPos) const
	{
	return (Element const*)bsearch(&aPos,iRep,iElements,sizeof(*iRep),Compare);
	}

int Frames::Compare(void const* aLeft,void const* aRight)
	{
	int left=static_cast<FramePos const*>(aLeft)->Pos();
	int right=static_cast<Element const*>(aRight)->iPos.Pos();
	if (left<right)
		{
		return -1;
		}
	if (left>right)
		{
		return 1;
		}
	return 0;
	}

// Header
FramePos Header::Toc() const
	{
	return tocoffset+(!Dirty() && iToc.iZero==0 ? iToc.iPos : iBackupToc>>backupshift);
	}

Header::Reloc const* Header::GetReloc() const
	{
	return (Dirty() || iToc.iZero==0) ? NULL : reinterpret_cast<Reloc const*>(&iReloc);
	}

ostream& operator<<(ostream& aStream,Header const& aHeader)
	{
	aStream << "Header is " << (aHeader.Dirty() ? "dirty" : "clean");
	Header::Reloc const* reloc=aHeader.GetReloc();
	if (reloc!=NULL)
		{
		aStream << "\npending relocation of ";
		if (reloc->iHandle.IsTocBase())
			{
			aStream << "toc-base";
			}
		else
			{
			aStream << "stream " << StreamId(reloc->iHandle);
			}
		aStream << " to " << reloc->iPos;
		}
	return aStream << "\n\n" << flush;
	}

// FileOffset
FileOffset::FileOffset(FramePos aPos)
	// calculate the file offset for a streampos
	{
	int pos=aPos.Pos();
	int n=pos>>FrameDes::FullShift;
	pos+=FrameDes::Size*n+FrameDes::First;
	iValue=pos;
	}

FileOffset::operator FramePos() const
	{
	int pos=iValue-FrameDes::First;
	int n=pos/FrameDes::Interval;
	pos-=n*FrameDes::Size;
	return FramePos(pos);
	}

ostream& operator<<(ostream& aStream,FileOffset anOffset)
	{
	return aStream << setw(StoreFile::OutWidth) << anOffset.iValue;
	}

// Handle
ostream& operator<<(ostream& aStream,Handle aHandle)
	{
	if (aHandle.IsNull())
		{
		aStream << "Null";
		}
	else
		{
		aStream << setw(6) << aHandle.Index() << ':' << aHandle.Generation();
		}
	return aStream;
	}

// FramePos
ostream& operator<<(ostream& aStream,FramePos aPos)
	{
	return aStream << setw(StoreFile::OutWidth) << aPos.iValue << '[' << FileOffset(aPos) << ']';
	}

// FrameDes
istream& operator>>(istream& aStream,FrameDes& aFrame)
	{
	return aStream.read((char*)&aFrame,FrameDes::Size);
	}

ostream& operator<<(ostream& aStream,FrameDes aFrame)
	{
	static char const* FrameType[]={"free","data","toc","continuation"};
	aStream << FrameType[aFrame.Type()] << " (";
	int length=aFrame.Length();
	if (length==0)
		{
		aStream << "full";
		}
	else
		{
		aStream << dec << length << hex;
		}
	return aStream << ')';
	}