installationservices/swtransactionservices/source/server/journalfile.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 15:21:33 +0300
branchRCL_3
changeset 25 7333d7932ef7
permissions -rw-r--r--
Revision: 201033 Kit: 201035

/*
* Copyright (c) 2008-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"
#include "usiflog.h"

_LIT(KBackupExt, "bak"); // extension for removed files in backup location

namespace Usif
{
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)
	{
	DEBUG_PRINTF2(_L("CJournalFile::ConstructL() - aFileName %S"), &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)
		{
		DEBUG_PRINTF2(_L("CJournalFile::ConstructL() - CJournalFile::ReadL() has left with error %d"), err);							
		// if necessary create the path
		if(err == KErrPathNotFound)
			{
			User::LeaveIfError(iFs.MkDirAll(iJournalFileName));
			}
		else if (err != KErrNotFound)
			{
			DEBUG_PRINTF2(_L("CJournalFile::ConstructL() - Leaving with error %d"), err);							
			User::Leave(err);
			}
		}
	// now prepare for writing
	PrepareToWriteL();
	}

void CJournalFile::ReadL()
	{
	RFileReadStream journalStream;
	User::LeaveIfError(journalStream.Open(iFs, iJournalFileName, EFileRead | 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)
			{
			DEBUG_PRINTF2(_L("CJournalFile::ReadL() - CJournalFile::ReadEntryL() has left with error %d Leaving with this error!"), err);							
			User::Leave(err);
			}
		}
	CleanupStack::PopAndDestroy(&journalStream);
	}

void VerifyIsFileEventL(TIntegrityServicesEvent aEvent)
	{
	if (aEvent != ERemovedFile &&
		aEvent != ETempFile && aEvent != EAddedFile)
		{
		User::Leave(KErrCorrupt);
		}	
	}

void VerifyIsEmptyOrFileEventL(TIntegrityServicesEvent aEvent)
	{
	if (aEvent == ENone)
		return;

	VerifyIsFileEventL(aEvent);
	}

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:
			{
			VerifyIsEmptyOrFileEventL(iLastEvent);
			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:
			VerifyIsEmptyOrFileEventL(iLastEvent);
			
			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:
			VerifyIsEmptyOrFileEventL(iLastEvent);
			
			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 ECommitted:
			VerifyIsFileEventL(iLastEvent);
				
			iLastEvent = ECommitted;
			break;
			
		case EBackupFilesRemoved:
			if(iLastEvent != ECommitted)
				{
				User::Leave(KErrCorrupt);
				}
			iLastEvent = EBackupFilesRemoved;
			break;
			
		case EAddedFilesRemoved:
			if(iLastEvent != ECommitted)
				VerifyIsFileEventL(iLastEvent); // The last event can be only a commit, or one of the file events

			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()
	{
	DEBUG_PRINTF2(_L("CJournalFile::PrepareToWriteL() iJournalFileName %S"), &iJournalFileName);							
	RFile file;
	CleanupClosePushL(file);
	// try opening the journal if it already exists
	TEntry entry;
	TBool journalFileExists = (iFs.Entry(iJournalFileName, entry) == KErrNone);
	if (journalFileExists)
		{
		User::LeaveIfError(file.Open(iFs, iJournalFileName, EFileWrite | EFileShareAny));
		}
	else
		{
		// journal does not exist, try creating one
		User::LeaveIfError(file.Create(iFs, iJournalFileName, EFileWrite | EFileShareAny));
		}
	
	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
	}

void CJournalFile::EventL(TIntegrityServicesEvent aEvent, TBool aSerializeEventToJournal /* = ETrue*/ )
	{
	DEBUG_PRINTF2(_L("CJournalFile::EventL() - aEvent %d"), aEvent);							
	if (aSerializeEventToJournal)
		{
		iWriteStream.WriteInt32L(static_cast<TInt> (aEvent));
		iWriteStream.CommitL();
		}
	
	iLastEvent = aEvent;
	}

void CJournalFile::AddL(const TDesC& aFileName)
	{
	DEBUG_PRINTF2(_L("CJournalFile::AddL() - aFileName %S"), &aFileName);							
	iTreeRoot->AddNodeL(aFileName, EAddedFile, iJournalFileName);

	// write filename to journal
	iWriteStream.WriteInt32L(static_cast<TInt> (EAddedFile));
	iWriteStream << aFileName;
	iWriteStream.CommitL();
	
	iLastEvent = EAddedFile;
	}

void CJournalFile::RemoveL(const TDesC& aFileName, TDes& aBackupFileName)
	{
	DEBUG_PRINTF2(_L("CJournalFile::RemoveL() - aFileName %S"), &aFileName);							
	// 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;
		}
	else
		{
		User::LeaveIfError(err);
		}
	}
	
void CJournalFile::TemporaryL(const TDesC& aFileName)
	{
	DEBUG_PRINTF2(_L("CJournalFile::TemporaryL() - aFileName %S"), &aFileName);							
	iTreeRoot->AddNodeL(aFileName, ETempFile, iJournalFileName);
	
	// write filename to journal
	iWriteStream.WriteInt32L(static_cast<TInt> (ETempFile));
	iWriteStream << aFileName;
	iWriteStream.CommitL();
	
	iLastEvent = ETempFile;
	}
	
void CJournalFile::JournalOperationL(TTreeWalkFunctionL aFunc, TIntegrityServicesEvent aTypeFilter,
	CIntegrityServices::TFailType aFailType)
	{
	CFileMan* fileman = CFileMan::NewL(iFs);
	CleanupStack::PushL(fileman);
	iTreeRoot->WalkTreeL(aFunc, aTypeFilter, iFs, iLoader, *fileman, 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;

	User::LeaveIfError(parse.Set(aFileName, NULL, NULL));

	if (!parse.DrivePresent())
		User::Leave(KErrArgument);
	
	if (!parse.PathPresent())
		User::Leave(KErrArgument);	
	
	TDriveUnit drive(parse.Drive());
	return drive;
	}
}//End of namespace Usif