persistentstorage/sql/SRC/Server/SqlBur.cpp
changeset 0 08ec8eefde2f
child 8 fa9941cf3867
--- /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><filename - UTF16 encoded>
+
+//////             Version 2
+//  8 chars          8 chars   4 chars     16 chars         8 chars             up to 256 characters (512 bytes)
+// <32-bit checksum><FFFFAA55><Version N#><64-bit filesize><32-bit filenamelen><filename - UTF16 encoded>
+
+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;a<dir->Count();++a)
+						{
+						TEntry entry=(*dir)[a];
+						TPtr rst=entry.iName.Des();
+						TInt len=rst.Length();
+						// format <filename>.db.bak.rst
+						// just a convenience!
+						TBufC<KMaxFileName> bak(rst.LeftTPtr(len-4));
+						TBufC<KMaxFileName> 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;a1<dir->Count();++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;a<dir->Count();++a)
+		{
+		TEntry entry=(*dir)[a];
+		TPtr bak=entry.iName.Des();
+		TInt len=bak.Length();
+		TBufC<KMaxFileName> 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<len;++i)
+			{
+			TUint64 carry=total&(0x8000000000000000ULL);
+			total<<=1;
+			if(carry)
+				{
+				total|=1;
+				}
+			TUint in=ptr[i];
+ 			total+=in;
+ 			}
+		};		
+	CleanupStack::PopAndDestroy(block);
+	// restore file position
+	seekPos=0;
+	__SQLLEAVE_IF_ERROR(aOpenFile.Seek(ESeekStart,seekPos));
+	return total;
+	}
+
+//Reads the content of aInBuf from position aInBufReadPos and stores the data into aOutBuf.
+//aDataLen is the length of the data. If the input buffer does not contain all the data, then only the
+//available data will be copied to the output buffer.
+//
+//Attention!!! This function won't work properly if aInBuf 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::CopyBufData(const TDesC8& aInBuf, TInt& aInBufReadPos, TDes& aOutBuf, TInt aDataLen)
+	{
+	__SQLASSERT(aInBufReadPos >= 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);
+	}