loggingservices/eventlogger/test/src/t_loghicaphelper.cpp
changeset 0 08ec8eefde2f
child 11 667e88a979d7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loggingservices/eventlogger/test/src/t_loghicaphelper.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,724 @@
+// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+// Platform security prohibits the existing logeng tests from doing
+// direct operations on data-caged files.  Giving the tests a higher
+// capability might mask other errors.  Therefore any file manipulation
+// may be sub-contracted to this process as needed.
+// 
+//
+
+
+#include <s32file.h>
+#include <babackup.h>
+#include <bautils.h>
+#include <barsc.h>
+#include <logserv.rsg>
+#include "TEST.H"
+
+const TUid KTestEventUid = {0x10005393};
+_LIT(KTestEventDesc1, "Event Type Description");
+_LIT(KTestDirection1, "Direction");
+_LIT(KTestStatus1, "Status");
+_LIT(KTestStatus2, "Changed Status");
+_LIT(KTestNumber1, "Number");
+const TLogContactItemId KTestContact1 = 0x1234;
+const TLogContactItemId KTestContact2 = 0x1234567;
+_LIT(KTestRemote1, "Remote Test 1");
+_LIT(KTestRemote2, "Remote Test 2");
+_LIT(KTestRemote3, "Remote Test 3");
+_LIT(KTestStatusT3, "Status Test 3");
+_LIT(KTestEventDesc, "Test Event");
+
+_LIT(KLogEngPrivatePath, "c:\\private\\101f401d\\");
+
+_LIT(KLogHiCapHelperPanic, "TLHCHlpr");
+
+RFs theFs;
+CActiveScheduler *TheTestScheduler = NULL;
+
+//===========================================================================================
+
+static TPtrC FileName(const TText* aFile)
+    {
+    TPtrC p(aFile);
+    TInt ix=p.LocateReverse('\\');
+    if (ix<0)
+        ix=p.LocateReverse('/');
+    if (ix>=0)
+        p.Set(p.Mid(1+ix));
+    return p;
+    }
+
+void LogLeave(TInt aErr, const TText* aFile, TInt aLine)
+    {
+    TPtrC fname(FileName(aFile));
+    RDebug::Print(_L("*** LogEng test leave, err=%d, file: %S-%d\r\n"), aErr, &fname, aLine);
+    User::Leave(aErr);
+    }
+
+#undef  TEST_STRING
+#define TEST_STRING(s) _S(s)
+
+#undef  LEAVE
+#undef  LEAVE_IF_ERROR
+#define LEAVE(err)           LogLeave(err, TEST_STRING(__FILE__), __LINE__)
+#define LEAVE_IF_ERROR(err)  (err < KErrNone ? LogLeave(err, TEST_STRING(__FILE__), __LINE__) : void(0))
+
+//===========================================================================================
+
+//Supported remote operaions
+enum THelperOp
+	{
+	EOpNotDefined,
+    EDeleteDatabase1,
+    EDeleteDatabase2,
+	EIsDatabaseOpen,
+	EDatabaseSize,
+	ECopyCorruptDbFile,
+	ECopyCorruptDamagedDbFile,
+	ECopyOldDbFile,
+	EAddEvent,
+	EAddTestEvents,
+	EAddEventType,
+	EInvalidSchema,
+	EIsMatchingEnabled
+	} ;
+
+_LIT(KOldCorruptDatabase,"z:\\system\\data\\corruptLogdbu.dat");
+_LIT(KOldCorruptDamagedDatabase,"z:\\system\\data\\corruptDamagedLogdbu.dat");
+_LIT(KOldFormatDatabase,"z:\\system\\data\\oldLogdbu.dat");
+
+
+CTestActive::CTestActive(TInt aPriority)
+:	CActive(aPriority)
+	{
+	CActiveScheduler::Add(this);
+	iDelayTime=0;
+	}
+
+CTestActive::~CTestActive()
+	{
+	Cancel();
+	}
+
+void CTestActive::DoCancel()
+	{
+	TRequestStatus* s=&iStatus;
+	User::RequestComplete(s, KErrNone);
+	}
+
+void CTestActive::StartL()
+	{
+	iDelayCompletion=EFalse;
+	iDelayTime=0;
+	iStatus = KRequestPending;
+	SetActive();
+	}
+
+void CTestActive::StartL(TInt aDelay)
+	{
+	iDelayCompletion=ETrue;
+	iDelayTime=aDelay;
+	iStatus = KRequestPending;
+	SetActive();
+	}
+
+void CTestActive::RunL() 
+	{
+	if(iDelayCompletion && iDelayTime)
+		{
+		// Wait for events in other threads to have a go....
+		User::After(iDelayTime);
+		iDelayTime=0;
+		iStoredStatus=iStatus;
+		SetActive();
+		TRequestStatus* s=&iStatus;
+		User::RequestComplete(s, KErrNone);
+		}
+	else
+		{
+		if(iDelayCompletion)
+			iStatus=iStoredStatus;
+
+		LOGTEXT("CTestActive::RunL() - Stopping the scheduler");
+		CActiveScheduler::Stop();
+		}
+	}
+
+//.................................................................................................
+
+//See TestUtils::TestInvalidSchemaL(). Re-creates the LogEng database and checkes whether a new 
+//LogEng connection can be established (by creating a CLogClient object).
+static void TestInvalidSchemaL()
+	{
+	TheTestScheduler = new (ELeave) CActiveScheduler;
+	CleanupStack::PushL( TheTestScheduler );
+	CActiveScheduler::Install( TheTestScheduler );
+
+   	//Reseting of log engine databse is done via its backup server.  This seems a 
+   	//bit odd.  Perhaps write a CLogClient API that does it?
+   	//Create backup session wrapper
+   	CBaBackupSessionWrapper* backup = CBaBackupSessionWrapper::NewL();
+   	CleanupStack::PushL(backup);
+   
+   	//This eventually calls CLogBackup::ChangeFileLockL(..) which closes the database 
+   	//file and notifies all handles to that file that it has closed.
+   	backup->CloseFileL(KLogDatabaseName, MBackupObserver::EReleaseLockNoAccess);
+ 	User::After(1000000);
+   	
+   	//Since the log engine database file is closed we can replace it.   
+   	//Once this file is deleted, the backup server notices this and attempts to reopen 
+   	//the database.  Since the file is deleted a default database is created instead.
+    RDbNamedDatabase database;
+	TInt err = database.Replace(theFs, KLogDatabaseName);
+	database.Close();
+	LEAVE_IF_ERROR(err);
+
+	// The following will leave if there is a problem
+	CLogClient* client = CLogClient::NewL(theFs);
+	delete client;
+	
+	CleanupStack::PopAndDestroy(2); // scheduler + backup
+	TheTestScheduler = NULL;
+	}
+
+//.................................................................................................
+
+//See TestUtils::AddEventTypeL(). Adds an event type to the LogEng database.
+static void AddEventTypeL()
+	{
+	TheTestScheduler = new (ELeave) CActiveScheduler;
+	CleanupStack::PushL(TheTestScheduler);
+	CActiveScheduler::Install(TheTestScheduler);
+
+	CLogClient* client = CLogClient::NewL(theFs);
+	CleanupStack::PushL(client);
+
+	CTestActive* active = new(ELeave)CTestActive();
+	CleanupStack::PushL(active);
+
+	CLogEventType* type = CLogEventType::NewL();
+	CleanupStack::PushL(type);
+
+	type->SetUid(KTestEventUid);
+	type->SetDescription(KTestEventDesc1);
+	type->SetLoggingEnabled(ETrue);
+
+	client->AddEventType(*type, active->iStatus);
+	
+	active->StartL();
+	CActiveScheduler::Start();
+	LEAVE_IF_ERROR(active->iStatus.Int());
+	
+	CleanupStack::PopAndDestroy(4); // scheduler, client, active, type
+	TheTestScheduler = NULL;
+	}
+	
+//.................................................................................................
+
+//See TestUtils::AddViewTestEventsL(). Adds events to the LogEng database.
+static void AddTestEventsL()
+	{
+	TheTestScheduler = new (ELeave) CActiveScheduler;
+	CleanupStack::PushL(TheTestScheduler);
+	CActiveScheduler::Install(TheTestScheduler);
+	
+	CTestActive* active = new(ELeave)CTestActive;
+	CleanupStack::PushL(active);
+
+ 	CLogClient* client = CLogClient::NewL(theFs);
+	CleanupStack::PushL(client);
+	
+	TLogString direction;
+	client->GetString(direction, R_LOG_DIR_IN);
+
+	// Create a test event type
+	CLogEventType* type = CLogEventType::NewL();
+	CleanupStack::PushL(type);
+	type->SetUid(KTestEventUid);
+	type->SetDescription(KTestEventDesc);
+	type->SetLoggingEnabled(ETrue);
+
+	// Register the event type
+	active->StartL();
+	client->AddEventType(*type, active->iStatus);
+	CActiveScheduler::Start();
+	LEAVE_IF_ERROR(active->iStatus.Int());
+	
+	// Now add some events...
+	//
+	CLogEvent* event = CLogEvent::NewL();
+	CleanupStack::PushL(event);
+	//
+	event->SetEventType(KLogCallEventTypeUid);
+	event->SetContact(KTestContact1);
+	event->SetDirection(direction);
+	event->SetDurationType(KLogDurationValid);
+	event->SetNumber(KTestNumber1);
+	event->SetRemoteParty(KTestRemote1);
+	event->SetStatus(KTestStatus1);
+
+	// Add event
+	active->StartL();
+	client->AddEvent(*event, active->iStatus);
+	CActiveScheduler::Start();
+	LEAVE_IF_ERROR(active->iStatus.Int());
+	//
+	
+	event->SetEventType(KTestEventUid);	// low cap visible
+	event->SetContact(KTestContact1);
+	event->SetDirection(direction);
+	event->SetDurationType(KLogDurationNone);
+	event->SetNumber(KTestNumber1);
+	event->SetRemoteParty(KTestRemote1);
+	event->SetStatus(KTestStatus1);
+
+	// Add event
+	active->StartL();
+	client->AddEvent(*event, active->iStatus);
+	CActiveScheduler::Start();
+	LEAVE_IF_ERROR(active->iStatus.Int());
+	//
+		
+	event->SetEventType(KLogCallEventTypeUid);
+	event->SetContact(KTestContact2);
+	event->SetDirection(direction);
+	event->SetDurationType(KLogDurationValid);
+	event->SetNumber(KTestNumber1);
+	event->SetRemoteParty(KTestRemote2);
+	event->SetStatus(KTestStatus2);
+
+	// Add event and 4 duplicates
+	for(TInt i=0; i<5; i++)
+		{
+		active->StartL();
+		client->AddEvent(*event, active->iStatus);
+		CActiveScheduler::Start();
+		LEAVE_IF_ERROR(active->iStatus.Int());
+		}
+	
+	event->SetEventType(KTestEventUid);	// low cap visible
+	event->SetContact(KTestContact2);
+	event->SetDirection(KTestDirection1);
+	event->SetDurationType(KLogDurationData);
+	event->SetNumber(KTestNumber1);
+	event->SetRemoteParty(KTestRemote3);
+	event->SetStatus(KTestStatusT3);
+
+	// Add event
+	active->StartL();
+	client->AddEvent(*event, active->iStatus);
+	CActiveScheduler::Start();
+	LEAVE_IF_ERROR(active->iStatus.Int());
+	
+	CleanupStack::PopAndDestroy(5);	// event, client, type, active, scheduler
+	TheTestScheduler = NULL;
+	}
+	
+//.................................................................................................
+
+//See TestUtils::AddEventL(). Adds an event to the LogEng database.
+//The event type is set to be: KLogCallEventTypeUid.
+//Return: the Id of the added event
+static TInt AddEventL()
+	{
+	TheTestScheduler = new (ELeave) CActiveScheduler;
+	CleanupStack::PushL(TheTestScheduler);
+	CActiveScheduler::Install(TheTestScheduler);
+
+	CLogClient* client = CLogClient::NewL(theFs);
+	CleanupStack::PushL(client);
+
+	CTestActive* active = new(ELeave)CTestActive();
+	CleanupStack::PushL(active);
+
+	CLogEvent* event = CLogEvent::NewL();
+	CleanupStack::PushL(event);
+	
+	event->SetEventType(KLogCallEventTypeUid);
+
+	active->StartL();
+	client->AddEvent(*event, active->iStatus);
+	CActiveScheduler::Start();
+	LEAVE_IF_ERROR(active->iStatus.Int());
+    TLogId eventId = event->Id();
+	
+	CleanupStack::PopAndDestroy(4); // scheduler, client, active, event
+	TheTestScheduler = NULL;
+	return eventId;
+	}
+
+//.................................................................................................
+
+#ifdef _DEBUG
+static void StopLogServerL()
+	{
+	static RLogTestSession logServSession;
+	//this function doesn't have any effect on UREL builds as LogEng server doesn't 
+	//support the transient mode in UREL builds	
+	//Used for LogEng server side heap failure testing.
+	TInt error = KErrNone;
+	  
+	if(!logServSession.Handle())
+		{
+		error = logServSession.Connect();
+		}
+	  
+	// Is the server running?
+	if(error == KErrNotFound)
+		{
+		return;
+		}
+	LEAVE_IF_ERROR(error);
+	  
+	// Make the server transient
+	TInt p0 = 1;
+	TIpcArgs  ipcArgs(p0);
+	LEAVE_IF_ERROR(logServSession.Send(ELogMakeTransient, ipcArgs));
+	  
+	logServSession.Close();
+	  
+	User::After(6 * 1000000); // Enough time for the server to exit
+	}
+#else//_DEBUG
+static void StopLogServerL()
+	{
+	RDebug::Print(_L("StopLogServerL(): the LogEng server cannot be stopped in release mode. ELogMakeTransient is a debug message.\n"));
+	}
+#endif//_DEBUG
+
+//.................................................................................................
+
+static void DoDeleteDatabaseL(const TDesC& aDbPath, TBool aCloseBeforeDelete)
+    {
+    CBaBackupSessionWrapper* backup = NULL;
+    if(aCloseBeforeDelete)
+        {
+        //Reseting of log engine databse is done via its backup server.
+        //Create backup session wrapper
+        backup = CBaBackupSessionWrapper::NewL();
+        CleanupStack::PushL(backup);
+
+        //This eventually calls CLogBackup::ChangeFileLockL(..) which closes the database 
+        //file and notifies all handles to that file that it has closed.
+        backup->CloseFileL(aDbPath, MBackupObserver::EReleaseLockNoAccess);
+        User::After(1000000);
+        }
+
+    //Since the log engine database file is closed we can delete it.   
+    //Once this file is deleted, the backup server notices this and attempts to reopen 
+    //the database.  Since the file is deleted a default database is created instead.
+    
+    TInt error = theFs.Delete(aDbPath);
+
+    if(!(error == KErrNone || error == KErrNotFound || error == KErrPathNotFound) )
+        {
+        RDebug::Print(_L("DoDeleteDatabaseL(), Error %d deleting database \"%S\"\n"),error, &aDbPath);
+        }
+    else
+        {
+        RDebug::Print(_L("DoDeleteDatabaseL(), Database \"%S\" deleted ok\n"), &aDbPath);
+        }
+
+    if(aCloseBeforeDelete)
+        {
+        //The next line restarts the logeng server and re-creates logdbu.dat file.
+        CleanupStack::PopAndDestroy(backup);
+            
+        TInt count = 10;
+        while(count--)
+            {
+            User::After(100000);
+            TEntry entry;
+            if(theFs.Entry(aDbPath, entry) == KErrNone)
+                {
+                break;
+                }
+            }
+        }
+    }
+
+//See TestUtils::DeleteDatabaseL().
+//If "aCloseBeforeDelete" is true then re-create the LogEng database.
+static void DeleteDatabaseL(TBool aCloseBeforeDelete)
+    {
+    RDebug::Print(_L("DeleteDatabaseL(), Deleting database \"%S\"\r\n"), &KLogDatabaseName);
+
+    TRAPD(err, DoDeleteDatabaseL(KLogDatabaseName, aCloseBeforeDelete));
+    if(err == KErrNotFound || err == KErrPathNotFound)
+        {
+        err = KErrNone;
+        }
+    LEAVE_IF_ERROR(err);
+    }
+
+//.................................................................................................
+
+//See TestUtils::CopyCorruptDbL().
+//See TestUtils::CopyOldDbL(). 
+//See TestUtils::CopyCorruptDamagedDbL()
+//
+//The LogEng database will be replaced with a the database which name is passed as a parameter (for testing purposes).
+//The LogEng server will be stopped.
+//This call works only in debug mode.
+static void CopyDatabaseL(const TDesC& aNewDatabase)
+	{
+	StopLogServerL();
+	
+	CFileMan* fileMan=CFileMan::NewL(theFs);
+	CleanupStack::PushL(fileMan);
+	
+	DeleteDatabaseL(ETrue); // it won't be replaced as the server has stopped
+
+  	TInt err = fileMan->Copy(aNewDatabase, KLogDatabaseName);
+	if(err != KErrNone)
+		{
+		// Note this only works on textshell ROMs, techview ROMs fail here with KErrInUse (-14)
+		RDebug::Print(_L("CopyDatabaseL(), File copy \"%S\" to \"%S\", err=%d\n"), &aNewDatabase, &KLogDatabaseName, err);
+		LEAVE(err);
+		}
+	// files copied are sometimes read-only, so make read-write	
+	err = theFs.SetAtt(KLogDatabaseName, 0, KEntryAttReadOnly);
+	if(err != KErrNone)
+		{
+		RDebug::Print(_L("CopyDatabaseL(), Set \"%S\" file attributes err=%d\n"), &KLogDatabaseName, err);
+		LEAVE(err);
+		}
+
+	CleanupStack::PopAndDestroy(); // fileMan
+	}
+
+//.................................................................................................
+
+//See TestUtils::DatabaseSizeL().
+//Returns the LogEng database size.
+static TInt DatabaseSizeL()
+	{
+	TEntry file;
+	LEAVE_IF_ERROR(theFs.Entry(KLogDatabaseName, file));
+	return file.iSize;
+	}
+
+//.................................................................................................
+
+static void Initialize(const TDesC& aName)
+	{
+    User::RenameThread(aName);
+	}
+
+//.................................................................................................
+
+//See TestUtils::IsDatabaseOpenL().
+//Returns whether the LogEng database is open or not.
+static TBool DatabaseIsOpenL()
+	{
+	TBool answer;
+	LEAVE_IF_ERROR(theFs.IsFileOpen(KLogDatabaseName, answer));
+	return answer;
+	}
+
+//.................................................................................................
+
+//See TestUtils::MatchingEnabledL().
+//The function opens the LogEng server resource file (logserv.rsc) and gets the value of 
+//r_log_contact_match_count resource. This value will be retured as a result of the call.
+//If the value is 0 - "contacts matching" part of the test will be skipped.
+static TBool MatchingEnabledL()
+	{
+	// Get language of resource file
+	_LIT(KLogResourceFile,"z:\\private\\101f401d\\logserv.rsc");
+	TFileName fileName(KLogResourceFile);
+	BaflUtils::NearestLanguageFile(theFs, fileName);
+
+	// Open resource file
+	RResourceFile res;
+	res.OpenL(theFs, fileName);
+	HBufC8* buf = res.AllocReadLC(R_LOG_CONTACT_MATCH_COUNT);
+	res.Close();
+
+	TResourceReader reader;
+	reader.SetBuffer(buf);
+
+	TBool enabled = reader.ReadInt16() > 0;
+	CleanupStack::PopAndDestroy(buf);
+	return enabled;
+	}
+
+//.................................................................................................
+
+static TInt DoTaskL(THelperOp aOperation)
+    {
+    TInt rc = KErrNone;
+	switch(aOperation)
+	    {
+        case EDeleteDatabase1:
+            DeleteDatabaseL(ETrue);
+            break;
+        case EDeleteDatabase2:
+            DeleteDatabaseL(EFalse);
+            break;
+        case EIsDatabaseOpen:
+            rc = DatabaseIsOpenL();
+            break;
+        case EDatabaseSize:
+            rc = DatabaseSizeL();
+            break;
+        case ECopyCorruptDbFile:
+            CopyDatabaseL(KOldCorruptDatabase);
+            break;
+        case ECopyCorruptDamagedDbFile:
+            CopyDatabaseL(KOldCorruptDamagedDatabase);
+            break;
+        case ECopyOldDbFile:
+            CopyDatabaseL(KOldFormatDatabase);
+            break;
+        case EAddEvent:
+            rc = AddEventL();
+            break;
+        case EAddTestEvents:
+            AddTestEventsL();
+            break;
+        case EAddEventType:
+            AddEventTypeL();
+            break;
+        case EInvalidSchema:
+            TestInvalidSchemaL();
+            break;
+        case EIsMatchingEnabled:
+            rc = MatchingEnabledL();
+            break;
+        default:
+            rc = KErrNone;	// go away quietly
+            break;
+	    }
+	LEAVE_IF_ERROR(rc);
+	return rc;
+    }
+
+//.................................................................................................
+
+static THelperOp TaskType(const TDesC& aCmdLine)
+    {
+    THelperOp task = EOpNotDefined;
+    
+    if(aCmdLine.CompareF(_L("-delete_db1")) == 0)
+        {
+        task = EDeleteDatabase1;
+        }
+    else if(aCmdLine.CompareF(_L("-delete_db2")) == 0)
+        {
+        task = EDeleteDatabase2;
+        }
+    else if(aCmdLine.CompareF(_L("-db_is_open")) == 0)
+        {
+        task = EIsDatabaseOpen;
+        }
+    else if(aCmdLine.CompareF(_L("-db_size")) == 0)
+        {
+        task = EDatabaseSize;
+        }
+    else if(aCmdLine.CompareF(_L("-copy_corrupt")) == 0)
+        {
+        task = ECopyCorruptDbFile;
+        }
+    else if(aCmdLine.CompareF(_L("-copy_corrupt_damaged")) == 0)
+        {
+        task = ECopyCorruptDamagedDbFile;
+        }
+    else if(aCmdLine.CompareF(_L("-copy_old")) == 0)
+        {
+        task = ECopyOldDbFile;
+        }
+    else if(aCmdLine.CompareF(_L("-add_event")) == 0)
+        {
+        task = EAddEvent;
+        }
+    else if(aCmdLine.CompareF(_L("-add_view_test_events")) == 0)
+        {
+        task = EAddTestEvents;
+        }
+    else if(aCmdLine.CompareF(_L("-add_event_type")) == 0)   
+        {
+        task = EAddEventType;
+        }
+    else if(aCmdLine.CompareF(_L("-invalid_schema")) == 0)
+        {
+        task = EInvalidSchema;
+        }
+    else if(aCmdLine.CompareF(_L("-is_matching_enabled")) == 0)
+        {
+        task = EIsMatchingEnabled;
+        }
+    else
+        {
+        RDebug::Print(_L("** t_logHiCapHelper, ** Bad command line argument: %S\r\n"), &aCmdLine);
+        User::Panic(KLogHiCapHelperPanic, KErrArgument);
+        }
+    return task;
+    }
+
+static TInt EnvCreate()
+    {
+    TInt err = theFs.Connect();
+    if(err == KErrNone)
+        {
+        err = theFs.MkDirAll(KLogEngPrivatePath);
+        if(err == KErrAlreadyExists)
+            {
+            err = KErrNone; 
+            }
+        }
+    if(err != KErrNone)
+        {
+        RDebug::Print(_L("** t_logHiCapHelper, error %d creating test environment\r\n"), err);
+        }
+    return err;
+    }
+
+TInt E32Main()
+	{	
+	__UHEAP_MARK;
+	
+	Initialize(_L("T_HICAPHELPER"));
+
+	CTrapCleanup* tc = CTrapCleanup::New();
+	if(!tc)
+	   {
+        User::Panic(KLogHiCapHelperPanic, KErrNoMemory);
+	   }
+	
+	TBuf<64> cmdLine;
+	User::CommandLine(cmdLine);
+	THelperOp task = TaskType(cmdLine);
+
+    TInt rc = 0;
+	TInt err = EnvCreate();
+	if(err == KErrNone)
+	    {
+	    TRAP(err, rc = DoTaskL(task));    
+	    if(err < 0)
+	        {
+	        rc = err;
+	        RDebug::Print(_L("** t_logHiCapHelper, DoTaskL(), Task %d, Error %d\n"), task, rc);
+	        }
+	    }
+
+    theFs.Close();
+	delete tc;
+	
+	__UHEAP_MARKEND;
+
+	return rc;
+	}
+