installationservices/swtransactionservices/source/server/integrityservices.cpp
branchRCL_3
changeset 65 7333d7932ef7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installationservices/swtransactionservices/source/server/integrityservices.cpp	Tue Aug 31 15:21:33 2010 +0300
@@ -0,0 +1,381 @@
+/*
+* Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+* CIntegrityServices implementation
+*
+*/
+
+
+/**
+ @file 
+ @released
+ @internalTechnology
+*/
+
+#include "integrityservices.h"
+#include "journal.h"
+#include "operationfunctions.h"
+#include "usiflog.h"
+
+#include <f32file.h>
+
+using namespace Usif;
+
+_LIT(KTransactionPath, "\\sys\\install\\integrityservices\\");
+
+CIntegrityServices::TFailType CIntegrityServices::iFailType = EFailNone;
+CIntegrityServices::TFailPosition CIntegrityServices::iFailPosition = EBeforeJournal;
+TFileName CIntegrityServices::iFailFileName;
+TBool CIntegrityServices::iIsFailureTestingEnabled = EFalse;
+
+
+/**
+ * This is a trivial C class that just encapsulates a TEntryArray object in order to 
+ * facilitate its storage on the heap.
+ *
+ * @released
+ * @internalComponent 
+ */
+class CEntryArray : public CBase
+	{
+public:
+	inline TEntryArray& operator()();
+
+private:
+	/**
+	 * Container to hold file entries
+	 */
+	TEntryArray iEntryArray;
+	};
+
+inline TEntryArray& CEntryArray::operator()()
+	{
+	return iEntryArray;
+	}
+
+ CIntegrityServices* CIntegrityServices::NewL(TStsTransactionId aTransactionID)
+	{
+	CIntegrityServices* self = CIntegrityServices::NewLC(aTransactionID);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+ CIntegrityServices* CIntegrityServices::NewLC(TStsTransactionId aTransactionID)
+	{
+	CIntegrityServices* self = new(ELeave) CIntegrityServices(aTransactionID);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+ CIntegrityServices::CIntegrityServices(TStsTransactionId aTransactionID) : iTransactionID(aTransactionID)
+	{
+	}
+
+ CIntegrityServices::~CIntegrityServices()
+	{
+	delete iJournal;
+	iFs.Close();
+	
+	iLoader.Close();
+	}
+
+ void CIntegrityServices::ConstructL()
+	{
+	DEBUG_PRINTF2(_L("CIntegrityServices::ConstructL() - Opening session with  Session ID %X."), iTransactionID);
+	
+	User::LeaveIfError(iFs.Connect());
+	User::LeaveIfError(iFs.ShareProtected()); //needed as new RFiles are to be passed back to client's side
+	User::LeaveIfError(iLoader.Connect());
+
+	// store the journal path and create the journal
+	TParsePtrC pathPtr(KTransactionPath);
+	iJournalPath = pathPtr.Path();
+	iJournal = CJournal::NewL(iFs, iLoader, iTransactionID, iJournalPath);
+	iSystemDrive = ::RFs::GetSystemDrive();
+	}
+ 
+const TInt KIntegrityServicesSimulatedBatteryFailure=-10205; 
+ 
+/*static*/ void CIntegrityServices::SimulatePowerFailureL(TFailType aFailType, TFailPosition aFailPosition, const TDesC& aFailFileName)
+	{
+	if (!iIsFailureTestingEnabled)
+		return;
+	
+	if(iFailType == aFailType && iFailPosition == aFailPosition && iFailFileName == aFailFileName)
+		{
+		User::Leave(KIntegrityServicesSimulatedBatteryFailure);
+		}
+	}
+
+/*static*/ void CIntegrityServices::NormalizeDirectoryName(TDes& aFileName)
+{
+	// Directories are represented in the integrity tree and integrity journal exactly as files,
+	// without the trailing slash 
+	TInt lastCharPos = aFileName.Length() - 1;
+	if ( lastCharPos >= 0 && aFileName[lastCharPos] == KPathDelimiter &&
+		 aFileName.Locate(KPathDelimiter) != lastCharPos) // Take care not to remove slash from "c:\" and the like
+		{
+		aFileName.Delete(lastCharPos, 1);
+		}			
+}
+
+ void CIntegrityServices::RegisterNewL(const TDesC& aFileName)
+	{
+	DEBUG_PRINTF3(_L("CIntegrityServices::RegisterNewL() - Session %X, File: %S."),	iTransactionID, &aFileName);
+
+	HBufC* localFilenameHeap = aFileName.AllocLC();
+	TPtr localFilename = localFilenameHeap->Des();
+	NormalizeDirectoryName(localFilename); // If it is a directory name, make sure to normalize it
+		
+	// Record the added file or directory in the journal
+	SimulatePowerFailureL(EFailAddingNewFile, EBeforeJournal, aFileName);
+	iJournal->AddL(localFilename);
+	SimulatePowerFailureL(EFailAddingNewFile, EAfterJournal, aFileName);
+	CleanupStack::PopAndDestroy(localFilenameHeap);
+	}
+
+ void VerifyMkDirErrorL(TInt err)
+	{
+	if(err != KErrNone && err != KErrAlreadyExists)
+		{
+		User::Leave(err);
+		}	
+	}
+ 
+ void ProcessNewFileRegistrationResultL(TInt err, RFs& aFs, const TDesC& aFileName, RFile& aFile)
+	{
+	if(err!= KErrNone)
+		{
+		//if we hit this point it means we successfully created the new file however registering with the transaction has  failed
+		//so we have to remove the new file to make the journal and the file system consistent
+		aFile.Close();
+		aFs.Delete(aFileName);
+		User::Leave(err);
+		}	
+	}
+
+ void CIntegrityServices::CreateNewL(const TDesC& aFileName, RFile &newFile, TUint aFileMode)
+	{
+	DEBUG_PRINTF3(_L("CIntegrityServices::CreateNewL() - Session %X, File: %S."),	iTransactionID, &aFileName);
+	TInt err = iFs.MkDirAll(aFileName); //first we have to create the full directory path to aFileName otherwise RFs::Create will fail
+	VerifyMkDirErrorL(err);
+	User::LeaveIfError(newFile.Create(iFs, aFileName, aFileMode));
+	TRAPD(regResult, RegisterNewL(aFileName));	
+	ProcessNewFileRegistrationResultL(regResult, iFs, aFileName, newFile); //checks if the registration failed and cleans up the file in the filesystem if it did
+	}
+
+ void CIntegrityServices::RemoveL(const TDesC& aFileName)
+	{
+	DEBUG_PRINTF3(_L("CIntegrityServices::RemoveL() - Session %X, File: %S."), iTransactionID, &aFileName);
+
+	// before doing anything check that the file or directory exists
+	TEntry entry;
+	
+	TInt res = iFs.Entry(aFileName, entry);
+	if (res == KErrNotFound || res == KErrPathNotFound)
+		return; // If the file is not present, do nothing. Returning an error would require the user of the API to do additional checks
+	User::LeaveIfError(res);
+
+	// We might need to grow this buffer by one byte later
+	HBufC* localFilenameHeap = HBufC::NewLC(aFileName.Length() + 1);
+	TPtr localFilename = localFilenameHeap->Des();
+	localFilename.Copy(aFileName);
+	
+	TBool isFilenameDir = entry.IsDir();
+	// The "if" below is not functionally necessary, but it is a slight optimization - 
+	// so that we won't attempt to normalize directory name on files. The optimization is not
+	// done in AddL or NormalizeDirectoryName itself, since we don't have future use for TEntry there, and the cost for RFs::Entry overweighs the one for NormalizeDirectoryName
+	if ( isFilenameDir ) 
+		{
+		NormalizeDirectoryName(localFilename);
+		}
+
+	RBuf backupFileName;
+	backupFileName.CreateL(KMaxFileName);
+	CleanupClosePushL(backupFileName);
+	SimulatePowerFailureL(EFailRemovingFile, EBeforeJournal, aFileName);
+	iJournal->RemoveL(localFilename, backupFileName);
+		
+	if (backupFileName.Length())
+		{
+		SimulatePowerFailureL(EFailRemovingFile, EAfterJournal, aFileName);
+
+		TInt err = iFs.MkDirAll(backupFileName);
+		VerifyMkDirErrorL(err);
+
+		SimulatePowerFailureL(EFailRemovingFile, EBeforeAction, aFileName);
+			err = iFs.Rename(localFilename, backupFileName);
+			DEBUG_PRINTF4(_L("CIntegrityServices::RemoveL() - Renamed %S as %S error %d"), &localFilename, &backupFileName, err);
+			User::LeaveIfError(err);
+		SimulatePowerFailureL(EFailRemovingFile, EAfterAction, aFileName);
+		}
+	else
+		{
+		DEBUG_PRINTF2(_L("CIntegrityServices::RemoveL() - %S already backed up"), &aFileName);
+		SimulatePowerFailureL(EFailRemovingFile, EBeforeAction, aFileName);
+		// If backupFileName is zero-length, the file was added earlier
+		// in the same journal and doesn't need to be backed up.
+		if (isFilenameDir)
+			{
+			CFileMan* fileman = CFileMan::NewL(iFs);
+			CleanupStack::PushL(fileman);			
+			// Make sure to append slash before calling RmDir - otherwise it deletes the parent directory
+			if (localFilename[localFilename.Length()-1] != KPathDelimiter)
+	  			{
+  				localFilename.Append(KPathDelimiter);
+  				}			
+			User::LeaveIfError(fileman->RmDir(localFilename)); // A directory cannot be a paged exec., so we don't have to use iLoader
+			CleanupStack::PopAndDestroy(fileman);
+			}
+		else
+			{
+			User::LeaveIfError(iLoader.Delete(aFileName));
+			}
+		SimulatePowerFailureL(EFailRemovingFile, EAfterAction, aFileName);			
+		}
+
+	// Don't leave an empty directory structure, try pruning it
+	RemoveDirectoryTreeL(iFs, aFileName);
+
+	CleanupStack::PopAndDestroy(2, localFilenameHeap); // backupFileName
+	}
+
+ void CIntegrityServices::RegisterTemporaryL(const TDesC& aFileName)
+	{
+	DEBUG_PRINTF3(_L("CIntegrityServices::RegisterTemporaryL() - Session %X, File: %S."), iTransactionID, &aFileName);
+
+	// record the temporary file or directory in the journal
+	SimulatePowerFailureL(EFailAddingTempFile, EBeforeJournal, aFileName);
+	iJournal->TemporaryL(aFileName);
+	SimulatePowerFailureL(EFailAddingTempFile, EAfterJournal, aFileName);
+	}
+
+ void CIntegrityServices::CreateTemporaryL(const TDesC& aFileName, RFile &newFile, TUint aFileMode)
+	{
+	DEBUG_PRINTF3(_L("CIntegrityServices::CreateTemporaryL() - Session %X, File: %S."), iTransactionID, &aFileName);
+
+	TInt err = iFs.MkDirAll(aFileName); //first we have to create the full directory path to aFileName otherwise RFs::Create will fail
+	VerifyMkDirErrorL(err);
+	User::LeaveIfError(newFile.Create(iFs, aFileName, aFileMode));
+	TRAPD(regResult, RegisterTemporaryL(aFileName));
+	ProcessNewFileRegistrationResultL(regResult, iFs, aFileName, newFile);
+	}
+
+ void CIntegrityServices::OverwriteL(const TDesC& aFileName, RFile &newFile, TUint aFileMode)
+	{
+	DEBUG_PRINTF3(_L("CIntegrityServices::OverwriteL() - Session %X, File: %S."), iTransactionID, &aFileName);
+
+	TBool b;
+	TInt err;
+	if((err=iFs.IsFileOpen(aFileName, b))== KErrNone) //returned error code shows whether the file exists or not; the bool value is ignored
+		{
+		//file exists remove first
+		RemoveL(aFileName);
+		}
+	else
+		{
+		if(err != KErrNotFound)
+			{
+			User::Leave(err);
+			}
+		}
+	CreateNewL(aFileName, newFile, aFileMode);
+	}
+
+ void CIntegrityServices::CommitL()
+	{
+	DEBUG_PRINTF2(_L("CIntegrityServices::CommitL() - Session %X."), iTransactionID);	
+	iJournal->CommitL();
+	}
+
+ void CIntegrityServices::RollBackL(TBool aRecordAllRollbackEvents /* = ETrue */)
+	{
+	DEBUG_PRINTF2(_L("CIntegrityServices::RollBackL() - transaction %X"), iTransactionID);
+	iJournal->RollBackL(aRecordAllRollbackEvents);
+	}
+
+/*static*/ void CIntegrityServices::GetListOfTransactionsL(RArray<TStsTransactionId>& aIdArray)
+	{
+	RFs fs;
+	User::LeaveIfError(fs.Connect());
+	CleanupClosePushL(fs);
+	TDriveUnit systemDrive(::RFs::GetSystemDrive());
+	RBuf fileSpec;
+	fileSpec.CreateL(systemDrive.Name(), KMaxFileName);
+	CleanupClosePushL(fileSpec);
+	fileSpec.Append(KTransactionPath);
+	fileSpec.Append(KMatchAny);
+	fileSpec.Append(KExtDelimiter);
+	fileSpec.Append(KDriveExt);
+
+	RDir dir;
+	TInt err = dir.Open(fs, fileSpec, KEntryAttNormal);
+	CleanupStack::PopAndDestroy(&fileSpec);
+	if (err == KErrPathNotFound || err == KErrNotFound)
+		{
+		CleanupStack::PopAndDestroy(&fs);
+		return; // These errors are not considered fatal - there may be no journals present
+		}
+	User::LeaveIfError(err);
+	CleanupClosePushL(dir);
+	
+	CEntryArray* entryArrayContainer = new (ELeave) CEntryArray;
+	TEntryArray& entryArray = (*entryArrayContainer)();
+	err = dir.Read(entryArray);
+	CleanupStack::PopAndDestroy(&dir);
+	CleanupStack::PushL(entryArrayContainer);	
+	if (err != KErrNone && err != KErrEof)
+		{
+		User::Leave(err);
+		}
+	TInt entryCount(entryArray.Count());
+	for (TInt index = 0; index < entryCount; ++index)
+		{
+		TStsTransactionId transactionID;
+		if (CJournal::RecoverTransactionIdFromDrvFileName(
+				entryArray[index].iName, transactionID) == KErrNone)
+			{
+			aIdArray.AppendL(transactionID);
+			}
+		}
+	CleanupStack::PopAndDestroy(entryArrayContainer);
+	CleanupStack::PopAndDestroy(&fs);
+	}
+
+/*static*/ void CIntegrityServices::RollbackAllL()
+	{
+	RArray<TStsTransactionId> transactionIDs;
+	CleanupClosePushL(transactionIDs);
+	CIntegrityServices::GetListOfTransactionsL(transactionIDs);
+	TInt numberOfTransactions(transactionIDs.Count());
+	TInt lastError=KErrNone;
+	DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() %d transactions have been found."), numberOfTransactions );							
+	for(TInt i=0; i<numberOfTransactions; ++i)
+		{
+		DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() Trying to roll back transaction %X"), transactionIDs[i]);
+		TRAPD(err,
+			CIntegrityServices* transactionPtr = CIntegrityServices::NewLC(transactionIDs[i]);
+			transactionPtr->RollBackL(EFalse); // Specify not to record roll back events. If we failed in the middle of a previous roll back, due to lack of resources we shouldn't be trying to record more events
+			CleanupStack::PopAndDestroy(transactionPtr);
+			); //failing to roll back one transaction shouldn't affect the overall rollback all procedure
+		if(err!=KErrNone)
+			{
+			lastError=err; //remember last error and leave with that error code indicating an error in the overall procedure
+			}
+		DEBUG_PRINTF2(_L("CIntegrityServices::RollbackAllL() Rolled back transaction %X"), transactionIDs[i]);			
+		}
+	CleanupStack::PopAndDestroy(&transactionIDs);
+	User::LeaveIfError(lastError);
+	}