installationservices/swi/source/integrityservices/journal.cpp
changeset 0 ba25891c3a9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installationservices/swi/source/integrityservices/journal.cpp	Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,633 @@
+/*
+* Copyright (c) 2004-2009 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: 
+* CJournal implementation
+*
+*/
+
+
+/**
+ @file 
+ @released
+ @internalTechnology
+*/
+
+#include "journal.h"
+#include "journalfile.h"
+#include "operationfunctions.h"
+#include "log.h"
+#include <f32file.h>
+
+using namespace Swi;
+
+CJournal* CJournal::NewL(RFs& aFs, RLoader& aLoader, TInt64 aTransactionID, const TDesC& aPath)
+	{
+	CJournal* self = CJournal::NewLC(aFs, aLoader, aTransactionID, aPath);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CJournal* CJournal::NewLC(RFs& aFs, RLoader& aLoader, TInt64 aTransactionID, const TDesC& aPath)
+	{
+	CJournal* self = new(ELeave) CJournal(aFs, aLoader);
+	CleanupStack::PushL(self);
+	self->ConstructL(aTransactionID, aPath);
+	return self;
+	}
+
+CJournal::~CJournal()
+	{
+	iJournalDrives.Reset();
+	iCompletedDrives.Reset();
+	iAllDrives.Reset();
+	iJournalFiles.ResetAndDestroy();
+	}
+
+CJournal::CJournal(RFs& aFs, RLoader& aLoader) : iFs(aFs), iLoader(aLoader)
+	{
+	}
+
+void CJournal::ConstructL(TInt64 aTransactionID, const TDesC& aPath)
+	{
+	// construct the generic journal filename (does not include a drive)
+	iJournalFileName = aPath;
+	iJournalFileName.AppendNumUC(aTransactionID, EHex);
+	iJournalFileName.Append(KExtDelimiter);
+	iJournalFileName.Append(KJournalExt);
+	
+	// construct the filename for the drives journal (located on the system drive)
+	TDriveUnit systemDrive(RFs::GetSystemDrive());
+	iDriveArrayFileName = systemDrive.Name();
+	iDriveArrayFileName.Append(aPath);
+	iDriveArrayFileName.AppendNumUC(aTransactionID, EHex);
+	iDriveArrayFileName.Append(KExtDelimiter);
+	iDriveArrayFileName.Append(KDriveExt);
+	
+	InitJournalsL();
+	}
+	
+void CJournal::InitJournalsL()
+	{
+	RefreshDrivesArrayL();
+	
+	// attempt to read the journals from all drives
+	
+	TInt drivesCount(iAllDrives.Count());
+	RBuf journal;
+	journal.CreateL(KMaxFileName);
+	CleanupClosePushL(journal);
+	for (TInt i = 0; i < drivesCount; ++i)
+		{
+		TDriveUnit drive(iAllDrives[i]);
+		journal = drive.Name();
+		journal.Append(iJournalFileName);
+
+		// do not load completed drives
+		if (iCompletedDrives.Find(drive) != KErrNotFound)
+			{
+			continue;
+			}
+		
+		CJournalFile* journalFile = NULL;
+		TRAPD(err, journalFile = CJournalFile::NewL(iFs, iLoader, journal, iAllDrives[i]));
+		
+		// ignore error'd drives. These will either not be rolled back,
+		// or the user will find out later we can't write or commit to them.
+		
+		if (err == KErrNone)
+			{
+			CleanupStack::PushL(journalFile);
+			iJournalFiles.AppendL(journalFile);
+			CleanupStack::Pop(journalFile);
+			
+			iJournalDrives.AppendL(drive);
+			}
+		}
+	CleanupStack::PopAndDestroy(&journal);
+	}
+
+void CJournal::DeleteJournalFilesL()
+	{
+	// close the journal files in preparation for delete
+	iJournalFiles.ResetAndDestroy();
+	
+	// delete all journal files
+	for(TInt drive = 0; drive < iJournalDrives.Count(); drive++)
+		{
+		DeleteJournalFileL(iJournalDrives[drive]);
+		}
+	
+	// delete the drive array only after all journals have been completed
+	// (committed or rolled back)
+	if (iAllDrives.Count() == iCompletedDrives.Count())
+		{
+		DeleteDrivesFileL();
+		}
+	}
+	
+void CJournal::DeleteJournalFileL(TInt aDrive)
+	{
+	TDriveUnit journalDrive(aDrive);
+	RBuf journal;
+	journal.CreateL(journalDrive.Name(), KMaxFileName);
+	CleanupClosePushL(journal);
+	journal.Append(iJournalFileName);
+
+	DEBUG_PRINTF2(_L("Integrity Services - deleting journal %S"), &journal);
+	User::LeaveIfError(iLoader.Delete(journal));
+
+	// record that we have completed this drive
+	UpdateDrivesArrayL(aDrive);
+	iCompletedDrives.InsertInOrder(aDrive);
+	TInt err = iFs.RmDir(journal);
+	if(err != KErrNone && err != KErrInUse && err != KErrAccessDenied && 
+		err != KErrNotFound && err != KErrPathNotFound)
+		{
+		User::Leave(err);
+		}	
+	CleanupStack::PopAndDestroy(&journal);
+	}
+	
+void CJournal::DeleteDrivesFileL()
+	{
+	iJournalDrives.Reset();
+	iCompletedDrives.Reset();
+	TInt err = KErrNone;
+	
+	DEBUG_PRINTF2(_L("Integrity Services - deleting drive array %S"), &iDriveArrayFileName);
+	err = iLoader.Delete(iDriveArrayFileName);
+			
+	if(err != KErrNone && err != KErrPathNotFound && err != KErrNotFound)
+		{
+		User::Leave(err);
+		}
+    
+    // try removing the journal path
+	TParse directory;
+	User::LeaveIfError(directory.SetNoWild(iDriveArrayFileName, NULL, NULL));
+	while(!directory.IsRoot())
+		{
+		// try to remove this directory
+		TInt err = iFs.RmDir(directory.DriveAndPath());
+		if(err != KErrNone)
+			{
+			if(err == KErrInUse || err == KErrAccessDenied || 
+				err == KErrNotFound || err == KErrPathNotFound)
+				{
+				break;
+				}
+			else
+				{
+				User::Leave(err);
+				}
+			}
+		User::LeaveIfError(directory.PopDir());
+		}
+	}
+
+void CJournal::RefreshDrivesArrayL()
+	{
+	// clear existing journal drive arrays prior to reloading them from file
+	iCompletedDrives.Reset();
+	iAllDrives.Reset();
+	
+	RFileReadStream journalStream;
+	TInt err = journalStream.Open(iFs, iDriveArrayFileName, EFileStream);
+	if(err == KErrNone)
+		{
+		CleanupClosePushL(journalStream);
+		while(err == KErrNone)
+			{
+			// read the next journal entry
+			TInt drive = 0;
+			TRAP(err, drive = journalStream.ReadInt32L());
+			
+			// Checks boundaries for Drive
+			if( drive <  EDriveA || drive > EDriveZ )
+				{
+				User::Leave(KErrCorrupt);
+				}
+						
+			if(err == KErrNone)
+				{
+				if(iAllDrives.Find(drive) == KErrNotFound)
+					{
+					// first instance indicates this drive was part of this
+					// transaction
+					iAllDrives.InsertInOrder(drive);
+					}
+				else
+					{
+					// second instance indicates this drive has been committed
+					// or rolled back
+					iCompletedDrives.InsertInOrder(drive);
+					}
+				}
+			else if (err != KErrEof)
+				{
+				User::Leave(err);
+				}
+			}
+		CleanupStack::PopAndDestroy(&journalStream);
+		}
+	else if(err != KErrNotFound && err != KErrPathNotFound)
+		{
+		User::Leave(err);
+		}
+	}
+
+void CJournal::UpdateDrivesArrayL(TInt aDrive)
+	{
+	RFile file;
+	CleanupClosePushL(file);
+	// try opening the file if it already exists
+	TInt err = file.Open(iFs, iDriveArrayFileName, EFileWrite);
+	if (err != KErrNone)
+		{
+		if (err == KErrNotFound || err == KErrPathNotFound)
+			{
+			err = iFs.MkDirAll(iDriveArrayFileName);
+			if(err != KErrNone && err != KErrAlreadyExists)
+				{
+				User::Leave(err);
+				}
+			// journal does not exist, try creating one
+			User::LeaveIfError(file.Create(iFs, iDriveArrayFileName, EFileWrite));
+			}
+		else
+			{
+			User::Leave(err);
+			}
+		}
+	
+	TInt fileSize;
+	User::LeaveIfError(file.Size(fileSize));
+	
+	// attach to end of file for writing
+	RFileWriteStream stream;
+	stream.Attach(file, fileSize);	
+	CleanupStack::Pop(&file); // file ownership
+	CleanupClosePushL(stream);// transfered to stream
+	stream.WriteInt32L(aDrive);
+	CleanupStack::PopAndDestroy(&stream);
+	}
+
+void CJournal::StartCommitL()
+	{
+	// To commit, all drives must exist. No exceptions.
+	if (iJournalDrives.Count() != iAllDrives.Count())
+		{
+		User::Leave(KErrNotReady);
+		}
+		
+	// none of the drives must yet be completed...
+	if (iCompletedDrives.Count() != 0)
+		{
+		User::Leave(KErrNotSupported);
+		}
+		
+	// synch up all the drives
+	TInt drivesCount(iJournalDrives.Count());
+	for (TInt i = 0; i < drivesCount; ++i)
+		{
+ 		//Check if Journal drives are present.
+ 		TDriveInfo info;
+ 		if (iFs.Drive(info, iJournalDrives[i])!=KErrNone || info.iType==EMediaNotPresent)
+ 			{
+ 			User::Leave(KErrNotReady);	
+ 			}
+		iJournalFiles[i]->SynchL();
+		}		
+	}
+
+void CJournal::FinishCommitL()
+	{
+	DeleteJournalFilesL();
+	
+	// return the journal to a state where it can be used again,
+	// in the insane event that someone wants to...
+	// (I'm looking at you sisregistry)
+	iCompletedDrives.Reset();
+	iJournalDrives.Reset();
+	iAllDrives.Reset();
+	}
+
+void CJournal::StartRollbackL(TInt aDrive)
+	{
+	TInt index = iJournalDrives.Find(aDrive);
+	User::LeaveIfError(index);
+
+	// check there is a Journal file
+	if (index >= iJournalFiles.Count())
+		{
+		// This can happen when the journal file is missing due to OOM
+		// So handle this as if OOM
+		User::LeaveIfError( KErrNoMemory );
+		}
+
+	iJournalFiles[index]->SynchL();
+	}
+
+void CJournal::FinishRollbackL(TInt aDrive)
+	{
+	TInt index = iJournalDrives.Find(aDrive);
+	User::LeaveIfError(index);
+	iJournalFiles[index]->Close();
+	
+	DeleteJournalFileL(aDrive);
+	
+	if (iCompletedDrives.Count() == iAllDrives.Count())
+		{
+		DeleteDrivesFileL();
+		}
+	}
+
+void CJournal::SynchL()
+	{
+	TInt drivesCount(iAllDrives.Count());
+	RefreshDrivesArrayL();
+	if (drivesCount != iAllDrives.Count())
+		{
+		// another process has added a drive. Read the log created.
+		RBuf journal;
+		journal.CreateL(KMaxFileName);
+		CleanupClosePushL(journal);
+		for (TInt i = 0; i < iAllDrives.Count(); ++i)
+			{
+			TChar drive = iAllDrives[i];
+			if (iJournalDrives.Find(drive) == KErrNotFound &&
+				iCompletedDrives.Find(drive) == KErrNotFound)
+				{
+				User::LeaveIfError(iJournalDrives.InsertInOrder(drive));
+		
+				TInt index = iJournalDrives.FindInOrder(drive);
+				User::LeaveIfError(index);
+
+				TDriveUnit driveUnit(drive);
+				journal = driveUnit.Name();
+ 				journal.Append(iJournalFileName);
+
+				CJournalFile* journalFile = CJournalFile::NewLC(iFs, iLoader, journal, drive);
+				User::LeaveIfError(iJournalFiles.Insert(journalFile, index));
+				CleanupStack::Pop(journalFile);
+				}
+			}
+		CleanupStack::PopAndDestroy(&journal);
+		}
+	}
+
+TInt CJournal::PrepareToWriteL(TInt aDrive)
+	{
+	SynchL();
+	
+	TInt index(0);
+	TInt err = iJournalDrives.FindInOrder(aDrive);
+	if (err >= 0)
+		{
+		// already exists, ensure it is in synch.
+		index = err;
+		iJournalFiles[index]->SynchL();
+		}
+	else if (err == KErrNotFound)
+		{
+		// Save the current count of our array's in case something goes wrong
+  		TInt previousJournalDrivesCount = iJournalDrives.Count();
+  		TInt previousAllDrivesCount = iAllDrives.Count();
+  
+ 		// As there are multiple instances of calls which can leave
+		// we need to provide a TRAP block to enable us to perform
+ 		// proper cleanup before propagating the leave up the call stack
+  		TRAPD(err,
+   			{
+   			
+    		// create a new journal for this file.
+			User::LeaveIfError(iJournalDrives.InsertInOrder(aDrive));
+			User::LeaveIfError(iAllDrives.InsertInOrder(aDrive));
+		
+			index = iJournalDrives.FindInOrder(aDrive);
+			User::LeaveIfError(index);
+
+			UpdateDrivesArrayL(aDrive);
+		
+			TDriveUnit drive(aDrive);
+			RBuf journal;
+			journal.CreateL(drive.Name(), KMaxFileName);
+			CleanupClosePushL(journal);
+ 			journal.Append(iJournalFileName);
+
+			CJournalFile* journalFile = CJournalFile::NewLC(iFs, iLoader, journal, aDrive);
+			User::LeaveIfError(iJournalFiles.Insert(journalFile, index));
+			CleanupStack::Pop(journalFile);
+			CleanupStack::PopAndDestroy(&journal);
+
+   			}); // TRAPD 
+   			
+		// If an error occurred at all in the preceding block we need
+		// to ensure that the iJournalDrives and iJournalFiles arrays are
+		// in sync before we leave.
+		if( err != KErrNone )
+			{
+
+			// Check the iJournalDrives array first
+    		if( iJournalDrives.Count() != previousJournalDrivesCount )
+     			{
+     			// We added aDrive to the array before leaving
+     			index = iJournalDrives.FindInOrder( aDrive );
+     
+     			if( index >=0 && index < iJournalDrives.Count() )
+      				{
+      				iJournalDrives.Remove(index);
+      				}
+      
+     			// Now check the iAllDrives array
+     			if( iAllDrives.Count() != previousAllDrivesCount )
+      				{
+					index = iAllDrives.FindInOrder( aDrive );
+      
+					if( index >=0 && index < iAllDrives.Count() )
+       					{
+       					iAllDrives.Remove( index );
+       					}
+      				}
+     			}
+      
+    		// We can now leave as nature intended.  We don't need to do anything
+    		// with iJournalFiles as if this succeeded (and modified the array)
+    		// then we wouldn't have left in the first place.
+    		User::Leave( err );  	
+			}
+		}
+	else
+		{
+		User::Leave(err);
+		}
+	return index;
+	}
+	
+void CJournal::RestoreFilesL(TIntegrityServicesEvent aTypeFilter, CIntegrityServices& aIntegrityServices, CIntegrityServices::TFailType aFailType)
+	{
+	TInt journalsCount(iJournalFiles.Count());
+	for (TInt i = 0; i < journalsCount; ++i)
+		{
+		iJournalFiles[i]->JournalOperationL(IntegrityRestoreFileL, aTypeFilter, aIntegrityServices,
+			aFailType);
+		}
+	}
+
+void CJournal::DeleteFilesL(TIntegrityServicesEvent aTypeFilter, CIntegrityServices& aIntegrityServices, CIntegrityServices::TFailType aFailType)
+	{
+	TInt journalsCount(iJournalFiles.Count());
+	for (TInt i = 0; i < journalsCount; ++i)
+		{
+		iJournalFiles[i]->JournalOperationL(IntegrityDeleteFileL, aTypeFilter, aIntegrityServices,
+			aFailType);
+		}
+	}
+	
+void CJournal::RestoreFilesL(TIntegrityServicesEvent aTypeFilter, TInt aDrive,
+	CIntegrityServices& aIntegrityServices, CIntegrityServices::TFailType aFailType)
+	{
+	TInt index = iJournalDrives.Find(aDrive);
+	User::LeaveIfError(index);
+	iJournalFiles[index]->JournalOperationL(IntegrityRestoreFileL, aTypeFilter, aIntegrityServices,
+		aFailType);
+	}
+		
+void CJournal::DeleteFilesL(TIntegrityServicesEvent aTypeFilter, TInt aDrive,
+	CIntegrityServices& aIntegrityServices, CIntegrityServices::TFailType aFailType)
+	{
+	TInt index = iJournalDrives.Find(aDrive);
+	User::LeaveIfError(index);
+	iJournalFiles[index]->JournalOperationL(IntegrityDeleteFileL, aTypeFilter, aIntegrityServices,
+		aFailType);
+	}
+
+void CJournal::WriteJournalEventL(TIntegrityServicesEvent aEvent)
+	{
+	// write the event to each journal file
+	for(TInt index = 0; index < iJournalFiles.Count(); index++)
+		{
+		iJournalFiles[index]->EventL(aEvent);
+		}
+	}
+	
+void CJournal::WriteJournalEventL(TIntegrityServicesEvent aEvent, TInt aDrive)
+	{
+	TInt index = iJournalDrives.Find(aDrive);
+	User::LeaveIfError(index);
+	iJournalFiles[index]->EventL(aEvent);
+	}
+
+void CJournal::AddL(const TDesC& aFileName)
+	{
+	// write the filename to the journal on the same drive
+	TInt drive = CJournalFile::CheckFileNameL(iFs, aFileName);
+	TInt index = PrepareToWriteL(drive);
+	iJournalFiles[index]->AddL(aFileName);
+	}
+
+void CJournal::RemoveL(const TDesC& aFileName, TDes& backupFileName)
+	{
+	// write the filename to the journal on the same drive
+	TInt drive = CJournalFile::CheckFileNameL(iFs, aFileName);
+	TInt index = PrepareToWriteL(drive);
+	iJournalFiles[index]->RemoveL(aFileName, backupFileName);
+	}
+
+void CJournal::TemporaryL(const TDesC& aFileName)
+	{
+	// write the filename to the journal on the same drive
+	TInt drive = CJournalFile::CheckFileNameL(iFs, aFileName);
+	TInt index = PrepareToWriteL(drive);
+	iJournalFiles[index]->TemporaryL(aFileName);
+	}
+
+TIntegrityServicesEvent CJournal::LastEvent() const
+	{
+	TIntegrityServicesEvent lastEvent = ENone;
+	// work out the "real" last event
+	// journals states may be at different since they cannot all be written
+	// to simultaneously
+	for(TInt index = 0; index < iJournalFiles.Count(); index++)
+		{		
+		switch(iJournalFiles[index]->LastEvent())
+			{
+			case ENone:
+				break;
+				
+			case ERemovedFile:
+			case ETempFile:
+			case EAddedFile:
+				if(lastEvent != EInstallComplete)
+					{
+					lastEvent = iJournalFiles[index]->LastEvent();
+					}
+				break;
+			
+			case EInstallComplete:
+				if(lastEvent != EBackupFilesRemoved)
+					{
+					lastEvent = iJournalFiles[index]->LastEvent();
+					}
+				break;
+			
+			case EBackupFilesRemoved:
+				if(lastEvent != ETempFilesRemoved)
+					{
+					lastEvent = iJournalFiles[index]->LastEvent();
+					}
+				break;
+			
+			case EAddedFilesRemoved:
+				if(lastEvent != ERemovedFilesRestored)
+					{
+					lastEvent = iJournalFiles[index]->LastEvent();
+					}
+				break;
+			
+			case ETempFilesRemoved:
+				lastEvent = iJournalFiles[index]->LastEvent();
+				break;
+			
+			case ERemovedFilesRestored:
+				if(lastEvent != ETempFilesRemoved)
+					{
+					lastEvent = iJournalFiles[index]->LastEvent();
+					}
+				break;
+					
+			default:
+				User::Leave(KErrCorrupt);
+				break;
+   			}
+		}
+	return lastEvent;
+	}
+	
+TIntegrityServicesEvent CJournal::LastEventL(TInt aDrive) const
+	{
+	TInt index = iJournalDrives.Find(aDrive);
+	User::LeaveIfError(index);
+
+	// check there is a Journal file
+	if (index >= iJournalFiles.Count())
+		{
+		// This can happen when the journal file is missing due to OOM
+		// So handle this as if OOM
+		User::LeaveIfError( KErrNoMemory );
+		}
+
+	return iJournalFiles[index]->LastEvent();
+	}
+