installationservices/swi/source/integrityservices/journalfile.cpp
changeset 0 ba25891c3a9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installationservices/swi/source/integrityservices/journalfile.cpp	Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,425 @@
+/*
+* 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: 
+* CJournalFile implementation
+*
+*/
+
+
+/**
+ @file 
+ @released
+ @internalTechnology
+*/
+
+#include "journalfile.h"
+
+_LIT(KBackupExt, "bak"); // extension for removed files in backup location
+
+using namespace Swi;
+
+CJournalFile* CJournalFile::NewL(RFs& aFs, RLoader& aLoader, const TDesC& aFileName, TInt aDrive)
+	{
+	CJournalFile* self = CJournalFile::NewLC(aFs, aLoader, aFileName, aDrive);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CJournalFile* CJournalFile::NewLC(RFs& aFs, RLoader& aLoader, const TDesC& aFileName, TInt aDrive)
+	{
+	CJournalFile* self = new(ELeave) CJournalFile(aFs, aLoader, aDrive);
+	CleanupStack::PushL(self);
+	self->ConstructL(aFileName);
+	return self;
+	}
+
+CJournalFile::CJournalFile(RFs& aFs, RLoader& aLoader, TInt aDrive) 
+	: iFs(aFs), iLoader(aLoader), iLastEvent(ENone), iDrive(aDrive)
+	{
+	}
+
+CJournalFile::~CJournalFile()
+	{
+	iWriteStream.Close();
+	delete iTreeRoot;
+	}
+
+void CJournalFile::ConstructL(const TDesC& aFileName)
+	{
+	iJournalFileName = aFileName;
+	
+	TChar drive;
+	User::LeaveIfError(iFs.DriveToChar(iDrive, drive));
+	
+	TBuf<2> driveSpec;
+	driveSpec.Append(drive);
+	driveSpec.Append(KDriveDelimiter);
+	iTreeRoot = CIntegrityTreeNode::NewL(driveSpec);
+	
+	// read any existing entries from this journal
+	TRAPD(err, ReadL());
+	if(err != KErrNone)
+		{
+		// if necessary create the path
+		if(err == KErrPathNotFound)
+			{
+			err = iFs.MkDirAll(iJournalFileName);
+			if(err != KErrNone && err != KErrAlreadyExists)
+				{
+				User::Leave(err);
+				}
+			}
+		else if (err != KErrNotFound)
+			{
+			User::Leave(err);
+			}
+		}
+	// now prepare for writing
+	PrepareToWriteL();
+	}
+
+void CJournalFile::ReadL()
+	{
+	RFileReadStream journalStream;
+	User::LeaveIfError(journalStream.Open(iFs, iJournalFileName, EFileWrite | EFileShareAny));
+	CleanupClosePushL(journalStream);
+	
+	// we need to check that any filenames read from the journal are on the
+	// same drive as the journal itself
+	TDriveUnit journalDrive(iJournalFileName);
+	
+	TInt err = KErrNone;
+	while(err == KErrNone)
+		{
+		// read the next journal entry
+		TRAP(err, ReadEntryL(journalStream, journalDrive));
+		if(err != KErrNone && err != KErrEof)
+			{
+			User::Leave(err);
+			}
+		}
+	CleanupStack::PopAndDestroy(&journalStream);
+	}
+
+void CJournalFile::ReadEntryL(RFileReadStream& aJournalStream, TInt aDrive)
+	{
+	TIntegrityServicesEvent event = static_cast<TIntegrityServicesEvent> (aJournalStream.ReadInt32L());
+	
+	HBufC* removedFile = NULL;
+	HBufC* backupFile = NULL;
+	HBufC* tempFile = NULL;
+	HBufC* addedFile = NULL;
+	
+	switch(event)
+		{
+		case ERemovedFile:
+			{
+			if(iLastEvent != ENone && iLastEvent != ERemovedFile &&
+				iLastEvent != ETempFile && iLastEvent != EAddedFile)
+				{
+				User::Leave(KErrCorrupt);
+				}
+
+			removedFile = HBufC::NewLC(aJournalStream, KMaxFileName);
+			// should be followed by the backup event
+			if(aJournalStream.ReadInt32L() != EBackupFile)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			backupFile = HBufC::NewLC(aJournalStream, KMaxFileName);
+			// make sure files are located on the same drive as the journal
+			if(CJournalFile::CheckFileNameL(iFs, *removedFile) != aDrive 
+				|| CJournalFile::CheckFileNameL(iFs, *backupFile) != aDrive)
+				{
+				User::Leave(KErrCorrupt);
+				}
+	
+			CIntegrityTreeLeaf* removeLeaf = iTreeRoot->AddNodeL(*removedFile, ERemovedFile, iJournalFileName);
+			CIntegrityTreeLeaf* backupLeaf = iTreeRoot->AddNodeL(*backupFile, EBackupFile, iJournalFileName);
+
+			removeLeaf->SetPeer(backupLeaf);
+			backupLeaf->SetPeer(removeLeaf);
+
+			CleanupStack::PopAndDestroy(backupFile);
+			CleanupStack::PopAndDestroy(removedFile);
+			iBackupFilesCount++;
+			iLastEvent = ERemovedFile;
+			}
+			break;
+			
+		case EBackupFile:
+			// cannot have a backup file on its own
+			User::Leave(KErrCorrupt);
+			break;
+			
+		case ETempFile:
+			if(iLastEvent != ENone && iLastEvent != ERemovedFile &&
+				iLastEvent != ETempFile && iLastEvent != EAddedFile)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			
+			tempFile = HBufC::NewLC(aJournalStream, KMaxFileName);
+			// make the file is located on the same drive as the journal
+			if(CJournalFile::CheckFileNameL(iFs, *tempFile) != aDrive)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			iTreeRoot->AddNodeL(*tempFile, ETempFile, iJournalFileName);
+			CleanupStack::PopAndDestroy(tempFile);
+			iLastEvent = ETempFile;
+			break;
+			
+		case EAddedFile:
+			if(iLastEvent != ENone && iLastEvent != ERemovedFile &&
+				iLastEvent != ETempFile && iLastEvent != EAddedFile)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			
+			addedFile = HBufC::NewLC(aJournalStream, KMaxFileName);
+			// make the file is located on the same drive as the journal
+			if(CJournalFile::CheckFileNameL(iFs, *addedFile) != aDrive)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			iTreeRoot->AddNodeL(*addedFile, EAddedFile, iJournalFileName);
+			CleanupStack::PopAndDestroy(addedFile);
+			iLastEvent = EAddedFile;
+			break;
+			
+		case EInstallComplete:
+			if(iLastEvent != ERemovedFile && iLastEvent != ETempFile &&
+				iLastEvent != EAddedFile)
+				{
+				User::Leave(KErrCorrupt);
+				}
+				
+			iLastEvent = EInstallComplete;
+			break;
+			
+		case EBackupFilesRemoved:
+			if(iLastEvent != EInstallComplete)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			iLastEvent = EBackupFilesRemoved;
+			break;
+			
+		case EAddedFilesRemoved:
+			if(iLastEvent != EInstallComplete && iLastEvent != ERemovedFile &&
+				iLastEvent != ETempFile && iLastEvent != EAddedFile)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			iLastEvent = EAddedFilesRemoved;
+			break;
+			
+		case ETempFilesRemoved:
+			if(iLastEvent != EBackupFilesRemoved || iLastEvent != ERemovedFilesRestored)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			iLastEvent = ETempFilesRemoved;
+			break;
+			
+		case ERemovedFilesRestored:
+			if(iLastEvent != EAddedFilesRemoved)
+				{
+				User::Leave(KErrCorrupt);
+				}
+			iLastEvent = ERemovedFilesRestored;
+			break;
+			
+		default:
+			User::Leave(KErrCorrupt);
+			break;
+   		}
+	}
+
+void CJournalFile::PrepareToWriteL()
+	{
+	RFile file;
+	CleanupClosePushL(file);
+	// try opening the journal if it already exists
+	TInt err = file.Open(iFs, iJournalFileName, EFileWrite | EFileShareAny);
+	if (err != KErrNone)
+		{
+		if (err == KErrNotFound)
+			{
+			// journal does not exist, try creating one
+			User::LeaveIfError(file.Create(iFs, iJournalFileName, EFileWrite | EFileShareAny));
+			}
+		else
+			{
+			User::Leave(err);
+			}
+		}
+	
+	TInt fileSize;
+	User::LeaveIfError(file.Size(fileSize));
+	
+	// attach to end of file for writing
+	iWriteStream.Attach(file, fileSize);
+	CleanupStack::Pop(&file); // file ownership transfered to stream
+	
+	// record the last known size.
+	iLastSize = fileSize;
+	}
+
+void CJournalFile::EventL(TIntegrityServicesEvent aEvent)
+	{
+	iWriteStream.WriteInt32L(static_cast<TInt> (aEvent));
+	iWriteStream.CommitL();
+	
+	iLastEvent = aEvent;
+	iLastSize = iWriteStream.Sink()->SizeL();
+	}
+
+void CJournalFile::AddL(const TDesC& aFileName)
+	{
+	iTreeRoot->AddNodeL(aFileName, EAddedFile, iJournalFileName);
+
+	// write filename to journal
+	iWriteStream.WriteInt32L(static_cast<TInt> (EAddedFile));
+	iWriteStream << aFileName;
+	iWriteStream.CommitL();
+	
+	iLastEvent = EAddedFile;
+	iLastSize = iWriteStream.Sink()->SizeL();
+	}
+
+void CJournalFile::RemoveL(const TDesC& aFileName, TDes& aBackupFileName)
+	{
+	// Only journal removals if the file hasn't been added in the same
+	// journalfile.
+	TInt err = iTreeRoot->FindNode(aFileName, EAddedFile);
+	if (err == KErrNotFound)
+		{
+		CIntegrityTreeLeaf* removalNode = iTreeRoot->AddNodeL(aFileName, ERemovedFile, iJournalFileName);
+
+		// generate the next backup filename for this journal
+		NextBackupFileNameL(aBackupFileName);
+	
+		CIntegrityTreeLeaf* backupNode = iTreeRoot->AddNodeL(aBackupFileName, EBackupFile, iJournalFileName);
+	
+		// set peers on both nodes
+		removalNode->SetPeer(backupNode);
+		backupNode->SetPeer(removalNode);
+		
+		// write filenames to journal
+		iWriteStream.WriteInt32L(static_cast<TInt> (ERemovedFile));
+		iWriteStream << aFileName;
+		iWriteStream.WriteInt32L(static_cast<TInt> (EBackupFile));
+		iWriteStream << aBackupFileName;
+		iWriteStream.CommitL();
+	
+		iLastEvent = ERemovedFile;
+		iLastSize = iWriteStream.Sink()->SizeL();
+		}
+	else
+		{
+		User::LeaveIfError(err);
+		}
+	}
+	
+void CJournalFile::TemporaryL(const TDesC& aFileName)
+	{
+	iTreeRoot->AddNodeL(aFileName, ETempFile, iJournalFileName);
+	
+	// write filename to journal
+	iWriteStream.WriteInt32L(static_cast<TInt> (ETempFile));
+	iWriteStream << aFileName;
+	iWriteStream.CommitL();
+	
+	iLastEvent = ETempFile;
+	iLastSize = iWriteStream.Sink()->SizeL();
+	}
+	
+void CJournalFile::SynchL()
+	{
+	// check if the journal is in synch with the underlying media
+	iWriteStream.Sink()->SynchL();
+	TInt size = iWriteStream.Sink()->SizeL();
+	if (size != iLastSize)
+		{
+		// it isn't in synch! read from the underlying media.
+		delete iTreeRoot;
+		iTreeRoot = NULL;
+		iWriteStream.Close();
+		iBackupFilesCount = 0;
+		
+		TChar drive;
+		User::LeaveIfError(iFs.DriveToChar(iDrive, drive));
+		
+		TBuf<2> driveSpec;
+		driveSpec.Append(drive);
+		driveSpec.Append(KDriveDelimiter);
+		iTreeRoot = CIntegrityTreeNode::NewL(driveSpec);
+		
+		ReadL();
+		PrepareToWriteL();
+		}
+	}	
+	
+void CJournalFile::JournalOperationL(TTreeWalkFunctionL aFunc, TIntegrityServicesEvent aTypeFilter,
+	CIntegrityServices& aIntegrityServices, CIntegrityServices::TFailType aFailType)
+	{
+	CFileMan* fileman = CFileMan::NewL(iFs);
+	CleanupStack::PushL(fileman);
+	iTreeRoot->WalkTreeL(aFunc, aTypeFilter, iFs, iLoader, *fileman, aIntegrityServices, aFailType);
+	CleanupStack::PopAndDestroy(fileman);
+	}
+
+void CJournalFile::NextBackupFileNameL(TDes& aBackupFileName)
+	{
+	TParsePtrC fileNamePtr(iJournalFileName);
+	aBackupFileName = fileNamePtr.DriveAndPath();
+	aBackupFileName.Append(fileNamePtr.Name());
+	aBackupFileName.Append(KPathDelimiter);
+	aBackupFileName.AppendNumUC(iBackupFilesCount, EHex);
+	aBackupFileName.Append(KExtDelimiter);
+	aBackupFileName.Append(KBackupExt);
+	
+	iBackupFilesCount++;
+	}
+
+
+TIntegrityServicesEvent CJournalFile::LastEvent() const
+	{
+	return iLastEvent;
+	}
+
+TInt CJournalFile::Drive()
+	{
+	return iDrive;
+	}
+	
+void CJournalFile::Close()
+	{
+	iWriteStream.Close();
+	}
+
+/*static*/ TInt CJournalFile::CheckFileNameL(RFs&, const TDesC& aFileName)
+	{
+	TParse parse;
+	// We allow wildcards because this is also used to generate the
+	// uninstall log which may contain FN entries using wildcards.
+	User::LeaveIfError(parse.Set(aFileName, NULL, NULL));
+	User::LeaveIfError(parse.DrivePresent());
+	User::LeaveIfError(parse.PathPresent());
+	
+	TDriveUnit drive(parse.Drive());
+	return drive;
+	}