diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sql/SRC/Server/SqlBur.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/sql/SRC/Server/SqlBur.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,903 @@ +// Copyright (c) 2005-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 "SqlBur.h" +#include "SqlAssert.h" +#include "SqlPanic.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////// Backup database file header format /////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////// No version (Version 0) +// 8 chars 8 chars 8 chars up to 256 characters (512 bytes) +// <32-bit checksum><32-bit filesize><32-bit filenamelen> + +////// Version 2 +// 8 chars 8 chars 4 chars 16 chars 8 chars up to 256 characters (512 bytes) +// <32-bit checksum><64-bit filesize><32-bit filenamelen> + +const TInt KBackupHeaderVersion = 2; //Current backup database file header version + +const TUint32 KMagicNum = 0xFFFFAA55; //Magic number. If the "old database file size" field in the header + //has this value, then the header version is 2+ +const TInt KMaxHeaderSize = 256 + KMaxFileName; //The size of the buffer used for the operations on the header + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//Extracts and returns 32-bit integer from aNumBuf buffer. +static TUint32 GetNumUint32L(const TDesC& aNumBuf) + { + TLex lex(aNumBuf); + lex.SkipSpace(); + TUint32 num = 0xFFFFFFFF; + __SQLLEAVE_IF_ERROR(lex.Val(num, EHex)); + return num; + } + +//Extracts and returns 64-bit integer from aNumBuf buffer. +static TInt64 GetNumInt64L(const TDesC& aNumBuf) + { + TLex lex(aNumBuf); + lex.SkipSpace(); + TInt64 num = -1; + __SQLLEAVE_IF_ERROR(lex.Val(num, EHex)); + return num; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// string consts +_LIT(KRestoreFilter,"*.rst"); // the filter for restore files +_LIT(KBackupFilter,"*.bak");// the filter for backup files +_LIT(KRestoreSuffix,".bak.rst"); // the suffix for restore files (a shortcut by using double suffix :) + +const TUint K8to16bitShift = 1; + +/** Standard two phase construction + @return an instance of the backup client + @param a pointer to the SQL server which must have implemented the + TSqlSrvBurInterface interface + @leave if no memory +*/ +CSqlBackupClient* CSqlBackupClient::NewLC(MSqlSrvBurInterface *aInterface) + { + CSqlBackupClient *self=(CSqlBackupClient *)new(ELeave) CSqlBackupClient(aInterface); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +/** Standard two phase construction + @return an instance of the backup client + @param a pointer to the SQL server which must have implemented the + TSqlSrvBurInterface interface + @leave if no memory +*/ +CSqlBackupClient* CSqlBackupClient::NewL(MSqlSrvBurInterface *aInterface) + { + CSqlBackupClient *self=(CSqlBackupClient *) NewLC(aInterface); + CleanupStack::Pop(); + return self; + } + +/** Standard two phase construction + @param a pointer to the SQL server which must have implemented the + TSqlSrvBurInterface interface +*/ +CSqlBackupClient::CSqlBackupClient(MSqlSrvBurInterface *aInterface) +: CActive(EPriorityStandard), iInterface(aInterface) + { + } + +/** Usual tidy up +*/ +CSqlBackupClient::~CSqlBackupClient() + { + // cancel outstanding requests + Cancel(); + + // release the pub/sub property + iProperty.Close(); + + // make sure the file list is released + iFileList.Reset(); + + // the header buffer + delete iBuffer; + + // the file list array + iFileList.Close(); + + // close the file + iFile.Close(); + + // nuke the active backup client + if(iActiveBackupClient) + { + delete iActiveBackupClient; + } + } + +/** Standard two phase construction + @leave if non memory or StartL leaves +*/ +void CSqlBackupClient::ConstructL() + { + // attach to backup/restore publish/subscribe property + __SQLLEAVE_IF_ERROR(iProperty.Attach(KUidSystemCategory,KUidBackupRestoreKey)); + + // add us to the scheduler + CActiveScheduler::Add(this); + + // a place for the header info + iBuffer=HBufC::NewL(KMaxHeaderSize); + + // set active and request notification of changes to backup + // and restore publish/subscribe property + StartL(); + } + +/** Nuke outstanding requests +*/ +void CSqlBackupClient::DoCancel() + { + // lose any oustanding reqs + iProperty.Cancel(); + } + +/** Not implemented + @return a flag indicating whether we actioned the error + @param the error unused +*/ +TInt CSqlBackupClient::RunError(TInt /* aError */) + { + // just satisfy it that we did something! + return KErrNone; + } + +/** Kick off the BUR client + @leave if TestBurStatusL leaves +*/ +void CSqlBackupClient::StartL() + { + if(!IsActive()) + { + TestBurStatusL(); + NotifyChange(); + } + } + +/** Resubscribe and wait for events +*/ +void CSqlBackupClient::NotifyChange() + { + iProperty.Subscribe(iStatus); + SetActive(); + } + +/** Something happened. Find out what. + Create an instance of BUR client if required + Delete it if no longer required + This is for performance reasons + @leave if ConfirmReadyForBURL leaves +*/ +void CSqlBackupClient::TestBurStatusL() + { + TInt status; + if(iProperty.Get(status)!=KErrNotFound) + { + status&=KBURPartTypeMask; + switch(status) + { + case EBURUnset: + // same as EBURNormal + case EBURNormal: + if(iActiveBackupClient) + { + delete iActiveBackupClient; + iActiveBackupClient=NULL; + } + break; + case EBURBackupFull: + case EBURBackupPartial: + // we only do full backups + if(!iActiveBackupClient) + { + iActiveBackupClient=CActiveBackupClient::NewL(this); + } + iActiveBackupClient->ConfirmReadyForBURL(KErrNone); + break; + case EBURRestoreFull: + case EBURRestorePartial: + // we only do full restores + if(!iActiveBackupClient) + { + iActiveBackupClient=CActiveBackupClient::NewL(this); + } + iActiveBackupClient->ConfirmReadyForBURL(KErrNone); + break; + default: + return; + } + } + } + +/** Called when BUE notifies a BUR event + @leave if TestBurStatusL leaves +*/ +void CSqlBackupClient::RunL() + { + NotifyChange(); + TestBurStatusL(); + } + +/** This is supposed to allow the BUE to know in advance how much + data is coming - but unfortunately there is no way to know this + at this stage since we don't even know yet what SID is being processed + So we just answer some number to make the BUE happy. It doesn't + actually rely on this number so there is no risk - the aFinishedFlag + indicates the end of data, not the value returned here. It is + supposed to allow the BUE to optimise its behaviour by know up front + the data volume. + @return an arbitrary number + @param TDrive unused +*/ +TUint CSqlBackupClient::GetExpectedDataSize(TDriveNumber /* aDrive */) + { + // we have no idea at this point - we even don't know who is to be backed up yet + const TUint KArbitraryNumber = 1024; + return KArbitraryNumber; + } + +/** This is the backup state machine + Because the data has to be sent back in sections and the various + components of the dataflow may straddle chunks, we have to keep + track of where we are between each transfer - a state machine is + the simplest and most understandable implementation + @param TPtr this is where the data will be put to be passed back + @param TBool set to true when all data has been submitted for backup + @leave +*/ +void CSqlBackupClient::GetBackupDataSectionL(TPtr8& aBuffer, TBool& aFinishedFlag) + { + // don't assume they set it to false + aFinishedFlag=EFalse; + // any files to backup + if(iFileList.Count()==0) + { + // nothing to backup - just return the finished flag + aFinishedFlag=ETrue; + // clear down the list + iFileList.Reset(); + // iFileList closed in dtor + return; + } + + // run the state machine + for(TInt bufFreeSpace=aBuffer.MaxSize()-aBuffer.Size(); bufFreeSpace>0; bufFreeSpace=aBuffer.MaxSize()-aBuffer.Size()) + { + switch(iState) + { + case EBackupNoFileOpen: // open a file for processing + { + if(iFileIndex>=iFileList.Count()) + { + // all files have been processed - send the finished flag + aFinishedFlag=ETrue; + // clear down the filelist + iFileList.Reset(); + return; + } + // open the database file to send + TInt rc=iFile.Open( iInterface->Fs(), iFileList[iFileIndex].FullName(), EFileRead | EFileShareExclusive); + if(KErrNone!=rc) + { + // there's nothing we can do if we can't open the file so we just skip it + ++iFileIndex; + break; + } + iState=EBackupOpenNothingSent; + break; + } + case EBackupOpenNothingSent: // nothing sent (so far) for this file - send the header info + { + TInt64 fileSize; + if(KErrNone!=iFile.Size(fileSize) || fileSize==0) // short circuit eval + { + // empty or unreadable - skip this file + iState=EBackupEndOfFile; + break; + } + + // build the header - this is an instance member because it + // has to persist over multiple calls to this method + TPtr hdrPtr=iBuffer->Des(); + + // get the checksum - only grab last 4 bytes - enough to be satisfied that + // the backup and restore worked ok + TUint32 checksum = CheckSumL(iFile) & 0xFFFFFFFF; + + // build the header + const TDesC& fileName = iFileList[iFileIndex].FullName(); + hdrPtr.Format(_L("%8x%8x%4x%16lx%8x%S"), + checksum, // %8x + KMagicNum, // %8x + KBackupHeaderVersion, // %4x + fileSize, // %16lx + fileName.Length(), // %8x + &fileName); // %S + + // we need it to look like an 8bit buffer + TPtr8 hdrPtr8((TUint8*)hdrPtr.Ptr(),hdrPtr.Size(),hdrPtr.Size()); + + TInt len = Min(hdrPtr8.Size(), bufFreeSpace); + + // append the header to the buffer (only till it's full) + aBuffer.Append(hdrPtr8.Ptr(), len); + + // decide what needs to happen next + // if complete then we need data, otherwise we need to put + // the rest of the header in the next chunk + if(hdrPtr8.Size() <= bufFreeSpace) + { + iState = EBackupOpenAllHeaderSent; + } + else + { + // we need to keep track of how much of the header has + // been sent so that we only send the reminder on the next + // iteration + iHeaderSent = len; + iState = EBackupOpenPartHeaderSent; + } + break; + } + case EBackupOpenPartHeaderSent: // need to send the rest of the header + { + // get back the header - this is already loaded with the necessary info + // from the previous state we were in + TPtr hdrPtr = iBuffer->Des(); + + // we need it to look like an 8bit buffer + TPtr8 hdrPtr8((TUint8*)hdrPtr.Ptr(),hdrPtr.Size(),hdrPtr.Size()); + + // how many bytes have we yet to send? + TInt bytesRemaining = hdrPtr.Size() - iHeaderSent; + TInt len = Min(bytesRemaining, bufFreeSpace); + aBuffer.Append(hdrPtr8.Ptr() + iHeaderSent, len); + + if(bytesRemaining <= bufFreeSpace) + { + iHeaderSent = 0; // ready for next header + iState = EBackupOpenAllHeaderSent; + } + else + { + iHeaderSent += len; // ready to do round again + //iState=EBackupOpenPartHeaderSent; same state as now! + } + break; + } + case EBackupOpenAllHeaderSent: // need to send some data + { + TPtr8 ptr((TUint8*)aBuffer.Ptr() + aBuffer.Size(), 0, bufFreeSpace); + __SQLLEAVE_IF_ERROR(iFile.Read(ptr)); + TInt bytesRead = ptr.Size(); + aBuffer.SetLength(aBuffer.Size() + bytesRead); + // EOF + if(bytesRead == 0) + { + iState = EBackupEndOfFile; + break; + } + break; + } + case EBackupEndOfFile: + { + iFile.Close(); + ++iFileIndex; // move on to next file + iState = EBackupNoFileOpen; // go round again + break; + } + default: + { + break; + } + }//end of the "switch" statement + }//end of the "for" statement + } + +/** This is called by BUE when the restore has completed + Nothing to do here except tell the server + @param TDrive the drive that is being restored (unused) +*/ +void CSqlBackupClient::RestoreComplete(TDriveNumber /* aDrive */) + { + } + +/** This is called to let us know that the given SID is to be backed up + We ask the SQL server for a list of databases that want to be backed + up - this is because the backup flag is an internal metadata object + in the database, and to decouple we don't want to have to know how + this data is stored. + @param TSecureSid the UID of the application to backup + @param TDriveNumber the drive to be backed up (unused) + @leave +*/ +void CSqlBackupClient::InitialiseGetProxyBackupDataL(TSecureId aSid, TDriveNumber /*aDrive*/) + { + // get the list of database files to back up - this is provided by the SQL server + GetBackupListL(aSid); + // this is the index of the file being processed - point to the beginning + iFileIndex=0; + // the first state of the backup state machine + iState=EBackupNoFileOpen; + // save the sid for notifying the server when the backup is complete + iSid=aSid; + } + +/** Called when the BUE wants to start sending data to us + @param TSecureId the UID of the application that is to be restored + @param TDriveNumber the drive to restore (unused) + @leave +*/ +void CSqlBackupClient::InitialiseRestoreProxyBaseDataL(TSecureId aSid, TDriveNumber /* aDrive */) + { + iBuffer->Des().Zero(); + // this is the first state of the restore state machine + iState=ERestoreExpectChecksum; + iAnyData=EFalse; // to keep track in the state machine whether any data was actually sent + // save the sid for notifying the server when the restore is done + iSid=aSid; + } + +/** This is repeatedly called by the BUE to send us chunks of restore data (for the current SID) + Becuase the data is spread over chunks we need to manage the state across mutiple calls + to this method so we use a state machine + @leave KErrCorrupt if the data is incomplete or the checksum fails + @param TDesc8 the data to be restored + @param TBool set when there is not more data to restore + +Attention!!! This function won't work properly if aInBuffer parameter contains odd number of bytes!!! +(a legacy problem, if it is a problem at all, because the B&R engine probably sends the data in chunks with even size) +*/ +void CSqlBackupClient::RestoreBaseDataSectionL(TDesC8& aInBuffer, TBool aFinishedFlag) + { + // used to walk the buffer + // got a new buffer - because each time this method is called, we have a + // fresh chunk of data + TInt inBufferPos = 0; + + // convert the buffer - this is KMaxHeaderSize=256+KMaxFileName + TPtr outBufPtr = iBuffer->Des(); + + // to mark when the state machine is through + TBool done = EFalse; + + // check whether this is an empty restore + if(aFinishedFlag && !iAnyData) + { + // we have to do this and not rely on aFinishedFlag alone, becuase + // if aFinished is used, we'll process the last state of the machine + // which does tidyup, except that if there was no data, no tidyup should + // be done + return; + } + + // run the machine + do + { + // how many bytes are there available in the buffer for processing? + TInt bytesAvailable = aInBuffer.Size() - inBufferPos; + // the reason why we are testing finishedFlag is because we must + // make sure we re-enter the machine to do the tidyup + if(bytesAvailable <= 0 && !aFinishedFlag) + { + // ran out of data in the chunk + // so we return and wait for more data to arrive + return; + } + if(aFinishedFlag && iState != ERestoreComplete && iState != ERestoreExpectData) + { + // ran out of data early + // will be ERestoreComplete if data not aligned on 128 + // will be ERestoreExpectData if data aligned on 128 + __SQLLEAVE(KErrCorrupt); + } + // yep there was some data in the chunk if we got here + if(bytesAvailable > 0) + { + iAnyData = ETrue; + } + switch(iState) + { + case ERestoreExpectChecksum: // 16 bytes (the header is UTF16 encoded, 8 unicode characters for the checksum) + { + const TInt KCheckSumStrLen = 8; + CopyBufData(aInBuffer, inBufferPos, outBufPtr, KCheckSumStrLen); + if(outBufPtr.Length() == KCheckSumStrLen) + { + iChecksum = ::GetNumUint32L(outBufPtr); + iState = ERestoreExpectOldFileSize; + outBufPtr.Zero(); + } + break; + } + case ERestoreExpectOldFileSize: // 16 bytes (the header is UTF16 encoded, 8 unicode characters for 32-bit old file size) + { + const TInt KOldFileSizeStrLen = 8; + CopyBufData(aInBuffer, inBufferPos, outBufPtr, KOldFileSizeStrLen); + if(outBufPtr.Length() == KOldFileSizeStrLen) + { + TUint32 oldFileSize = ::GetNumUint32L(outBufPtr); + if(oldFileSize == KMagicNum) + { + iState = ERestoreExpectVersion; + } + else + { + iFileSize = oldFileSize; + iState = ERestoreExpectFileNameSize; + } + outBufPtr.Zero(); + } + break; + } + case ERestoreExpectVersion: + { + const TInt KVersionStrLen = 4; + CopyBufData(aInBuffer, inBufferPos, outBufPtr, KVersionStrLen); + if(outBufPtr.Length() == KVersionStrLen) + { + //Ignore the version: ::GetNumUint32L(outBufPtr); + //At this stage we know that the version is 2+ + iState = ERestoreExpectFileSize; + outBufPtr.Zero(); + } + break; + } + case ERestoreExpectFileSize: + { + const TInt KFileSizeStrLen = 16; + CopyBufData(aInBuffer, inBufferPos, outBufPtr, KFileSizeStrLen); + if(outBufPtr.Length() == KFileSizeStrLen) + { + iFileSize = GetNumInt64L(outBufPtr); + iState = ERestoreExpectFileNameSize; + outBufPtr.Zero(); + } + break; + } + case ERestoreExpectFileNameSize: // the size of the file name to restore + { + const TInt KFileNameLenStrLen = 8; + CopyBufData(aInBuffer, inBufferPos, outBufPtr, KFileNameLenStrLen); + if(outBufPtr.Length() == KFileNameLenStrLen) + { + iFileNameSize = GetNumUint32L(outBufPtr); + iState = ERestoreExpectFileName; + outBufPtr.Zero(); + } + break; + } + case ERestoreExpectFileName: // the name of the file to restore + { + CopyBufData(aInBuffer, inBufferPos, outBufPtr, iFileNameSize); + if(outBufPtr.Length() == iFileNameSize) + { + iState = ERestoreExpectData; + outBufPtr.Append(KRestoreSuffix); + // now we start writing the data to the target file + // write to a temp - double disk space potentially + // once all the temp files are created, then they are renamed to the + // real file names in one fell swoop + __SQLLEAVE_IF_ERROR(iFile.Replace(iInterface->Fs(), outBufPtr, EFileWrite | EFileShareExclusive)); + outBufPtr.Zero(); + } + break; + } + case ERestoreExpectData: // now for the data + { + TInt len = Min((aInBuffer.Size() - inBufferPos), iFileSize); + __SQLLEAVE_IF_ERROR(iFile.Write(aInBuffer.Mid(inBufferPos, len))); + inBufferPos += len; + iFileSize -= len; + if(iFileSize == 0) + { + iState = ERestoreComplete; + } + break; + } + case ERestoreComplete: // file completely restored + { + // calculate the checksum + TUint32 cksum = CheckSumL(iFile) & 0xFFFFFFFF; + + // validate that the checksum matches + if(cksum!=iChecksum) + { + __SQLLEAVE(KErrCorrupt); + } + + // done with the file now - has to follow checksum cos it + // expects ann open file + iFile.Close(); + + // end of data - or another file to be restored? + if(aFinishedFlag) + { + // we need to rename all the + // temp rst files to the real database names + CDir *dir=NULL; + __SQLLEAVE_IF_ERROR(iInterface->Fs().GetDir(KRestoreFilter,KEntryAttNormal,ESortNone,dir)); + CleanupStack::PushL(dir); + for(TInt a=0;aCount();++a) + { + TEntry entry=(*dir)[a]; + TPtr rst=entry.iName.Des(); + TInt len=rst.Length(); + // format .db.bak.rst + // just a convenience! + TBufC bak(rst.LeftTPtr(len-4)); + TBufC db(rst.LeftTPtr(len-8)); + + // first, rename the orig .db as .bak just in case + // ok if not found - might have been deleted. + //the ".bak" file, if exists, will be deleted first. + (void)iInterface->Fs().Delete(bak); + TInt err=iInterface->Fs().Rename(db,bak); + if(err!=KErrNone && err!=KErrNotFound) + { + __SQLLEAVE(err); + } + + // now, rename the .rst as .db + __SQLLEAVE_IF_ERROR(iInterface->Fs().Rename(rst,db)); + + // if we got here, we have a backup of the original database in .db.bak + // and the new database in .db + } + + // clean up dir + //delete dir; + CleanupStack::PopAndDestroy(dir); + dir=NULL; + + // now delete all the .bak files + // we do this here and not part of the earlier loop + // because we want to make sure that we have a coherent set of database + // files that belong together and not bits of old and new + __SQLLEAVE_IF_ERROR(iInterface->Fs().GetDir(KBackupFilter,KEntryAttNormal,ESortNone,dir)); + CleanupStack::PushL(dir); + for(TInt a1=0;a1Count();++a1) + { + TEntry entry=(*dir)[a1]; + TPtr bak=entry.iName.Des(); + __SQLLEAVE_IF_ERROR(iInterface->Fs().Delete(bak)); + } + + // clean up dir + //delete dir; + CleanupStack::PopAndDestroy(dir); + dir=NULL; + done=ETrue; + } + else + { + iState=ERestoreExpectChecksum; + } + + break; + } + default: + break; + } + } while(!done); + } + +/** The operation was terminated - we should tidyup here (as best we can) + Nothing needs to be done for a backup. Restore is more + complicated in the case of an interruption. + What we need to do here is move all the backup files + back to being db files.... +*/ +void CSqlBackupClient::TerminateMultiStageOperation() + { + // backup/restore terminated, try to tidy up! Can't leave, can't Panic!!!!! + // rename all the .bak files to .db + CDir *dir=NULL; + TInt rc=iInterface->Fs().GetDir(KBackupFilter,KEntryAttNormal,ESortNone,dir); + if(KErrNone!=rc) + { + // can't get a file list - can't do anything + return; + } + for(TInt a=0;aCount();++a) + { + TEntry entry=(*dir)[a]; + TPtr bak=entry.iName.Des(); + TInt len=bak.Length(); + TBufC db(bak.LeftTPtr(len-4)); + rc=iInterface->Fs().Delete(db); // rename does not overwrite + if(KErrNone!=rc) + { + // nothing happened, still have bak file (and new db) + delete dir; + return; + } + rc=iInterface->Fs().Rename(bak,db); + if(KErrNone!=rc) + { + // still have bak file, but db is gone! + delete dir; + return; + } + // backup restored ok + } + // cleanup dir + delete dir; + } + +/** We do our own checksumming so we don't need this + @return the checksum + @param TDriveNumber the drive affected (unused) +*/ +TUint CSqlBackupClient::GetDataChecksum(TDriveNumber /* aDrive */) + { + // not required - not implemented + const TUint KArbitraryNumber = 1024; + return KArbitraryNumber; + } + +/** We don't support incremental backup +*/ +void CSqlBackupClient::GetSnapshotDataL(TDriveNumber /* aDrive */, TPtr8& /* aBuffer */, + TBool& /* aFinishedFlag */) + { + // incremental backup not supported + __SQLLEAVE(KErrNotSupported); + } + +/** We don't support incremental backup +*/ +void CSqlBackupClient::InitialiseGetBackupDataL(TDriveNumber /* aDrive */) + { + // incremental backup not supported + __SQLLEAVE(KErrNotSupported); + } + +/** We don't support incremental backup +*/ +void CSqlBackupClient::InitialiseRestoreBaseDataL(TDriveNumber /* aDrive */) + { + // incremental backup not supported + __SQLLEAVE(KErrNotSupported); + } + +/** We don't support incremental backup +*/ +void CSqlBackupClient::InitialiseRestoreIncrementDataL(TDriveNumber /* aDrive */) + { + // incremental backup not supported + __SQLLEAVE(KErrNotSupported); + } + +/** We don't support incremental backup +*/ +void CSqlBackupClient::RestoreIncrementDataSectionL(TDesC8& /* aBuffer */, TBool /* aFinishedFlag */) + { + // incremental backup not supported + __SQLLEAVE(KErrNotSupported); + } + +/** We don't support incremental backup +*/ +void CSqlBackupClient::AllSnapshotsSuppliedL() + { + // incremental backup not supported + // cannot leave or panic! + } + +/** We don't support incremental backup +*/ +void CSqlBackupClient::ReceiveSnapshotDataL(TDriveNumber /* aDrive */, TDesC8& /* aBuffer */, + TBool /* aFinishedFlag */) + { + // incremental backup not supported + __SQLLEAVE(KErrNotSupported); + } + +/** + Get a list of database files that need to be backed up + This is decided by the SQL server on the basis of the UID provided + and whether the metadata in the database indicates that this data + should be backed up or not. The list of database files is populated + into the iFileList array. + @leave + @param TSecureSid the UID of the data owner +*/ +void CSqlBackupClient::GetBackupListL(TSecureId aSid) + { + // we own the array - the SQL server just populates it + iInterface->GetBackUpListL(aSid,iFileList); + } + +/** A simple checksumming algorithm to allow a degree + of trust that the backup and restore worked + This is visble externally because the test harness + needs to use it - NOTE the file pointer will be back at the + start when this function ends. + @leave + @param RFile64 an OPEN file to checksum +*/ +TUint64 CSqlBackupClient::CheckSumL(const RFile64& aOpenFile) const + { + // scoot through the database file building the checksum + TInt64 seekPos=0; // rewind first + __SQLLEAVE_IF_ERROR(aOpenFile.Seek(ESeekStart,seekPos)); + TUint64 total=0; + const TUint KCheckSumBlockSize = 4 * 1024; + HBufC8* block=HBufC8::NewLC(KCheckSumBlockSize); + TPtr8 ptr=block->Des(); + for(;;) + { + __SQLLEAVE_IF_ERROR(aOpenFile.Read(ptr)); + TInt len=ptr.Length(); + if(len==0) + { + break; + } + // calculate the checksum + for(TInt i=0;i= 0, ESqlPanicBadArgument); + __SQLASSERT(aDataLen > 0, ESqlPanicBadArgument); + + TInt needed = (aDataLen - aOutBuf.Length()) << K8to16bitShift; + TInt available = aInBuf.Size() - aInBufReadPos; + TInt len = Min(needed, available); + TPtrC8 ptr8 = aInBuf.Mid(aInBufReadPos, len); + aInBufReadPos += len; + + len >>= K8to16bitShift; + aOutBuf.Append((const TUint16*)ptr8.Ptr(), len); + }