diff -r 000000000000 -r 08ec8eefde2f persistentstorage/store/HTOOLS/PFSDUMP.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/store/HTOOLS/PFSDUMP.CPP Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,861 @@ +// 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 "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: +// + +#include "PFSDUMP.H" + +// entry point + +int main(int argc,char *argv[]) + { + if (TheProgram.ProcessCommandLine(argc,argv)) + { + TheProgram.ExplainUsage(); + return 1; + } + TheProgram.Run(); + return 0; + } + +// Class Program + +class Program TheProgram; + +Program::Program() + : iFile(NULL),iOptions(0),iBin(&iBinBuf),iErrors(0) + { + cout << setfill('0'); + } + +void Program::Information() + { + if (!Option(quiet)) + cout << "\nEPOC PermanentFileStore Dump Utility Version " << MajorVersion << '.' \ + << setw(2) << MinorVersion << "(build " << setw(3) << setfill('0') << Build \ + << ")\nCopyright (c) Symbian Software Limited 1997-2004. All rights reserved.\n\n" << flush; + cout << hex; + cerr << hex; + } + +void Program::ExplainUsage() + { + Information(); + cout << "Usage: PFSDUMP [options] storefile\n" \ + " -v verbose: dump contents\n" \ + " -f generate a frame dump\n" \ + " -r use nth previous store revision\n" \ + " -c format output for comparison (not for frame dumps)\n" + " -q quiet: suppress logo\n" << flush; + } + +int Program::ProcessCommandLine(int argc,char ** argv) + { + if (argc<2) + return 1; + + int options=0; + while (--argc>0) + { + istrstream arg(*++argv); + int c=arg.get(); + if (c=='/' || c=='-') + { + while ((c=arg.get())!=EOF) + { + switch (c) + { + case 'c': case 'C': + options|=compare; + break; + case 'f': case 'F': + options|=frame; + break; + case 'q': case 'Q': + options|=quiet; + break; + case 'r': case 'R': + arg >> iTocRevision; + if (arg.fail() || iTocRevision<0) + return 1; + break; + case 'v': case 'V': + options|=verbose; + break; + default: // unrecognised option + return 1; + } + } + } + else if (iFile!=NULL) + return 1; // two filenames? + else + iFile=arg.str(); + } + if (options&frame) + options&=~compare; + iOptions=options; + return iFile==NULL; + } + +void Program::Run() +// +// The main part of the program +// + { + Information(); + StoreFile store(iFile); + if (iErrors) + cerr << endl; + if (store.Empty()) + { + cout << "Store is empty" << endl; + return; + } + if (Option(frame)) + { + store.EmitHeader(); + store.EmitFrames(); + } + store.EmitToc(); + if (!Option(frame)) + store.EmitStreams(); + } + +void Program::Abort(char const* aMessage) + { + cerr << aMessage << endl; + exit(3); + } + +void Program::Corrupt(char const* aMessage) +// +// terminate after detecting a fatal corruption error +// + { + cerr << "\nfatal error: " << aMessage << "\ncannot continue\n" << flush; + exit(2); + } + +ostream& Program::Error() + { + ++TheProgram.iErrors; + return cerr << "error: "; + } + +ostream& Program::Warning() + { + ++TheProgram.iErrors; + return cerr << "warning: "; + } + +// Bin stream buffer + +binbuf::binbuf() + : streambuf() + {} + +int binbuf::overflow(int) + {return 0;} + +int binbuf::underflow() + {return EOF;} + +// Class StoreFile + +int StoreFile::OutWidth; + +StoreFile::StoreFile(char const* aFile) + { +#ifdef __MSVCDOTNET__ + iFile.open(aFile, ios::binary); +#else //!__MSVCDOTNET__ + iFile.open(aFile, ios::binary | ios::nocreate); +#endif //__MSVCDOTNET__ + if (!iFile) + Program::Abort("Unable to open file or file does not exist"); +// + unsigned long uid; + iFile.read((char*)&uid,sizeof(uid)); + iFile.seekg(0,ios::end); + int size=iFile.tellg(); + if (size>=4; + } + OutWidth=width; + } +// + cout << "Dumping " << aFile << "\n\n"; +// + iFile.seekg(Header::Offset,ios::beg); + iFile.read((char*)&iHeader,Header::Size); +// + if (Empty()) + return; + LoadFrames(); + LoadToc(); + } + +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::SizeiSize) + { + Program::Warning() << "incomplete 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) + Program::Warning() << "incomplete frame at " << FramePos(offset-diff) << endl; + offset=full; + } + else + { + int newoffset=offset+length+FrameDes::Size; + if (newoffset>=full || newoffset-FrameDes::Size>iSize) + { + Program::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()) + { + Program::Error() << "invalid toc address " << toc << endl; + return; + } + if (stream.Type()!=FrameDes::Toc) + { + Program::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=TheProgram.TocRevision();rev;--rev) + { + do { + if (--fiDes.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();iteriHandle.IsNull()) + Program::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) + Program::Error() << "invalid stream reference in toc entry " << iter->iHandle << endl; + else if (iter->Pos().Pos()>=toc.Pos()) + Program::Error() << "virtual stream reference in toc entry " << iter->iHandle << endl; + else if (f->iDes.Type()!=FrameDes::Data) + Program::Error() << "toc entry " << iter->iHandle << ": refers to non-data frame" << endl; + } + } + } + +void StoreFile::EmitHeader() + { + cout << iHeader; + } + +void StoreFile::EmitFrames() + { + int verbose=TheProgram.Option(Program::verbose); + Frames::Iterator const end=iFrames.End(); + for (Frames::Iterator iter=iFrames.Begin();iteriPos; + cout << "Frame at " << pos << ": " << iter->iDes << endl; + if (!verbose) + continue; + + // dump contents + int length=iter->iDes.Length(); + if (length==0) + { // full frame + if (iter+1==end) // no more + continue; + length=iter[1].iPos.Pos()-pos.Pos(); + } + HexDump hex; + iFile.seekg(FileOffset(pos).Offset(),ios::beg); + hex.Dump(iFile,length); + hex.Flush(); + cout << endl; + } + cout << endl; + } + +void StoreFile::EmitToc() + { + cout << iToc; + } + +void StoreFile::EmitStreams() + { + int verbose=TheProgram.Option(Program::verbose); + cout << endl; + Toc::Iterator const end=iToc.End(); + for (Toc::Iterator iter=iToc.Begin();iteriHandle.Avail()) + { + cout << "Stream " << iter->iHandle; + if (iter->Pos().Pos()==-1) + { + cout << " is empty\n"; + continue; + } + if (!TheProgram.Option(Program::compare)) + cout << " at " << iter->Pos(); + Stream stream(iFrames,iter->Pos()); + if (!stream.IsGood() || stream.Type()!=FrameDes::Data) + { + cout << " is invalid\n"; + continue; + } + int len=stream.Length(); + cout << ", " << dec << len << hex << " byte" << (len==1 ? "\n" : "s\n"); + if (!verbose) + continue; + + // dump contents + HexDump hex; + Frames::Iterator f=stream.Frame(); + do + { + FramePos pos=f->iPos; + int len=f++->iDes.Length(); + if (len==0) + len=f->iPos.Pos()-pos.Pos(); + iFile.seekg(FileOffset(pos).Offset(),ios::beg); + hex.Dump(iFile,len); + } while (f->iDes.Type()==FrameDes::Continuation); + hex.Flush(); + cout << endl; + } + } + } + +// Class HexDump + +HexDump::HexDump() + : iPos(0),iByte(0) + { + Reset(); + } + +HexDump::~HexDump() + { + Flush(); + } + +void HexDump::Reset() + { + memset(iChar,' ',HexInterval+3); + iChar[HexInterval+3]=0; + iPtr=&iChar[2]; + } + +void HexDump::Dump(istream& aStream,int aBytes) + { + while (--aBytes>=0 && !aStream.eof()) + { + if (iByte==0) + cout << Margin << FileOffset(iPos) << ':'; + int c=aStream.get(); + cout << ' ' << setw(2) << c; + *iPtr++=char(isprint(c) ? c : '.'); + if (++iByte==HexInterval/2) + { + cout << ' '; + ++iPtr; + } + else if (iByte==HexInterval) + Flush(); + } + } + +void HexDump::Flush() + { + if (iByte) + { + for (int ii=iByte;iiiHandle=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(toc); + memcpy(&iHeader,p,Head::Size); + p+=Head::Size; + int n = iHeader.iCount; + if (n < 0) + { + memset(&iHeader,0,Head::Size); + Program::Error() << "corrupt toc" << endl; + return; + } + iRep=static_cast(malloc(n*sizeof(Entry))); + if (iRep==NULL) + Program::Abort("Out of memory"); + + 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); + Program::Error() << "incomplete 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); + Program::Error() << "invalid toc-base address " << tocbase << endl; + return; + } + if (toc2.Type()!=FrameDes::Toc) + { + memset(&iHeader,0,Head::Size); + Program::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(tocb); + Head headbase; + memcpy(&headbase,p2,Head::Size); + p2+=Head::Size; + if (headbase.IsDelta()) + { + memset(&iHeader,0,Head::Size); + Program::Error() << "toc-base is a toc-delta"<< endl; + return; + } + int bn = headbase.iCount; + if (bn > n) + { + memset(&iHeader,0,Head::Size); + Program::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); + Program::Error() << "toc-delta entry " << e.iHandle << " is outside toc"<< endl; + return; + } + if (ix <= last) + { + memset(&iHeader,0,Head::Size); + Program::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); + Program::Error() << "incomplete 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) + Program::Corrupt("invalid index in relocation patch"); + + Entry& e=iRep[ix-1]; + if (e.iHandle.Generation()!=aReloc->iHandle.Generation()) + Program::Corrupt("incorrect generation in relocation patch"); + e.iPos=aReloc->iPos.Pos(); + } + + // count the available entries + int avail=0; + for (int i=0;iiHeader.iCount) + { +eAvail: Program::Error() << "corrupt available link in toc header " << link << endl; + return; + } + Entry const* en=&(*this)[ix]; + if (en->iHandle!=link) + goto eAvail; + for (;;) + { + if (--avail<0) + { + Program::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) + { +eLink: Program::Error() << "corrupt link in toc entry " << link << endl; + return; + } + en=&(*this)[ix]; + if (en->iHandle!=next) + goto eLink; + link=next; + } + } + if (avail!=0) + Program::Error() << "corrupt available list: free index leakage" << endl; + } + +ostream& operator<<(ostream& aStream,Toc const& aToc) + { + int all=TheProgram.Option(Program::frame); + + Toc::Head const& head=aToc.iHeader; + if (TheProgram.Option(Program::compare)) + aStream << "Toc: "; + else + 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; + if (!all) + aStream << "root is " << head.Root() << '\n'; + else + { + aStream << "first available is " << head.iAvail << '\n'; + Toc::Iterator const end=aToc.End(); + for (Toc::Iterator iter=aToc.Begin();iteriHandle==head.Root() ? "* " : Margin) << iter->iHandle; + if (iter->iHandle.Avail()) + aStream << " free -> " << iter->Link() << '\n'; + else if (iter->Pos().Pos()==-1) + aStream << " empty\n"; + else + aStream << " alloc at " << iter->Pos() << '\n'; + } + } + 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) + Program::Abort("Out of memory"); + + char* read=reinterpret_cast(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) + Program::Abort("Out of memory"); + 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(aLeft)->Pos(); + int right=static_cast(aRight)->iPos.Pos(); + if (leftright) + 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(&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 << ')'; + } +