installationservices/swtransactionservices/source/server/journal.cpp
changeset 24 84a16765cd86
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installationservices/swtransactionservices/source/server/journal.cpp	Fri Mar 19 09:33:35 2010 +0200
@@ -0,0 +1,646 @@
+/*
+* 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 "usiflog.h"
+#include <f32file.h>
+
+using namespace Usif;
+
+CJournal* CJournal::NewL(RFs& aFs, RLoader& aLoader, TStsTransactionId aTransactionID, const TDesC& aPath)
+	{
+	CJournal* self = CJournal::NewLC(aFs, aLoader, aTransactionID, aPath);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CJournal* CJournal::NewLC(RFs& aFs, RLoader& aLoader, TStsTransactionId aTransactionID, const TDesC& aPath)
+	{
+	CJournal* self = new(ELeave) CJournal(aFs, aLoader);
+	CleanupStack::PushL(self);
+	self->ConstructL(aTransactionID, aPath);
+	return self;
+	}
+
+CJournal::~CJournal()
+	{
+	iCompletedDrives.Reset();
+	iAllDrives.Reset();
+	iJournalFiles.ResetAndDestroy();
+	}
+
+CJournal::CJournal(RFs& aFs, RLoader& aLoader) : iFs(aFs), iLoader(aLoader)
+	{
+	}
+
+void CJournal::ConstructL(TStsTransactionId aTransactionID, const TDesC& aPath)
+	{
+	// construct the generic journal filename (does not include a drive)
+	CJournal::CreateJournalFileNameL(aTransactionID, aPath, iJournalFileName);
+	// construct the filename for the drives journal (located on the system drive)
+	CJournal::CreateDrvFileNameL(aTransactionID, aPath, iDriveArrayFileName);	
+	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);
+			}
+		}
+	CleanupStack::PopAndDestroy(&journal);
+	}
+
+TInt CJournal::FindJournalFileIndexL(TInt aDrive) const
+	{
+	for(TInt index = 0; index < iJournalFiles.Count(); ++index)
+		if (iJournalFiles[index]->Drive() == aDrive)
+			return index;
+		
+	return KErrNotFound;
+	}
+
+void CJournal::DeleteJournalFilesL()
+	{
+	// delete all journal files
+	for(TInt drive = 0; drive < iJournalFiles.Count(); drive++)
+		{		
+		DeleteJournalFileL(iJournalFiles[drive]->Drive());		
+		}	
+	iJournalFiles.ResetAndDestroy();
+	
+	// delete the drive array only after all journals have been completed
+	// (committed or rolled back)
+	if (iAllDrives.Count() == iCompletedDrives.Count())
+		{
+		DeleteDrivesFileL();
+		}
+	}
+
+void VerifyDirectoryDeletionErrorL(TInt err)
+	{
+	if(err != KErrNone && err != KErrNotFound && err != KErrPathNotFound && err != KErrInUse && err != KErrAccessDenied)
+		{
+		User::Leave(err);
+		}	
+	}
+
+
+void CJournal::DeleteJournalFileL(TInt aDrive, TBool aRecordAllRollbackEvents /* = ETrue */)
+	{	
+	TInt index = FindJournalFileIndexL(aDrive);
+	iJournalFiles[index]->Close();
+	TDriveUnit journalDrive(aDrive);
+	RBuf journal;
+	journal.CreateL(journalDrive.Name(), KMaxFileName);
+	CleanupClosePushL(journal);
+	journal.Append(iJournalFileName);
+
+	User::LeaveIfError(iFs.Delete(journal));
+
+	// record that we have completed this drive
+	if (aRecordAllRollbackEvents)
+		UpdateDrivesFileL(aDrive);
+		
+	iCompletedDrives.InsertInOrder(aDrive);
+	
+	VerifyDirectoryDeletionErrorL(iFs.RmDir(journal));
+	
+	CleanupStack::PopAndDestroy(&journal);
+	}
+	
+void CJournal::DeleteDrivesFileL()
+	{
+	iCompletedDrives.Reset();
+	TInt err = KErrNone;
+	
+	err = iFs.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)
+			{
+			VerifyDirectoryDeletionErrorL(err);
+			break;
+			}
+		User::LeaveIfError(directory.PopDir());
+		}
+	}
+
+// This function parses all the drives registered in the main drive file for this transaction.
+// This function also checks which drives have been completed.
+// As a result, two main drive sets: iCompletedDrives and iAllDrives are populated.
+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 == KErrNotFound || err == KErrPathNotFound)
+		return;
+	User::LeaveIfError(err);
+
+	CleanupClosePushL(journalStream);
+	while(ETrue)
+		{
+		// read the next entry in the drives' file
+		TInt drive = 0;
+		TRAP(err, drive = journalStream.ReadInt32L());
+		if (err == KErrEof)
+			break;
+		
+		User::LeaveIfError(err);
+
+		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);
+			}
+		}
+	CleanupStack::PopAndDestroy(&journalStream);
+	}
+
+void CJournal::UpdateDrivesFileL(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()
+	{
+	DEBUG_PRINTF3(_L("CJournal::StartCommitL() - iJournalFileName %S   iDriveArrayFileName %S"), &iJournalFileName, &iDriveArrayFileName);							
+	// To commit, all drives must exist. No exceptions.
+	if (iJournalFiles.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(iJournalFiles.Count());
+	for (TInt i = 0; i < drivesCount; ++i)
+		{
+ 		//Check if Journal drives are present.
+ 		TDriveInfo info;
+ 		if (iFs.Drive(info, iJournalFiles[i]->Drive())!=KErrNone || info.iType==EMediaNotPresent)
+ 			{
+ 			User::Leave(KErrNotReady);	
+ 			}
+		}		
+	}
+
+void CJournal::FinishCommitL()
+	{
+	DEBUG_PRINTF3(_L("CJournal::FinishCommitL() - iJournalFileName %S   iDriveArrayFileName %S"), &iJournalFileName, &iDriveArrayFileName);							
+	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();
+	iAllDrives.Reset();
+	}
+
+void CJournal::FinishRollbackL(TInt aDrive, TBool aRecordAllRollbackEvents /* = ETrue */)
+	{
+	DEBUG_PRINTF4(_L("CJournal::FinishRollbackL() - iJournalFileName %S   iDriveArrayFileName %S   aDrive %d"), &iJournalFileName, &iDriveArrayFileName, aDrive);
+	DeleteJournalFileL(aDrive, aRecordAllRollbackEvents);
+	
+	if (iCompletedDrives.Count() == iAllDrives.Count())
+		{
+		DeleteDrivesFileL();
+		}
+	}
+
+// This function verifies whether a journal exists for this drive in this transaction.
+// If it doesn't, then the journal file is created and added to iJournalFiles
+TInt CJournal::PrepareToWriteL(TInt aDrive)
+	{	
+	TInt index = FindJournalFileIndexL(aDrive);
+	if (index >= 0) // The journal already exists
+		{
+		return index;
+		}
+	
+	__ASSERT_ALWAYS(index == KErrNotFound, User::Invariant());
+	// The journal does not exist - we need to create one
+	
+	TDriveUnit drive(aDrive);
+	RBuf journalPath;
+	journalPath.CreateL(drive.Name(), KMaxFileName);
+	CleanupClosePushL(journalPath);
+	journalPath.Append(iJournalFileName);
+
+	CJournalFile* journalFile = CJournalFile::NewLC(iFs, iLoader, journalPath, aDrive);
+	iJournalFiles.AppendL(journalFile);
+	CleanupStack::Pop(journalFile);
+	CleanupStack::PopAndDestroy(&journalPath);
+	
+	UpdateDrivesFileL(aDrive);
+	
+	User::LeaveIfError(iAllDrives.InsertInOrder(aDrive));
+
+	return iJournalFiles.Count() - 1; // Since we appended the entry, we return the last index
+	}
+	
+void CJournal::DeleteFilesL(TIntegrityServicesEvent aTypeFilter)
+	{
+	TInt journalsCount(iJournalFiles.Count());
+	for (TInt i = 0; i < journalsCount; ++i)
+		{
+		iJournalFiles[i]->JournalOperationL(IntegrityDeleteFileL, aTypeFilter, CIntegrityServices::EFailDeletingFile);
+		}
+	}
+	
+void CJournal::RestoreFilesL(TInt aDrive)
+	{
+	TInt index = FindJournalFileIndexL(aDrive);
+	User::LeaveIfError(index);
+	iJournalFiles[index]->JournalOperationL(IntegrityRestoreFileL, ERemovedFile, CIntegrityServices::EFailRestoringFile);
+	}
+		
+void CJournal::DeleteFilesL(TIntegrityServicesEvent aTypeFilter, TInt aDrive)
+	{
+	TInt index = FindJournalFileIndexL(aDrive);
+	User::LeaveIfError(index);
+	iJournalFiles[index]->JournalOperationL(IntegrityDeleteFileL, aTypeFilter, CIntegrityServices::EFailDeletingFile);
+	}
+
+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, TBool aSerializeEventToJournal)
+	{
+	TInt index = FindJournalFileIndexL(aDrive);
+	User::LeaveIfError(index);
+	iJournalFiles[index]->EventL(aEvent, aSerializeEventToJournal);
+	}
+
+void CJournal::AddL(const TDesC& aFileName)
+	{
+	DEBUG_PRINTF2(_L("CJournal::AddL() - aFileName %S"), &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)
+	{
+	DEBUG_PRINTF2(_L("CJournal::RemoveL() - aFileName %S"), &aFileName);							
+	// 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)
+	{
+	DEBUG_PRINTF2(_L("CJournal::TemporaryL() - aFileName %S"), &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::LastEventL() 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++)
+		{
+		TInt position = iCompletedDrives.Find(iJournalFiles[index]->Drive());
+		
+		if(position!=KErrNotFound) 
+			continue; //don't check completed drives
+		
+		TIntegrityServicesEvent currentJournalLastEvent = iJournalFiles[index]->LastEvent();
+		if (currentJournalLastEvent >= lastEvent)
+			lastEvent = currentJournalLastEvent;
+		}//for
+	return lastEvent;
+	}
+	
+TIntegrityServicesEvent CJournal::LastEventL(TInt aDrive) const
+	{
+	DEBUG_PRINTF2(_L("CJournal::LastEventL() - aDrive %d"), aDrive);							
+	TInt index = FindJournalFileIndexL(aDrive);
+	User::LeaveIfError(index);
+
+	return iJournalFiles[index]->LastEvent();
+	}
+
+/*static*/ void CJournal::CreateDrvFileNameL(TStsTransactionId aTransactionID, const TDesC& aPath, TDes& aDrvFileName)
+	{
+	DEBUG_PRINTF3(_L("CJournal::CreateDrvFileNameL() - aTransactionID %X aPath %S"), aTransactionID, &aPath);							
+	aDrvFileName = TDriveUnit(::RFs::GetSystemDrive()).Name();
+	aDrvFileName.Append(aPath);
+	aDrvFileName.AppendNumUC(static_cast<TUint32>(aTransactionID), EHex);
+	aDrvFileName.Append(KExtDelimiter);
+	aDrvFileName.Append(KDriveExt);
+	}
+
+/*static*/ void CJournal::CreateJournalFileNameL(TStsTransactionId aTransactionID, const TDesC& aPath, TDes& aJournalFileName)
+	{
+	aJournalFileName.Append(aPath);
+	aJournalFileName.AppendNumUC(static_cast<TUint32>(aTransactionID), EHex);
+	aJournalFileName.Append(KExtDelimiter);
+	aJournalFileName.Append(KJournalExt);
+	}
+
+/*static*/ TInt CJournal::RecoverTransactionIdFromDrvFileName(const TDesC& aDrvFileName, TStsTransactionId& aTransactionID)
+	{
+	TLex lex(aDrvFileName);
+	TUint32 tmp;
+	TInt result = lex.Val(tmp, EHex);
+	aTransactionID = static_cast<TStsTransactionId>(tmp);
+	return result;
+	}
+
+void CJournal::RollBackDriveL(TInt aDrive, TBool aRecordAllRollbackEvents)
+	{
+	switch (LastEventL())
+		{
+		// Transaction did not complete, rollback required
+		case ERemovedFile:
+		case EBackupFile:
+		case ETempFile:
+		case EAddedFile:
+		case EAddedFilesRemoved:
+		case ERemovedFilesRestored:
+			// rollback this individual journal from where it last got to.
+			switch (LastEventL(aDrive))
+				{
+			case ERemovedFile:
+			case EBackupFile:
+			case ETempFile:
+			case EAddedFile:
+				DeleteFilesL(EAddedFile, aDrive);
+				
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailNewFilesRemoved, CIntegrityServices::EBeforeJournal, KNullDesC);				
+				WriteJournalEventL(EAddedFilesRemoved, aDrive, aRecordAllRollbackEvents);
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailNewFilesRemoved, CIntegrityServices::EAfterJournal, KNullDesC);
+				
+			// fall-through - automatically proceed to the next state, we'll start from EAddedFilesRemoved in case the RestoreFileL has failed
+			case EAddedFilesRemoved:
+				RestoreFilesL(aDrive);				
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailOldFilesRestored, CIntegrityServices::EBeforeJournal, KNullDesC);			
+				WriteJournalEventL(ERemovedFilesRestored, aDrive, aRecordAllRollbackEvents);
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailOldFilesRestored, CIntegrityServices::EAfterJournal, KNullDesC);
+
+			// fall-through - automatically proceed to the next state
+			case ERemovedFilesRestored:				
+				DeleteFilesL(ETempFile, aDrive);
+				
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailTempFilesRemoved, CIntegrityServices::EBeforeJournal, KNullDesC);				
+				WriteJournalEventL(ETempFilesRemoved, aDrive, aRecordAllRollbackEvents);
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailTempFilesRemoved, CIntegrityServices::EAfterJournal, KNullDesC);
+				break;
+				
+			case ETempFilesRemoved:
+				break;
+		
+			// nothing was done, just delete the journal file
+			case ENone:
+				break;
+		
+			// Erk! Bad state, bad state!
+			default:
+				User::Leave(KErrCorrupt);
+				break;
+				}
+			break;
+			
+		// Transaction complete, just need to remove the backup
+		case ECommitted:
+		case EBackupFilesRemoved:
+			switch (LastEventL(aDrive))
+				{
+			// At least one journal had a complete transaction...
+			// roll forwards all journal files.
+			case ERemovedFile:
+			case EBackupFile:
+			case ETempFile:
+			case EAddedFile:
+			case ECommitted:
+				DeleteFilesL(EBackupFile, aDrive);
+			
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailBackupFilesRemoved, CIntegrityServices::EBeforeJournal, KNullDesC);			
+				WriteJournalEventL(EBackupFilesRemoved, aDrive, aRecordAllRollbackEvents);
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailBackupFilesRemoved, CIntegrityServices::EAfterJournal, KNullDesC);
+
+				//fall-through - automatically proceed to the next state
+			case EBackupFilesRemoved:				
+				DeleteFilesL(ETempFile, aDrive);
+			
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailTempFilesRemoved, CIntegrityServices::EBeforeJournal, KNullDesC);
+				WriteJournalEventL(ETempFilesRemoved, aDrive, aRecordAllRollbackEvents);
+				CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailTempFilesRemoved, CIntegrityServices::EAfterJournal, KNullDesC);
+				break;
+							
+			case ETempFilesRemoved:
+				break;
+		
+			// nothing was done, just delete the journal file
+			case ENone:
+				break;
+		
+			// unknown state	
+			default:
+				User::Leave(KErrCorrupt);
+				break;
+				}
+			break;
+			
+		case ETempFilesRemoved:
+			break;
+		
+		// nothing was done, just delete the journal file
+		case ENone:
+			break;
+		
+		// unknown state	
+		default:
+			User::Leave(KErrCorrupt);
+			break;
+		}
+		
+	FinishRollbackL(aDrive, aRecordAllRollbackEvents);
+	}
+
+void CJournal::CommitL()
+	{
+	StartCommitL();
+	
+	CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailInstallComplete, CIntegrityServices::EBeforeJournal, KNullDesC);
+	WriteJournalEventL(ECommitted);
+	CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailInstallComplete, CIntegrityServices::EAfterJournal, KNullDesC);
+	
+	DeleteFilesL(EBackupFile);
+	
+	CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailBackupFilesRemoved, CIntegrityServices::EBeforeJournal, KNullDesC);
+	WriteJournalEventL(EBackupFilesRemoved);
+	CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailBackupFilesRemoved, CIntegrityServices::EAfterJournal, KNullDesC);
+	
+	DeleteFilesL(ETempFile);
+	
+	CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailTempFilesRemoved, CIntegrityServices::EBeforeJournal, KNullDesC);
+	WriteJournalEventL(ETempFilesRemoved);
+	CIntegrityServices::SimulatePowerFailureL(CIntegrityServices::EFailTempFilesRemoved, CIntegrityServices::EAfterJournal, KNullDesC);
+	
+	FinishCommitL();
+	}
+
+void CJournal::RollBackL(TBool aRecordAllRollbackEvents /*= ETrue*/)
+	{
+#ifdef __WINSCW__
+	// For 2 minutes after initial boot, DLLs are not unloaded. If we are doing a
+	// rollback, we need to make sure any pending unloadeds are actioned, otherwise a
+	// previously loaded DLL could cause the rollback to fail on windows (on arm it is legal to
+	// delete a loaded DLL/EXE, whilst on windows it is not).
+	RLoader loader;
+	TInt r = loader.Connect();
+	if(r == KErrNone)
+		{
+		(void)loader.CancelLazyDllUnload();
+		loader.Close();
+		}
+#endif
+	
+	for(TInt index = 0; index < iJournalFiles.Count(); index++)
+		{
+		TDriveUnit drive = iJournalFiles[index]->Drive();
+		// check to see if this drive has already been completed
+		if(iCompletedDrives.Find(drive) != KErrNotFound)
+			continue;
+			
+		// only attempt to recover writeable drives that are present
+		TDriveInfo info;
+		User::LeaveIfError(iFs.Drive(info, drive));
+		if (info.iMediaAtt & KMediaAttWriteProtected || info.iType==EMediaNotPresent)
+			continue;
+
+		TRAPD(err, RollBackDriveL(drive, aRecordAllRollbackEvents));
+		if(err != KErrNone && err != KErrPathNotFound && err != KErrNotFound
+			&& err != KErrNotReady)
+			{
+			// unexpected error
+			User::Leave(err);
+			}
+		}
+	}