persistentstorage/sql/SRC/Server/SqlBur.cpp
changeset 0 08ec8eefde2f
child 8 fa9941cf3867
equal deleted inserted replaced
-1:000000000000 0:08ec8eefde2f
       
     1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include "SqlBur.h"
       
    17 #include "SqlAssert.h"
       
    18 #include "SqlPanic.h"
       
    19 
       
    20 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
       
    21 //////////////                     Backup database file header format                           ///////////////////
       
    22 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
       
    23 
       
    24 ////// No version (Version 0)
       
    25 //  8 chars          8 chars          8 chars             up to 256 characters (512 bytes)
       
    26 // <32-bit checksum><32-bit filesize><32-bit filenamelen><filename - UTF16 encoded>
       
    27 
       
    28 //////             Version 2
       
    29 //  8 chars          8 chars   4 chars     16 chars         8 chars             up to 256 characters (512 bytes)
       
    30 // <32-bit checksum><FFFFAA55><Version N#><64-bit filesize><32-bit filenamelen><filename - UTF16 encoded>
       
    31 
       
    32 const TInt KBackupHeaderVersion = 2;			//Current backup database file header version
       
    33 
       
    34 const TUint32 KMagicNum = 0xFFFFAA55;			//Magic number. If the "old database file size" field in the header
       
    35 												//has this value, then the header version is 2+
       
    36 const TInt KMaxHeaderSize = 256 + KMaxFileName;	//The size of the buffer used for the operations on the header
       
    37 
       
    38 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
       
    39 
       
    40 //Extracts and returns 32-bit integer from aNumBuf buffer.
       
    41 static TUint32 GetNumUint32L(const TDesC& aNumBuf)
       
    42 	{
       
    43 	TLex lex(aNumBuf);
       
    44 	lex.SkipSpace();
       
    45 	TUint32 num = 0xFFFFFFFF;
       
    46 	__SQLLEAVE_IF_ERROR(lex.Val(num, EHex));
       
    47 	return num;
       
    48 	}
       
    49 
       
    50 //Extracts and returns 64-bit integer from aNumBuf buffer.
       
    51 static TInt64 GetNumInt64L(const TDesC& aNumBuf)
       
    52 	{
       
    53 	TLex lex(aNumBuf);
       
    54 	lex.SkipSpace();
       
    55 	TInt64 num = -1;
       
    56 	__SQLLEAVE_IF_ERROR(lex.Val(num, EHex));
       
    57 	return num;
       
    58 	}
       
    59 
       
    60 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
       
    61 
       
    62 // string consts
       
    63 _LIT(KRestoreFilter,"*.rst"); // the filter for restore files
       
    64 _LIT(KBackupFilter,"*.bak");// the filter for backup files
       
    65 _LIT(KRestoreSuffix,".bak.rst"); // the suffix for restore files (a shortcut by using double suffix :)
       
    66 
       
    67 const TUint K8to16bitShift = 1;
       
    68 
       
    69 /** Standard two phase construction
       
    70 	@return an instance of the backup client
       
    71 	@param a pointer to the SQL server which must have implemented the
       
    72 			TSqlSrvBurInterface interface
       
    73 	@leave if no memory
       
    74 */
       
    75 CSqlBackupClient* CSqlBackupClient::NewLC(MSqlSrvBurInterface *aInterface)
       
    76 	{
       
    77 	CSqlBackupClient *self=(CSqlBackupClient *)new(ELeave) CSqlBackupClient(aInterface);
       
    78 	CleanupStack::PushL(self);
       
    79 	self->ConstructL();
       
    80 	return self;
       
    81 	}
       
    82 
       
    83 /** Standard two phase construction
       
    84 	@return an instance of the backup client
       
    85 	@param a pointer to the SQL server which must have implemented the
       
    86 			TSqlSrvBurInterface interface
       
    87 	@leave if no memory
       
    88 */
       
    89 CSqlBackupClient* CSqlBackupClient::NewL(MSqlSrvBurInterface *aInterface)
       
    90 	{
       
    91 	CSqlBackupClient *self=(CSqlBackupClient *) NewLC(aInterface);
       
    92 	CleanupStack::Pop();
       
    93 	return self;
       
    94 	}
       
    95 
       
    96 /** Standard two phase construction
       
    97 	@param a pointer to the SQL server which must have implemented the
       
    98 			TSqlSrvBurInterface interface
       
    99 */		
       
   100 CSqlBackupClient::CSqlBackupClient(MSqlSrvBurInterface *aInterface)
       
   101 : CActive(EPriorityStandard), iInterface(aInterface)
       
   102 	{
       
   103 	}
       
   104 
       
   105 /** Usual tidy up
       
   106 */
       
   107 CSqlBackupClient::~CSqlBackupClient()
       
   108 	{
       
   109 	// cancel outstanding requests
       
   110 	Cancel();
       
   111 	
       
   112 	// release the pub/sub property
       
   113 	iProperty.Close();
       
   114 	
       
   115 	// make sure the file list is released
       
   116 	iFileList.Reset();
       
   117 	
       
   118 	// the header buffer
       
   119 	delete iBuffer;
       
   120 	
       
   121 	// the file list array
       
   122 	iFileList.Close();
       
   123 	
       
   124 	// close the file
       
   125 	iFile.Close();
       
   126 	
       
   127 	// nuke the active backup client
       
   128 	if(iActiveBackupClient)
       
   129 		{
       
   130 		delete iActiveBackupClient;
       
   131 		}
       
   132 	}
       
   133 
       
   134 /** Standard two phase construction
       
   135 	@leave if non memory or StartL leaves
       
   136 */	
       
   137 void CSqlBackupClient::ConstructL()
       
   138 	{
       
   139 	// attach to backup/restore publish/subscribe property
       
   140 	__SQLLEAVE_IF_ERROR(iProperty.Attach(KUidSystemCategory,KUidBackupRestoreKey));
       
   141 	
       
   142 	// add us to the scheduler
       
   143 	CActiveScheduler::Add(this);
       
   144 
       
   145 	// a place for the header info
       
   146 	iBuffer=HBufC::NewL(KMaxHeaderSize);
       
   147 
       
   148 	// set active and request notification of changes to backup
       
   149 	// and restore publish/subscribe property
       
   150 	StartL();	
       
   151 	}
       
   152 
       
   153 /** Nuke outstanding requests
       
   154 */
       
   155 void CSqlBackupClient::DoCancel()
       
   156 	{
       
   157 	// lose any oustanding reqs
       
   158 	iProperty.Cancel();
       
   159 	}
       
   160 
       
   161 /** Not implemented
       
   162 	@return a flag indicating whether we actioned the error
       
   163 	@param the error unused
       
   164 */
       
   165 TInt CSqlBackupClient::RunError(TInt /* aError */)
       
   166 	{
       
   167 	// just satisfy it that we did something!
       
   168 	return KErrNone;
       
   169 	}
       
   170 
       
   171 /**	Kick off the BUR client
       
   172 	@leave if TestBurStatusL leaves
       
   173 */
       
   174 void CSqlBackupClient::StartL()
       
   175 	{
       
   176 	if(!IsActive())
       
   177 		{
       
   178 		TestBurStatusL();
       
   179 		NotifyChange();
       
   180 		}
       
   181 	}
       
   182 
       
   183 /** Resubscribe and wait for events
       
   184 */	
       
   185 void CSqlBackupClient::NotifyChange()
       
   186 	{
       
   187 	iProperty.Subscribe(iStatus);
       
   188 	SetActive();
       
   189 	}
       
   190 
       
   191 /** Something happened. Find out what.
       
   192 	Create an instance of BUR client if required
       
   193 	Delete it if no longer required
       
   194 	This is for performance reasons
       
   195 	@leave if ConfirmReadyForBURL leaves
       
   196 */
       
   197 void CSqlBackupClient::TestBurStatusL()
       
   198 	{
       
   199 	TInt status;
       
   200 	if(iProperty.Get(status)!=KErrNotFound)
       
   201 		{
       
   202 		status&=KBURPartTypeMask;
       
   203 		switch(status)
       
   204 			{
       
   205 			case EBURUnset:
       
   206 				// same as EBURNormal
       
   207 			case EBURNormal:
       
   208 				if(iActiveBackupClient)
       
   209 					{
       
   210 					delete iActiveBackupClient;
       
   211 					iActiveBackupClient=NULL;
       
   212 					}
       
   213 				break;
       
   214 			case EBURBackupFull:
       
   215 			case EBURBackupPartial:
       
   216 				// we only do full backups
       
   217 				if(!iActiveBackupClient)
       
   218 					{
       
   219 					iActiveBackupClient=CActiveBackupClient::NewL(this);
       
   220 					}
       
   221 				iActiveBackupClient->ConfirmReadyForBURL(KErrNone);
       
   222 				break;
       
   223 			case EBURRestoreFull:
       
   224 			case EBURRestorePartial:
       
   225 				// we only do full restores
       
   226 				if(!iActiveBackupClient)
       
   227 					{
       
   228 					iActiveBackupClient=CActiveBackupClient::NewL(this);
       
   229 					}
       
   230 				iActiveBackupClient->ConfirmReadyForBURL(KErrNone);
       
   231 				break;
       
   232 			default:
       
   233 				return;
       
   234 			}
       
   235 		}
       
   236 	}
       
   237 
       
   238 /** Called when BUE notifies a BUR event
       
   239 	@leave if TestBurStatusL leaves
       
   240 */
       
   241 void CSqlBackupClient::RunL()
       
   242 	{
       
   243 	NotifyChange();
       
   244 	TestBurStatusL();
       
   245 	}
       
   246 
       
   247 /** This is supposed to allow the BUE to know in advance how much
       
   248 	data is coming - but unfortunately there is no way to know this
       
   249 	at this stage since we don't even know yet what SID is being processed
       
   250 	So we just answer some number to make the BUE happy. It doesn't
       
   251 	actually rely on this number so there is no risk - the aFinishedFlag
       
   252 	indicates the end of data, not the value returned here. It is
       
   253 	supposed to allow the BUE to optimise its behaviour by know up front
       
   254 	the data volume.
       
   255 	@return an arbitrary number
       
   256 	@param TDrive unused
       
   257 */
       
   258 TUint CSqlBackupClient::GetExpectedDataSize(TDriveNumber /* aDrive */)
       
   259 	{
       
   260 	// we have no idea at this point - we even don't know who is to be backed up yet
       
   261 	const TUint KArbitraryNumber = 1024;
       
   262 	return KArbitraryNumber;
       
   263 	}
       
   264 
       
   265 /** This is the backup state machine
       
   266 	Because the data has to be sent back in sections and the various
       
   267 	components of the dataflow may straddle chunks, we have to keep
       
   268 	track of where we are between each transfer - a state machine is
       
   269 	the simplest and most understandable implementation
       
   270 	@param TPtr this is where the data will be put to be passed back
       
   271 	@param TBool set to true when all data has been submitted for backup
       
   272 	@leave
       
   273 */
       
   274 void CSqlBackupClient::GetBackupDataSectionL(TPtr8& aBuffer, TBool& aFinishedFlag)
       
   275 	{
       
   276 	// don't assume they set it to false
       
   277 	aFinishedFlag=EFalse;
       
   278 	// any files to backup
       
   279 	if(iFileList.Count()==0)
       
   280 		{
       
   281 		// nothing to backup - just return the finished flag
       
   282 		aFinishedFlag=ETrue;
       
   283 		// clear down the list
       
   284 		iFileList.Reset();
       
   285 		// iFileList closed in dtor
       
   286 		return;
       
   287 		}
       
   288 	
       
   289 	// run the state machine
       
   290 	for(TInt bufFreeSpace=aBuffer.MaxSize()-aBuffer.Size(); bufFreeSpace>0; bufFreeSpace=aBuffer.MaxSize()-aBuffer.Size())
       
   291 		{
       
   292 		switch(iState)
       
   293 			{
       
   294 			case EBackupNoFileOpen: // open a file for processing
       
   295 				{
       
   296 				if(iFileIndex>=iFileList.Count())
       
   297 					{
       
   298 					// all files have been processed - send the finished flag
       
   299 					aFinishedFlag=ETrue;
       
   300 					// clear down the filelist
       
   301 					iFileList.Reset();
       
   302 					return;
       
   303 					}
       
   304 				// open the database file to send
       
   305 				TInt rc=iFile.Open(	iInterface->Fs(), iFileList[iFileIndex].FullName(), EFileRead | EFileShareExclusive);
       
   306 				if(KErrNone!=rc)
       
   307 					{
       
   308 					// there's nothing we can do if we can't open the file so we just skip it
       
   309 					++iFileIndex;
       
   310 					break;
       
   311 					}
       
   312 				iState=EBackupOpenNothingSent;
       
   313 				break;
       
   314 				}
       
   315 			case EBackupOpenNothingSent: // nothing sent (so far) for this file - send the header info
       
   316 				{
       
   317 				TInt64 fileSize;
       
   318 				if(KErrNone!=iFile.Size(fileSize) || fileSize==0) // short circuit eval
       
   319 					{
       
   320 					// empty or unreadable - skip this file
       
   321 					iState=EBackupEndOfFile;
       
   322 					break;
       
   323 					}
       
   324 				
       
   325 				// build the header - this is an instance member because it
       
   326 				// has to persist over multiple calls to this method
       
   327 				TPtr hdrPtr=iBuffer->Des();
       
   328 				
       
   329 				// get the checksum - only grab last 4 bytes - enough to be satisfied that
       
   330 				// the backup and restore worked ok
       
   331 				TUint32 checksum = CheckSumL(iFile) & 0xFFFFFFFF;
       
   332 
       
   333 				// build the header
       
   334 				const TDesC& fileName = iFileList[iFileIndex].FullName();
       
   335 				hdrPtr.Format(_L("%8x%8x%4x%16lx%8x%S"),
       
   336 					checksum,					// %8x
       
   337 					KMagicNum,					// %8x
       
   338 					KBackupHeaderVersion,		// %4x
       
   339 					fileSize,					// %16lx
       
   340 					fileName.Length(),			// %8x
       
   341 					&fileName);					// %S
       
   342 				
       
   343 				// we need it to look like an 8bit buffer
       
   344 				TPtr8 hdrPtr8((TUint8*)hdrPtr.Ptr(),hdrPtr.Size(),hdrPtr.Size());
       
   345 							
       
   346 				TInt len = Min(hdrPtr8.Size(), bufFreeSpace);
       
   347 				
       
   348 				// append the header to the buffer (only till it's full)
       
   349 				aBuffer.Append(hdrPtr8.Ptr(), len);
       
   350 				
       
   351 				// decide what needs to happen next
       
   352 				// if complete then we need data, otherwise we need to put
       
   353 				// the rest of the header in the next chunk
       
   354 				if(hdrPtr8.Size() <= bufFreeSpace)
       
   355 					{
       
   356 					iState = EBackupOpenAllHeaderSent;
       
   357 					}
       
   358 				else
       
   359 					{
       
   360 					// we need to keep track of how much of the header has
       
   361 					// been sent so that we only send the reminder on the next
       
   362 					// iteration
       
   363 					iHeaderSent = len;
       
   364 					iState = EBackupOpenPartHeaderSent;
       
   365 					}
       
   366 				break;
       
   367 				}
       
   368 			case EBackupOpenPartHeaderSent: // need to send the rest of the header
       
   369 				{
       
   370 				// get back the header - this is already loaded with the necessary info
       
   371 				// from the previous state we were in
       
   372 				TPtr hdrPtr = iBuffer->Des();
       
   373 				
       
   374 				// we need it to look like an 8bit buffer
       
   375 				TPtr8 hdrPtr8((TUint8*)hdrPtr.Ptr(),hdrPtr.Size(),hdrPtr.Size());
       
   376 				
       
   377 				// how many bytes have we yet to send?
       
   378 				TInt bytesRemaining = hdrPtr.Size() - iHeaderSent;
       
   379 				TInt len = Min(bytesRemaining, bufFreeSpace);
       
   380 				aBuffer.Append(hdrPtr8.Ptr() + iHeaderSent, len);
       
   381 				
       
   382 				if(bytesRemaining <= bufFreeSpace)
       
   383 					{
       
   384 					iHeaderSent = 0; // ready for next header
       
   385 					iState = EBackupOpenAllHeaderSent;
       
   386 					}
       
   387 				else
       
   388 					{
       
   389 					iHeaderSent += len; // ready to do round again
       
   390 					//iState=EBackupOpenPartHeaderSent; same state as now!
       
   391 					}
       
   392 				break;
       
   393 				}
       
   394 			case EBackupOpenAllHeaderSent: // need to send some data
       
   395 				{
       
   396 				TPtr8 ptr((TUint8*)aBuffer.Ptr() + aBuffer.Size(), 0, bufFreeSpace);
       
   397 				__SQLLEAVE_IF_ERROR(iFile.Read(ptr));
       
   398 				TInt bytesRead = ptr.Size();
       
   399 				aBuffer.SetLength(aBuffer.Size() + bytesRead);
       
   400 				// EOF
       
   401 				if(bytesRead == 0)
       
   402 					{
       
   403 					iState = EBackupEndOfFile;
       
   404 					break;
       
   405 					}
       
   406 				break;
       
   407 				}
       
   408 			case EBackupEndOfFile:
       
   409 				{
       
   410 				iFile.Close();
       
   411 				++iFileIndex; // move on to next file
       
   412 				iState = EBackupNoFileOpen; // go round again
       
   413 				break;
       
   414 				}
       
   415 			default:
       
   416 				{
       
   417 				break;
       
   418 				}
       
   419 			}//end of the "switch" statement
       
   420 		}//end of the "for" statement
       
   421 	}
       
   422 
       
   423 /** This is called by BUE when the restore has completed
       
   424 	Nothing to do here except tell the server
       
   425 	@param TDrive the drive that is being restored (unused)
       
   426 */
       
   427 void CSqlBackupClient::RestoreComplete(TDriveNumber /* aDrive */)
       
   428 	{
       
   429 	}
       
   430 
       
   431 /** This is called to let us know that the given SID is to be backed up
       
   432 	We ask the SQL server for a list of databases that want to be backed
       
   433 	up - this is because the backup flag is an internal metadata object
       
   434 	in the database, and to decouple we don't want to have to know how
       
   435 	this data is stored.
       
   436 	@param TSecureSid the UID of the application to backup
       
   437 	@param TDriveNumber the drive to be backed up (unused)
       
   438 	@leave
       
   439 */
       
   440 void CSqlBackupClient::InitialiseGetProxyBackupDataL(TSecureId aSid, TDriveNumber /*aDrive*/)
       
   441 	{
       
   442 	// get the list of database files to back up - this is provided by the SQL server
       
   443 	GetBackupListL(aSid);
       
   444 	// this is the index of the file being processed - point to the beginning
       
   445 	iFileIndex=0;
       
   446 	// the first state of the backup state machine
       
   447 	iState=EBackupNoFileOpen;
       
   448 	// save the sid for notifying the server when the backup is complete
       
   449 	iSid=aSid;
       
   450 	}
       
   451 
       
   452 /** Called when the BUE wants to start sending data to us
       
   453 	@param TSecureId the UID of the application that is to be restored
       
   454 	@param TDriveNumber the drive to restore (unused)
       
   455 	@leave
       
   456 */
       
   457 void CSqlBackupClient::InitialiseRestoreProxyBaseDataL(TSecureId aSid, TDriveNumber /* aDrive */)
       
   458 	{
       
   459 	iBuffer->Des().Zero();
       
   460 	// this is the first state of the restore state machine
       
   461 	iState=ERestoreExpectChecksum;
       
   462 	iAnyData=EFalse; // to keep track in the state machine whether any data was actually sent
       
   463 	// save the sid for notifying the server when the restore is done
       
   464 	iSid=aSid;
       
   465 	}
       
   466 
       
   467 /** This is repeatedly called by the BUE to send us chunks of restore data (for the current SID)
       
   468     Becuase the data is spread over chunks we need to manage the state across mutiple calls
       
   469     to this method so we use a state machine
       
   470     @leave KErrCorrupt if the data is incomplete or the checksum fails
       
   471     @param TDesc8 the data to be restored
       
   472     @param TBool set when there is not more data to restore
       
   473 
       
   474 Attention!!! This function won't work properly if aInBuffer parameter contains odd number of bytes!!!
       
   475 (a legacy problem, if it is a problem at all, because the B&R engine probably sends the data in chunks with even size)
       
   476 */
       
   477 void CSqlBackupClient::RestoreBaseDataSectionL(TDesC8& aInBuffer, TBool aFinishedFlag)
       
   478 	{
       
   479 	// used to walk the buffer
       
   480 	// got a new buffer - because each time this method is called, we have a
       
   481 	// fresh chunk of data
       
   482 	TInt inBufferPos = 0;
       
   483 
       
   484 	// convert the buffer - this is KMaxHeaderSize=256+KMaxFileName
       
   485 	TPtr outBufPtr = iBuffer->Des();	
       
   486 	
       
   487 	// to mark when the state machine is through
       
   488 	TBool done = EFalse;
       
   489 	
       
   490 	// check whether this is an empty restore
       
   491 	if(aFinishedFlag && !iAnyData)
       
   492 		{
       
   493 		// we have to do this and not rely on aFinishedFlag alone, becuase
       
   494 		// if aFinished is used, we'll process the last state of the machine
       
   495 		// which does tidyup, except that if there was no data, no tidyup should
       
   496 		// be done
       
   497 		return;
       
   498 		}
       
   499 		
       
   500 	// run the machine
       
   501 	do
       
   502 		{
       
   503 		// how many bytes are there available in the buffer for processing?
       
   504 		TInt bytesAvailable = aInBuffer.Size() - inBufferPos;
       
   505 		// the reason why we are testing finishedFlag is because we must
       
   506 		// make sure we re-enter the machine to do the tidyup
       
   507 		if(bytesAvailable <= 0 && !aFinishedFlag)
       
   508 			{
       
   509 			// ran out of data in the chunk
       
   510 			// so we return and wait for more data to arrive
       
   511 			return;
       
   512 			}
       
   513 		if(aFinishedFlag && iState != ERestoreComplete && iState != ERestoreExpectData)
       
   514 			{
       
   515 			// ran out of data early
       
   516 			// will be ERestoreComplete if data not aligned on 128
       
   517 			// will be ERestoreExpectData if data aligned on 128
       
   518 			__SQLLEAVE(KErrCorrupt);
       
   519 			}
       
   520 		// yep there was some data in the chunk if we got here
       
   521 		if(bytesAvailable > 0)
       
   522 			{
       
   523 			iAnyData = ETrue;
       
   524 			}
       
   525 		switch(iState)
       
   526 			{
       
   527 			case ERestoreExpectChecksum: // 16 bytes (the header is UTF16 encoded, 8 unicode characters for the checksum)
       
   528 				{
       
   529 				const TInt KCheckSumStrLen = 8;
       
   530 				CopyBufData(aInBuffer, inBufferPos, outBufPtr, KCheckSumStrLen);
       
   531 				if(outBufPtr.Length() == KCheckSumStrLen)
       
   532 					{
       
   533 					iChecksum = ::GetNumUint32L(outBufPtr);
       
   534 					iState = ERestoreExpectOldFileSize;
       
   535 					outBufPtr.Zero();
       
   536 					}
       
   537 				break;
       
   538 				}
       
   539 			case ERestoreExpectOldFileSize: // 16 bytes (the header is UTF16 encoded, 8 unicode characters for 32-bit old file size)
       
   540 				{
       
   541 				const TInt KOldFileSizeStrLen = 8;
       
   542 				CopyBufData(aInBuffer, inBufferPos, outBufPtr, KOldFileSizeStrLen);
       
   543 				if(outBufPtr.Length() == KOldFileSizeStrLen)
       
   544 					{
       
   545 					TUint32 oldFileSize = ::GetNumUint32L(outBufPtr);
       
   546 					if(oldFileSize == KMagicNum)
       
   547 						{
       
   548 						iState = ERestoreExpectVersion;
       
   549 						}
       
   550 					else
       
   551 						{
       
   552 						iFileSize = oldFileSize;	
       
   553 						iState = ERestoreExpectFileNameSize;
       
   554 						}
       
   555 					outBufPtr.Zero();
       
   556 					}
       
   557 				break;
       
   558 				}	
       
   559 			case ERestoreExpectVersion:
       
   560 				{
       
   561 				const TInt KVersionStrLen = 4;
       
   562 				CopyBufData(aInBuffer, inBufferPos, outBufPtr, KVersionStrLen);
       
   563 				if(outBufPtr.Length() == KVersionStrLen)
       
   564 					{
       
   565 					//Ignore the version: ::GetNumUint32L(outBufPtr);	
       
   566 					//At this stage we know that the version is 2+
       
   567 					iState = ERestoreExpectFileSize;
       
   568 					outBufPtr.Zero();
       
   569 					}
       
   570 				break;
       
   571 				}
       
   572 			case ERestoreExpectFileSize:
       
   573 				{
       
   574 				const TInt KFileSizeStrLen = 16;
       
   575 				CopyBufData(aInBuffer, inBufferPos, outBufPtr, KFileSizeStrLen);
       
   576 				if(outBufPtr.Length() == KFileSizeStrLen)
       
   577 					{
       
   578 					iFileSize = GetNumInt64L(outBufPtr);	
       
   579 					iState = ERestoreExpectFileNameSize;
       
   580 					outBufPtr.Zero();
       
   581 					}
       
   582 				break;
       
   583 				}
       
   584 			case ERestoreExpectFileNameSize: // the size of the file name to restore
       
   585 				{
       
   586 				const TInt KFileNameLenStrLen = 8;
       
   587 				CopyBufData(aInBuffer, inBufferPos, outBufPtr, KFileNameLenStrLen);
       
   588 				if(outBufPtr.Length() == KFileNameLenStrLen)
       
   589 					{
       
   590 					iFileNameSize = GetNumUint32L(outBufPtr);		
       
   591 					iState = ERestoreExpectFileName;
       
   592 					outBufPtr.Zero();
       
   593 					}
       
   594 				break;
       
   595 				}
       
   596 			case ERestoreExpectFileName:  // the name of the file to restore
       
   597 				{
       
   598 				CopyBufData(aInBuffer, inBufferPos, outBufPtr, iFileNameSize);
       
   599 				if(outBufPtr.Length() == iFileNameSize)
       
   600 					{
       
   601 					iState = ERestoreExpectData;
       
   602 					outBufPtr.Append(KRestoreSuffix);
       
   603 					// now we start writing the data to the target file
       
   604 					// write to a temp - double disk space potentially
       
   605 					// once all the temp files are created, then they are renamed to the
       
   606 					// real file names in one fell swoop
       
   607 					__SQLLEAVE_IF_ERROR(iFile.Replace(iInterface->Fs(), outBufPtr, EFileWrite | EFileShareExclusive));
       
   608 					outBufPtr.Zero();
       
   609 					}
       
   610 				break;
       
   611 				}
       
   612 			case ERestoreExpectData: // now for the data
       
   613 				{
       
   614 				TInt len = Min((aInBuffer.Size() - inBufferPos), iFileSize);
       
   615 				__SQLLEAVE_IF_ERROR(iFile.Write(aInBuffer.Mid(inBufferPos, len)));
       
   616 				inBufferPos += len;
       
   617 				iFileSize -= len;
       
   618 				if(iFileSize == 0)
       
   619 					{
       
   620 					iState = ERestoreComplete;
       
   621 					}
       
   622 				break;
       
   623 				}
       
   624 			case ERestoreComplete: // file completely restored
       
   625 				{
       
   626 				// calculate the checksum
       
   627 				TUint32 cksum = CheckSumL(iFile) & 0xFFFFFFFF;
       
   628 				
       
   629 				// validate that the checksum matches
       
   630 				if(cksum!=iChecksum)
       
   631 					{
       
   632 					__SQLLEAVE(KErrCorrupt);
       
   633 					}
       
   634 
       
   635 				// done with the file now - has to follow checksum cos it
       
   636 				// expects ann open file
       
   637 				iFile.Close();
       
   638 
       
   639 				// end of data - or another file to be restored?
       
   640 				if(aFinishedFlag)
       
   641 					{
       
   642 					// we need to rename all the
       
   643 					// temp rst files to the real database names
       
   644 					CDir *dir=NULL;
       
   645 					__SQLLEAVE_IF_ERROR(iInterface->Fs().GetDir(KRestoreFilter,KEntryAttNormal,ESortNone,dir));
       
   646 					CleanupStack::PushL(dir);
       
   647 					for(TInt a=0;a<dir->Count();++a)
       
   648 						{
       
   649 						TEntry entry=(*dir)[a];
       
   650 						TPtr rst=entry.iName.Des();
       
   651 						TInt len=rst.Length();
       
   652 						// format <filename>.db.bak.rst
       
   653 						// just a convenience!
       
   654 						TBufC<KMaxFileName> bak(rst.LeftTPtr(len-4));
       
   655 						TBufC<KMaxFileName> db(rst.LeftTPtr(len-8));
       
   656 						
       
   657 						// first, rename the orig .db as .bak just in case
       
   658 						// ok if not found - might have been deleted.
       
   659 						//the ".bak" file, if exists, will be deleted first.
       
   660 						(void)iInterface->Fs().Delete(bak);
       
   661 						TInt err=iInterface->Fs().Rename(db,bak);
       
   662 						if(err!=KErrNone && err!=KErrNotFound)
       
   663 							{
       
   664 							__SQLLEAVE(err);
       
   665 							}
       
   666 						
       
   667 						// now, rename the .rst as .db
       
   668 						__SQLLEAVE_IF_ERROR(iInterface->Fs().Rename(rst,db));
       
   669 						
       
   670 						// if we got here, we have a backup of the original database in .db.bak
       
   671 						// and the new database in .db
       
   672 						}
       
   673 					
       
   674 					// clean up dir
       
   675 					//delete dir;
       
   676 					CleanupStack::PopAndDestroy(dir);
       
   677 					dir=NULL;
       
   678 					
       
   679 					// now delete all the .bak files
       
   680 					// we do this here and not part of the earlier loop
       
   681 					// because we want to make sure that we have a coherent set of database
       
   682 					// files that belong together and not bits of old and new
       
   683 					__SQLLEAVE_IF_ERROR(iInterface->Fs().GetDir(KBackupFilter,KEntryAttNormal,ESortNone,dir));
       
   684 					CleanupStack::PushL(dir);
       
   685 					for(TInt a1=0;a1<dir->Count();++a1)
       
   686 						{
       
   687 						TEntry entry=(*dir)[a1];
       
   688 						TPtr bak=entry.iName.Des();
       
   689 						__SQLLEAVE_IF_ERROR(iInterface->Fs().Delete(bak));
       
   690 						}
       
   691 					
       
   692 					// clean up dir
       
   693 					//delete dir;
       
   694 					CleanupStack::PopAndDestroy(dir);
       
   695 					dir=NULL;
       
   696 					done=ETrue;
       
   697 					}
       
   698 				else
       
   699 					{
       
   700 					iState=ERestoreExpectChecksum;
       
   701 					}
       
   702 					
       
   703 				break;
       
   704 				}
       
   705 			default:
       
   706 				break;
       
   707 			}
       
   708 		} while(!done);
       
   709 	}
       
   710 
       
   711 /** The operation was terminated - we should tidyup here (as best we can)
       
   712 	Nothing needs to be done for a backup. Restore is more
       
   713 	complicated in the case of an interruption.
       
   714 	What we need to do here is move all the backup files
       
   715 	back to being db files....
       
   716 */	
       
   717 void CSqlBackupClient::TerminateMultiStageOperation()
       
   718 	{
       
   719 	// backup/restore terminated, try to tidy up! Can't leave, can't Panic!!!!!
       
   720 	// rename all the .bak files to .db
       
   721 	CDir *dir=NULL;
       
   722 	TInt rc=iInterface->Fs().GetDir(KBackupFilter,KEntryAttNormal,ESortNone,dir);
       
   723 	if(KErrNone!=rc)
       
   724 		{
       
   725 		// can't get a file list - can't do anything
       
   726 		return;
       
   727 		}
       
   728 	for(TInt a=0;a<dir->Count();++a)
       
   729 		{
       
   730 		TEntry entry=(*dir)[a];
       
   731 		TPtr bak=entry.iName.Des();
       
   732 		TInt len=bak.Length();
       
   733 		TBufC<KMaxFileName> db(bak.LeftTPtr(len-4));
       
   734 		rc=iInterface->Fs().Delete(db); // rename does not overwrite
       
   735 		if(KErrNone!=rc)
       
   736 			{
       
   737 			// nothing happened, still have bak file (and new db)
       
   738 			delete dir;
       
   739 			return;
       
   740 			}
       
   741 		rc=iInterface->Fs().Rename(bak,db);
       
   742 		if(KErrNone!=rc)
       
   743 			{
       
   744 			// still have bak file, but db is gone!
       
   745 			delete dir;
       
   746 			return;
       
   747 			}
       
   748 		// backup restored ok
       
   749 		}
       
   750 	// cleanup dir
       
   751 	delete dir;
       
   752 	}
       
   753 
       
   754 /** We do our own checksumming so we don't need this
       
   755 	@return the checksum
       
   756 	@param TDriveNumber the drive affected (unused)
       
   757 */
       
   758 TUint CSqlBackupClient::GetDataChecksum(TDriveNumber /* aDrive */)
       
   759 	{
       
   760 	// not required - not implemented
       
   761 	const TUint KArbitraryNumber = 1024;
       
   762 	return KArbitraryNumber;
       
   763 	}
       
   764 
       
   765 /** We don't support incremental backup
       
   766 */
       
   767 void CSqlBackupClient::GetSnapshotDataL(TDriveNumber /* aDrive */, TPtr8& /* aBuffer */,
       
   768 										TBool& /* aFinishedFlag */)
       
   769 	{
       
   770 	// incremental backup not supported
       
   771 	__SQLLEAVE(KErrNotSupported);
       
   772 	}
       
   773 
       
   774 /** We don't support incremental backup
       
   775 */
       
   776 void CSqlBackupClient::InitialiseGetBackupDataL(TDriveNumber /* aDrive */)
       
   777 	{
       
   778 	// incremental backup not supported
       
   779 	__SQLLEAVE(KErrNotSupported);
       
   780 	}
       
   781 
       
   782 /** We don't support incremental backup
       
   783 */
       
   784 void CSqlBackupClient::InitialiseRestoreBaseDataL(TDriveNumber /* aDrive */)
       
   785 	{
       
   786 	// incremental backup not supported
       
   787 	__SQLLEAVE(KErrNotSupported);
       
   788 	}
       
   789 
       
   790 /** We don't support incremental backup
       
   791 */
       
   792 void CSqlBackupClient::InitialiseRestoreIncrementDataL(TDriveNumber /* aDrive */)
       
   793 	{
       
   794 	// incremental backup not supported
       
   795 	__SQLLEAVE(KErrNotSupported);
       
   796 	}
       
   797 
       
   798 /** We don't support incremental backup
       
   799 */
       
   800 void CSqlBackupClient::RestoreIncrementDataSectionL(TDesC8& /* aBuffer */, TBool /* aFinishedFlag */)
       
   801 	{
       
   802 	// incremental backup not supported
       
   803 	__SQLLEAVE(KErrNotSupported);
       
   804 	}
       
   805 
       
   806 /** We don't support incremental backup
       
   807 */
       
   808 void CSqlBackupClient::AllSnapshotsSuppliedL()
       
   809 	{
       
   810 	// incremental backup not supported
       
   811 	// cannot leave or panic!
       
   812 	}
       
   813 
       
   814 /** We don't support incremental backup
       
   815 */
       
   816 void CSqlBackupClient::ReceiveSnapshotDataL(TDriveNumber /* aDrive */, TDesC8& /* aBuffer */,
       
   817 									TBool /* aFinishedFlag */)
       
   818 	{
       
   819 	// incremental backup not supported
       
   820 	__SQLLEAVE(KErrNotSupported);
       
   821 	}
       
   822 
       
   823 /**
       
   824 	Get a list of database files that need to be backed up
       
   825 	This is decided by the SQL server on the basis of the UID provided
       
   826 	and whether the metadata in the database indicates that this data
       
   827 	should be backed up or not. The list of database files is populated
       
   828 	into the iFileList array.
       
   829 	@leave
       
   830 	@param TSecureSid the UID of the data owner
       
   831 */
       
   832 void CSqlBackupClient::GetBackupListL(TSecureId aSid)
       
   833 	{
       
   834 	// we own the array - the SQL server just populates it
       
   835 	iInterface->GetBackUpListL(aSid,iFileList);
       
   836 	}
       
   837 
       
   838 /** A simple checksumming algorithm to allow a degree
       
   839 	of trust that the backup and restore worked
       
   840 	This is visble externally because the test harness
       
   841 	needs to use it - NOTE the file pointer will be back at the
       
   842 	start when this function ends.
       
   843 	@leave
       
   844 	@param RFile64 an OPEN file to checksum
       
   845 */
       
   846 TUint64 CSqlBackupClient::CheckSumL(const RFile64& aOpenFile) const
       
   847 	{
       
   848 	// scoot through the database file building the checksum
       
   849 	TInt64 seekPos=0; // rewind first
       
   850 	__SQLLEAVE_IF_ERROR(aOpenFile.Seek(ESeekStart,seekPos));
       
   851 	TUint64 total=0;
       
   852 	const TUint KCheckSumBlockSize = 4 * 1024;
       
   853 	HBufC8* block=HBufC8::NewLC(KCheckSumBlockSize);
       
   854 	TPtr8 ptr=block->Des();
       
   855 	for(;;)
       
   856 		{
       
   857 		__SQLLEAVE_IF_ERROR(aOpenFile.Read(ptr));
       
   858 		TInt len=ptr.Length();
       
   859 		if(len==0)
       
   860 			{
       
   861 			break;
       
   862 			}
       
   863 		// calculate the checksum
       
   864 		for(TInt i=0;i<len;++i)
       
   865 			{
       
   866 			TUint64 carry=total&(0x8000000000000000ULL);
       
   867 			total<<=1;
       
   868 			if(carry)
       
   869 				{
       
   870 				total|=1;
       
   871 				}
       
   872 			TUint in=ptr[i];
       
   873  			total+=in;
       
   874  			}
       
   875 		};		
       
   876 	CleanupStack::PopAndDestroy(block);
       
   877 	// restore file position
       
   878 	seekPos=0;
       
   879 	__SQLLEAVE_IF_ERROR(aOpenFile.Seek(ESeekStart,seekPos));
       
   880 	return total;
       
   881 	}
       
   882 
       
   883 //Reads the content of aInBuf from position aInBufReadPos and stores the data into aOutBuf.
       
   884 //aDataLen is the length of the data. If the input buffer does not contain all the data, then only the
       
   885 //available data will be copied to the output buffer.
       
   886 //
       
   887 //Attention!!! This function won't work properly if aInBuf parameter contains odd number of bytes!!!
       
   888 //(a legacy problem, if it is a problem at all, because the B&R engine probably sends the data in chunks with even size)
       
   889 //
       
   890 void CSqlBackupClient::CopyBufData(const TDesC8& aInBuf, TInt& aInBufReadPos, TDes& aOutBuf, TInt aDataLen)
       
   891 	{
       
   892 	__SQLASSERT(aInBufReadPos >= 0, ESqlPanicBadArgument);
       
   893 	__SQLASSERT(aDataLen > 0, ESqlPanicBadArgument);
       
   894 	
       
   895 	TInt needed = (aDataLen - aOutBuf.Length()) << K8to16bitShift;
       
   896 	TInt available = aInBuf.Size() - aInBufReadPos;
       
   897 	TInt len = Min(needed, available);
       
   898 	TPtrC8 ptr8 = aInBuf.Mid(aInBufReadPos, len);
       
   899 	aInBufReadPos += len;
       
   900 	
       
   901 	len >>= K8to16bitShift;
       
   902 	aOutBuf.Append((const TUint16*)ptr8.Ptr(), len);
       
   903 	}