remotestoragefw/remotefileengine/src/rsfwvolumetable.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:15:08 +0100
branchRCL_3
changeset 16 1aa8c82cb4cb
parent 0 3ad9d5175a89
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201021 Kit: 201035

/*
* Copyright (c) 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:   data struct for all volumes
*
*/


#include <sysutil.h>
#include <bautils.h>
#include <rsfwmountman.h>
#include <rsfwmountentry.h>

#include "rsfwvolumetable.h"
#include "rsfwvolume.h"
#include "rsfwinterface.h"
#include "rsfwmountstatemachine.h"
#include "rsfwfileentry.h"
#include "rsfwfiletable.h"
#include "rsfwconfig.h"
#include "rsfwfileengine.h"
#include "rsfwrfeserver.h"

#include "rsfwmountstore.h"
#include "rsfwwaitnotemanager.h"
#include "rsfwdormantmountloader.h"
#include "mdebug.h"

//CONSTANTS
_LIT(KRsfwLruFileName, "lru.dat");
_LIT(KRsfwRestorePendingMark, "rsfwmetadatarestorepending.dat");

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::NewL
//
// ----------------------------------------------------------------------------
//
CRsfwVolumeTable* CRsfwVolumeTable::NewL(CRsfwRfeServer* aRfeServer,
                                 CRsfwConfig* aRsfwConfig)
    {
    CRsfwVolumeTable* self = new (ELeave) CRsfwVolumeTable;
    DEBUGSTRING(("CRsfwVolumeTable: in NewL 0x%x", self));
    CleanupStack::PushL(self);
    self->ConstructL(aRfeServer, aRsfwConfig);
    CleanupStack::Pop(self);
    return self;
    }


// ----------------------------------------------------------------------------
// CRsfwVolumeTable::RestoreDormantMountsL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::RestoreDormantMountsL() 
    {
    DEBUGSTRING(("CRsfwVolumeTable::RestoreDormantMountsL enter"));
    // Currently, we always enable persistence (used to be configurrable)
    iPermanence = ETrue;
    if (iPermanence)
        {
        // if restoring permanent state causes RFE panic, we must delete 
        // the old permanent metadata and skip restoring it
        // otherwise RFE will just crash every time
        // Strategy is to add a file that tells that the process is ongoing.
        // If it is not able to finish successfully, we can conclude that there was a crash
        // marker is deleted from CRsfwDormantMountLoader, when we have succesfully also checked for dirty files
        TBool internalize = CheckAndAddProcessStartMarker();
        if (internalize) 
            {
            WillLRUPriorityListBeInternalized();
            RestoreVolumesL();
            InternalizeLRUPriorityListL();
            }
        else 
            {
            CleanupCorruptedCacheL();
            }
        }
    iDormantMountRestorePending = EFalse;   
    DEBUGSTRING(("CRsfwVolumeTable::RestoreDormantMountsL exit"));
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::ConstructL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::ConstructL(CRsfwRfeServer* aRfeServer, CRsfwConfig* aRsfwConfig)
    {
    iRfeServer = aRfeServer;
    iRsfwConfig = aRsfwConfig;

    iAllEnginesIdle = ETrue;
    
    // set some local pointers for commonly used variable from CRsfwRfeServer::Env()
    iFs = CRsfwRfeServer::Env()->iFs;
    iCacheRoot = &(CRsfwRfeServer::Env()->iCacheRoot);
    
    TInt err = iRsfwConfig->Get(RsfwConfigKeys::KMaxCacheSize,
                                iMaxCacheSize);
    if (err != KErrNone)
        {
        iMaxCacheSize = KDefaultMaxCacheSize;
        }

    err = iRsfwConfig->Get(RsfwConfigKeys::KMaxEntryCount,
                           iMaxEntryCount);
    if (err != KErrNone)
        {
        iMaxEntryCount = KDefaultMaxEntryCount;
        }

    HBufC* confItem = HBufC::NewLC(KMaxRsfwConfItemLength);
    TPtr confItemPtr = confItem->Des();

    // global wait notes manager class
    // must be created before restoring  the volumes
    iWaitNoteManager = CRsfwWaitNoteManager::NewL();
    
    RestoreDormantMountsL();
    
    err = iRsfwConfig->Get(RsfwConfigKeys::KFileCacheValidity,
                           iFileCacheTimeout);
    if (err)
        {
        iFileCacheTimeout = KDefaultCacheValidity;
        }
    err = iRsfwConfig->Get(RsfwConfigKeys::KDirCacheValidity,
                           iDirCacheTimeout);
    if (err)
        {
        iDirCacheTimeout = KDefaultDirCacheValidity;
        }

    err = iRsfwConfig->Get(RsfwConfigKeys::KRecognizerLimit,
                           iRecognizerLimit);
    if (err)
        {
        iRecognizerLimit = KDefaultRecognizerLimit;
        }

    err = iRsfwConfig->Get(RsfwConfigKeys::KCachingMode, confItemPtr);
    if (err == KErrNone)
        {
        TLex lex(confItemPtr);
        TChar firstChar = lex.Get();
        firstChar.UpperCase();
        switch (firstChar)
            {
        case 'W':
            iCachingMode = EWholeFileCaching;
            break;

        case 'F':
            iCachingMode = EFullIfa;
            break;

        case 'M':
        default:
            iCachingMode = EMetadataIfa;
            break;
            }
        }
    else
        {
        // caching mode configuration entry not found
        iCachingMode = EMetadataIfa;
        }
    if (iCachingMode != EWholeFileCaching)
        {
        GetMimeTypeSpecificLimits();
        }

    err = iRsfwConfig->Get(RsfwConfigKeys::KInactivityTimeout,
                           iInactivityTimeout);
    if (err)
        {
        iInactivityTimeout = KDefaultInactivityTimeout;
        }

    CleanupStack::PopAndDestroy(confItem);

    iMountStore = CRsfwMountStore::NewL(NULL);

    RProperty::Define(KRfeServerSecureUid,
                      ERsfwPSKeyConnect,
                      RProperty::EByteArray,
                      KMaxDrives);

    iMountStateProperty.Attach(KRfeServerSecureUid,
                               ERsfwPSKeyConnect);


   // this restores the dormant mounts asynchrously shortly after the server has started
    iDormantMountLoader = CRsfwDormantMountLoader::NewL(this);
    iDormantMountRestorePending = ETrue;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::~CRsfwVolumeTable
//
// ----------------------------------------------------------------------------
//
CRsfwVolumeTable::~CRsfwVolumeTable()
    {
    // save LRU list to the file
    ExternalizeLRUPriorityList();
    
    TInt i;
    DEBUGSTRING(("Deleting all volumes"));
    for (i = 0; i < KMaxVolumes; i++)
        {
        if (iVolumes[i])
            {
            delete iVolumes[i];
            }
        }
    delete iMountStore;
    iMountStateProperty.Close();
    if (iWaitNoteManager)
    	{
    	delete iWaitNoteManager;
    	}
    if (iDormantMountLoader)
    	{
    	delete iDormantMountLoader;
    	}    	
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::DispatchL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::DispatchL(TAny* aIp, TAny* aOp)
    {
    TRfeInArgs* ip = reinterpret_cast<TRfeInArgs*>(aIp);
    TRfeOutArgs* op = reinterpret_cast<TRfeOutArgs*>(aOp);
    CRsfwFileEngine* fileEngine = NULL;
    TInt volumeId;

    if(ip->iOpCode == EFsRoot)
        {
        volumeId = iLastVolumeId;
        // boot  - assume that ROOT
        // immediately follows after MountL()
        if (volumeId)
            {
            DEBUGSTRING(("Volume: acquired engine %d", volumeId));
            CRsfwVolume* volume = iVolumes[volumeId];
            if (volume)
                {
                fileEngine = volume->iFileEngine;
                }
            }
        }
    else
        {
        // iFid is always in the same position in the messages
        volumeId = static_cast<TRfeLookupInArgs*>(ip)->iFid.iVolumeId;
        CRsfwVolume* volume = VolumeByVolumeId(volumeId);
        if (volume)
            {
            fileEngine = volume->iFileEngine;
            }
        }
    if (fileEngine)
        {
        fileEngine->DispatchL(*ip, *op);
        }
    else
        {
        DEBUGSTRING(("Volume: no engine for %d", volumeId));
        User::Leave(KErrNotReady);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::VolumeIdByDriveLetter
//
// ----------------------------------------------------------------------------
//
TInt CRsfwVolumeTable::VolumeIdByDriveLetter(TChar aDriveLetter)
    {
    TInt driveNumber;
    iFs.CharToDrive(aDriveLetter, driveNumber);
    if (driveNumber < 0) // note that 0 = EDriveA is allowed
        {
        driveNumber = KErrNotFound;
        }
    return driveNumber;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::VolumeByVolumeId
//
// ----------------------------------------------------------------------------
//
CRsfwVolume* CRsfwVolumeTable::VolumeByVolumeId(TInt aVolumeId)
    {
    if ((aVolumeId >= 0) && (aVolumeId < KMaxVolumes))
        {
        return iVolumes[aVolumeId];
        }
    return NULL;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::VolumeByDriveLetter
//
// ----------------------------------------------------------------------------
//
CRsfwVolume* CRsfwVolumeTable::VolumeByDriveLetter(TChar aDriveLetter)
    {
    return VolumeByVolumeId(VolumeIdByDriveLetter(aDriveLetter));
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::RecoverVolumeL
//
// ----------------------------------------------------------------------------
//
TUint CRsfwVolumeTable::RecoverVolumeL(const TRsfwMountConfig& aMountConfig,
                                   CRsfwMountStateMachine* aCaller)
    {
    DEBUGSTRING(("Recovering volume"));
    TUint transactionId = 0;
    CRsfwVolume* volume = VolumeByVolumeId(aCaller->iVolumeId);
    if (volume)
        {
        aCaller->iVolume = volume;
        // set also fileengine pointer in the state machine,
        // so that the operation can be cancelled
        aCaller->SetFileEngine(volume->iFileEngine);
        if (User::UpperCase(
                aCaller->iVolume->iMountInfo.iMountConfig.iDriveLetter) ==
            User::UpperCase(aMountConfig.iDriveLetter))
            {
            if (aCaller->iVolume->iMountInfo.iMountConfig.iFlags &
                KMountFlagOffLine)
                {
                // We are working offline
                aCaller->iVolume->iMountInfo.iMountStatus.iConnectionState =
                    KMountNotConnected;
                }
            else
                {
                aCaller->iVolume->iMountInfo.iMountStatus.iConnectionState =
                    KMountStronglyConnected;
                }

            transactionId =
                aCaller->iVolume->iFileEngine->RequestConnectionStateL(
                    aCaller->iVolume->iMountInfo.iMountStatus.iConnectionState,
                    aCaller);
            }
        else
            {
            User::Leave(KErrArgument);
            }
        }
    else
        {
        User::Leave(KErrNotFound);
        }

    return transactionId;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::MountState
//
// ----------------------------------------------------------------------------
//
TInt CRsfwVolumeTable::MountState(TChar aDriveLetter)
    {
    DEBUGSTRING(("Getting mount state"));
    TInt state;
    // Find the volume
    CRsfwVolume* volume = VolumeByDriveLetter(aDriveLetter);
    if (volume)
        {
        TRsfwMountInfo& mountInfo = volume->iMountInfo;
        DEBUGSTRING16(("mount '%S'in state %d",
                       &mountInfo.iMountConfig.iName,
                       mountInfo.iMountStatus.iMountState));
        state = mountInfo.iMountStatus.iMountState;
        }
    else
        {
        DEBUGSTRING(("mount not found"));
        state = 0; // define KMountStateNone=0 in RsfwControl.h
        }
    return state;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::GetMountConfigL
//
// ----------------------------------------------------------------------------
//
TInt CRsfwVolumeTable::GetMountConfigL(TRsfwMountConfig& aMountConfig)
    {
    DEBUGSTRING16(("Getting mount config for name '%S', drive %c",
                   &aMountConfig.iName,
                   TUint(aMountConfig.iDriveLetter)));
    TInt err = KErrNone;
    const CRsfwMountEntry* entry =
        iMountStore->LookupEntryByDriveL(aMountConfig.iDriveLetter);
    if (!entry)
        {
        // Could not find by drive letter - retry with name
        entry = iMountStore->LookupEntryByNameL(aMountConfig.iName);
        }
    if (entry &&
        entry->Item(EMountEntryItemDrive) &&
        entry->Item(EMountEntryItemUri))
        {
        if (entry->Item(EMountEntryItemDrive)->Length())
            {
            TLex parse(*entry->Item(EMountEntryItemDrive));
            aMountConfig.iDriveLetter = parse.Get();
            if (entry->Item(EMountEntryItemName))
                {
                aMountConfig.iName.Copy(*entry->Item(EMountEntryItemName));
                }
            else
                {
                aMountConfig.iName.SetLength(0);
                }
            aMountConfig.iUri.Copy(*entry->Item(EMountEntryItemUri));
            if (entry->Item(EMountEntryItemUserName))
                {
                aMountConfig.iUserName.Copy(
                    *entry->Item(EMountEntryItemUserName));
                }
            else
                {
                aMountConfig.iUserName.SetLength(0);
                }

            if (entry->Item(EMountEntryItemPassword))
                {
                aMountConfig.iPassword.Copy(
                    *entry->Item(EMountEntryItemPassword));
                }
            else
                {
                aMountConfig.iPassword.SetLength(0);
                }

            if (entry->Item(EMountEntryItemIap))
                {
                aMountConfig.iAuxData.Copy(
                    *entry->Item(EMountEntryItemIap));
                }
            else
                {
                aMountConfig.iAuxData.SetLength(0);
                }

            TInt inactivityTimeout = iInactivityTimeout;
            if (entry->Item(EMountEntryItemInactivityTimeout))
                {
                TLex timeout(*entry->Item(EMountEntryItemInactivityTimeout));
                timeout.Val(inactivityTimeout);
                DEBUGSTRING(("Set inactivity timeout = %d",
                             inactivityTimeout));
                }
            aMountConfig.iInactivityTimeout = inactivityTimeout;

            aMountConfig.iFlags = 0;
            }
        else
            {
            err = KErrArgument;
            }
        }
    else
        {
        DEBUGSTRING(("mount configuration not found"));
        err = KErrNotFound;
        }

    return err;
    }


// ----------------------------------------------------------------------------
// CRsfwVolumeTable::RestoreVolumesL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::RestoreVolumesL()
    {
    DEBUGSTRING(("CRsfwVolumeTable::RestoreVolumesL"));
    // Load persistently stored volumes
    CDir* dirList;
    TInt err = iFs.GetDir(*iCacheRoot,
                         KEntryAttMaskSupported,
                         ESortByName,
                         dirList);
    CleanupStack::PushL(dirList);
    DEBUGSTRING(("GetDir for cacheRoot returned %d", err));
    if (err == KErrNone)
        {
        TInt i;
        for (i = 0; i < dirList->Count(); i++)
            {
            const TEntry& entry = (*dirList)[i];
            if (entry.iAtt & KEntryAttDir)
                {
                // The name of cache directories are C<volume_id>
                _LIT(KCacheMatch, "C*");
                if (entry.iName.Match(KCacheMatch) == 0)
                    {
                    TLex volumeAlpha(entry.iName.Mid(1));
                    TInt volumeId;
                    err = volumeAlpha.Val(volumeId);
                    if ((err == KErrNone) &&
                        (volumeId >= 0) &&
                        (volumeId < KMaxVolumes))
                        {
                        TRsfwMountConfig* mountConfig = new (ELeave) TRsfwMountConfig;
                        CleanupStack::PushL(mountConfig);
                        HBufC* metaDataPath = HBufC::NewLC(KMaxPath);
                        TPtr metaDataPathPtr = metaDataPath->Des();
                        metaDataPathPtr.Copy(*iCacheRoot);
                        metaDataPathPtr.Append(entry.iName);
                        metaDataPathPtr.Append('\\');
                        metaDataPathPtr.Append(KMetaDataFileName);
                        CRsfwMetaDataStore* metaDataStore =
                            CRsfwMetaDataStore::NewLC(metaDataPathPtr);
                        // Get the mount configuration info from the
                        // persistent store
                        TRAP(err,
                             metaDataStore->GetMountConfigL(*mountConfig));
                        // metaDataStore, metaDataPath
                        CleanupStack::PopAndDestroy(2);
                        if (err == KErrNone)
                            {
                            DEBUGSTRING16(("Restoring '%S' as volume %d'",
                                           &mountConfig->iUri,
                                           volumeId));
                            TRAP_IGNORE(MountDormantL(*mountConfig,
                                                      volumeId));
                            // In case of error, we should clear the cache
                            }
                        CleanupStack::PopAndDestroy(mountConfig);
                        }
                    }
                }
            }
        }
    CleanupStack::PopAndDestroy(dirList); // dirList
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::DismountByVolumeId
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::DismountByVolumeIdL(TInt aVolumeId,
                                       TBool aDiscardPermanentData)
    {
    DEBUGSTRING(("Dismounting volume %d", aVolumeId));
    CRsfwVolume* volume = VolumeByVolumeId(aVolumeId);
    if (volume)
        {
        if (aDiscardPermanentData)
            {
            // Delete also the meta data
            CRsfwFileEngine* fileEngine = volume->iFileEngine;
            if (fileEngine)
                {
                // Clear cache of files and meta data
                fileEngine->SetPermanenceL(EFalse);
                if (!volume->iMountInfo.iMountStatus.iPermanence)
                    {
                    // There was no change in the above -
                    // so, we have to clear the cache explicitly
                    fileEngine->iFileTable->SetupCacheL();
                    }
                }
            }
        delete volume;
        iVolumes[aVolumeId] = NULL;
        }
    else
        {
        User::Leave(KErrNotFound);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::DismountByDriveLetter
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::DismountByDriveLetterL(TChar aDriveLetter,
                                          TBool aDiscardPermanentData)
    {
    DEBUGSTRING(("Dismounting drive %c", TUint(aDriveLetter)));
    CRsfwVolume* volume = VolumeByDriveLetter(aDriveLetter);
    if (volume)
        {
        DismountByVolumeIdL(volume->iMountInfo.iMountStatus.iVolumeId,
                            aDiscardPermanentData);
        }
    else
        {
        User::Leave(KErrNotFound);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::GetMountList
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::GetMountList(TDriveList& aMountList)
    {
    aMountList.Zero();
    TInt i;
    for (i = 0; i < KMaxVolumes; i++)
        {
        if (iVolumes[i])
            {
            aMountList.Append(iVolumes[i]->
                              iMountInfo.iMountConfig.iDriveLetter);
            }
        }
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::GetMountInfo
//
// ----------------------------------------------------------------------------
//
TInt CRsfwVolumeTable::GetMountInfo(TRsfwMountInfo& aMountInfo)
    {
    CRsfwVolume* volume =
        VolumeByDriveLetter(aMountInfo.iMountConfig.iDriveLetter);
    if (volume)
        {
        volume->GetMountInfo(aMountInfo);
        return KErrNone;
        }
    return KErrNotFound;
    }


// ----------------------------------------------------------------------------
// CRsfwVolumeTable::GetMimeTypeSpecificLimits
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::GetMimeTypeSpecificLimits()
    {
    TInt err;
    err = iRsfwConfig->Get(RsfwConfigKeys::KImgJpegLimit, iImageJpegLimit);
    if (err != KErrNone)
        {
        iImageJpegLimit = KDefaultJpegLimit;
        }

    err = iRsfwConfig->Get(RsfwConfigKeys::KAudMpegLimit, iAudioMpegLimit);
    if (err)
        {
        iAudioMpegLimit = KDefaultMpegLimit;
        }
    }



// ----------------------------------------------------------------------------
// CRsfwVolumeTable::EnsureCacheCanBeAddedL
//
// ----------------------------------------------------------------------------
//
TBool CRsfwVolumeTable::EnsureCacheCanBeAddedL(TInt aBytes)
    {
    DEBUGSTRING(("CACHE MANAGER: ensure that %d bytes can be added to cache",
                 aBytes));
    DEBUGSTRING(("CACHE MANAGER: total cached size is currently %d",
                 TotalCachedSize()));
    DEBUGSTRING(("CACHE MANAGER: max cache size is %d", iMaxCacheSize));
 

    // FIRST: Is there enough space on the cache drive to store the new data
    TInt cacheDrive = CRsfwRfeServer::Env()->iCacheDrive;
    TBool isDiskFull = SysUtil::DiskSpaceBelowCriticalLevelL(&iFs,
                                                             aBytes,
                                                             cacheDrive);
    if ( isDiskFull )
        {
        // check whether clearing cache may help at all
        if ( aBytes > TotalCachedSize() )
            {
            isDiskFull = SysUtil::DiskSpaceBelowCriticalLevelL(&iFs,
                                                               aBytes - TotalCachedSize(),
                                                               cacheDrive);
            if ( isDiskFull )
                {
                DEBUGSTRING(("CACHE MANAGER: no space on disk"));
                return EFalse;
                }
            }
        // seems that clearing cache may help
        else
            {
            DEBUGSTRING(("CACHE MANAGER: running out of disk space, attempting to purge some bytes from cache"));
            do
                {
                CRsfwFileEntry* victim = iLRUPriorityList.GetAndRemoveFirstEntry();

                if (!victim)
                    {
                    DEBUGSTRING(("CACHE MANAGER: nothing to delete!!!"));
                    return EFalse; // cannot clear enough cache space
                    }

                DEBUGSTRING(("CACHE MANAGER: removing fid %d from the cache ",
                             victim->Fid().iNodeId));

                TDesC* cacheNamep = victim->CacheFileName();
                User::LeaveIfError(iFs.Delete(*cacheNamep));

                victim->SetCached(EFalse);
                victim->iCachedSize = 0;
                victim->Parent()->SetLocallyDirty();


                isDiskFull = SysUtil::DiskSpaceBelowCriticalLevelL(&iFs,
                                                                   aBytes,
                                                                   cacheDrive);
                }
            while ( isDiskFull );
            }
        }

    // SECOND: is there enough space in the cache
    if (TotalCachedSize() + aBytes > iMaxCacheSize)
        {
        TInt bytesDeleted = 0;
        TInt bytesToBeDeleted = TotalCachedSize() + aBytes - iMaxCacheSize;
        DEBUGSTRING(("CACHE MANAGER: attempting to purge %d bytes from cache",
                     bytesToBeDeleted));

        while (bytesDeleted < bytesToBeDeleted)
            {
            CRsfwFileEntry* victim = iLRUPriorityList.GetAndRemoveFirstEntry();

            if (!victim)
                {
                DEBUGSTRING(("CACHE MANAGER: XXXX: nothing to delete!!!"));
                return EFalse; // cannot clear enough cache space
                }

            DEBUGSTRING(("CACHE MANAGER: removing fid %d from the cache ",
                         victim->Fid().iNodeId));

            TDesC* cacheNamep = victim->CacheFileName();
            TInt victimSize = victim->iCachedSize;
            User::LeaveIfError(iFs.Delete(*cacheNamep));

            victim->SetCached(EFalse);
            victim->iCachedSize = 0;
            victim->Parent()->SetLocallyDirty();

            bytesDeleted = bytesDeleted + victimSize;
            }
        }

    return ETrue;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::EnsureMetadataCanBeAddedL
// This function is called at any time a new CRsfwFileEntry object is about to 
// be created in memory.
// However we want to prevent the scenario in which parent entry is deleted 
// just before its kid creation.
// That's why function takes as a parameter a parent entry for the entry 
// that is about to be created.
// ----------------------------------------------------------------------------
//
TBool CRsfwVolumeTable::EnsureMetadataCanBeAddedL(CRsfwFileEntry* aParent)
    {
    DEBUGSTRING(("memory cap: number of entries %d, entry limit: %d",
                 TotalEntryCount(), iMaxEntryCount));

    TBool parentFound = EFalse;

    while ( TotalEntryCount() >= iMaxEntryCount )
    {
        CRsfwFileEntry* victim = iMetadataLRUPriorityList.GetAndRemoveFirstEntry();

        // if no entries to delete on metadata LRU list try to remove item from file cache LRU list
        if (!victim)
            {
            victim = iLRUPriorityList.GetAndRemoveFirstEntry();

            if (!victim)
                {
                DEBUGSTRING(("memory cap: nothing to delete!!!"));
                return EFalse; // no posibility to add new entry
                }
            }

        // don't touch the root items
        if ( IsRoot(victim) )
            {
            continue;
            }
        
        // if we've found the parent don't touch it, just find the other victim ...
        if (victim && aParent && victim == aParent)
            {
            DEBUGSTRING(("<<<<< SAVED THE PARENT!!!! >>>>"));
            parentFound = ETrue;
            continue;
            }


        // destroy the item
        DEBUGSTRING(("memory cap: removing fid %d from memory",
                         victim->Fid().iNodeId));
        // victim's parent metadata will become out-of-date if we remove victim from memory
        if ( victim->Parent() )
            {
            victim->Parent()->iUseCachedData = EFalse;    
            }

        victim->DropLD();
    }
    
    // put the parent back to the list if it was removed from the list.
    // the reason is that at this point we are not sure whether child will be in fact created.
    // for now we were only interested in preventing the parent from being deleted, nothing more
    // as soon as the child is created, the parent will be removed from the list anyway
    if (parentFound)
        {
        iMetadataLRUPriorityList.AddNodeL(aParent, ECachePriorityNormal);
        }
    
    return ETrue;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::IsRoot
// Checks whether given entry is a root of some file table
// ----------------------------------------------------------------------------
//
TBool CRsfwVolumeTable::IsRoot(const CRsfwFileEntry* aEntry)
    {
    if (!aEntry)
        {
        return EFalse;
        }
        
    TInt i;
    for ( i = 0; i < KMaxVolumes; i++ )
        {
        CRsfwVolume* volume;
        volume = iVolumes[i];
        if ( volume && aEntry == volume->iFileEngine->iFileTable->Root())
            {
            return ETrue;
            }
        }
    return EFalse;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::TotalCachedSize
//
// ----------------------------------------------------------------------------
//
TInt CRsfwVolumeTable::TotalCachedSize()
    {
    TInt totalSize = 0;
    CRsfwVolume* volume;
    TInt i = 0;
    while (i < KMaxVolumes)
        {
        volume = iVolumes[i];
        if (volume)
            {
            TInt newSize = volume->iFileEngine->iFileTable->TotalCachedSize();
            totalSize += newSize;
            }
        i++;
        }
    return totalSize;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::TotalEntryCount
//
// ----------------------------------------------------------------------------
//
TInt CRsfwVolumeTable::TotalEntryCount()
    {
    TInt totalCount = 0;
    CRsfwVolume* volume;
    for ( TInt i = 0; i < KMaxVolumes; i++ )
        {
        volume = iVolumes[i];
        if (volume)
            {
            TInt volumeCount = volume->iFileEngine->iFileTable->TotalEntryCount();
            totalCount += volumeCount;
            }
        }
    return totalCount;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::AddToLRUPriorityListL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::AddToLRUPriorityListL(CRsfwFileEntry *aFe, TInt aPriority)
    {
    DEBUGSTRING(("CACHE MANAGER: adding fid '%d' to the LRU cache",
                 aFe->Fid().iNodeId));
    iLRUPriorityList.AddNodeL(aFe, aPriority);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::RemoveFromLRUPriorityList
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::RemoveFromLRUPriorityList(CRsfwFileEntry *aFe)
    {
    DEBUGSTRING(("CACHE MANAGER: removing fid '%d' from the LRU cache",
                  aFe->Fid().iNodeId));
    iLRUPriorityList.RemoveNode(aFe);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::AddToMetadataLRUPriorityListL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::AddToMetadataLRUPriorityListL(CRsfwFileEntry *aFe, TInt aPriority)
    {
    DEBUGSTRING(("memory cap: adding fid '%d' to the metadata LRU list",
                 aFe->Fid().iNodeId));
    iMetadataLRUPriorityList.AddNodeL(aFe, aPriority);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::RemoveFromMetadataLRUPriorityList
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::RemoveFromMetadataLRUPriorityList(CRsfwFileEntry *aFe)
    {
    DEBUGSTRING(("CRsfwVolumeTable::RemoveFromMetadataLRUPriorityList"));
    iMetadataLRUPriorityList.RemoveNode(aFe);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::MoveToTheBackOfMetadataLRUPriorityList
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::MoveToTheBackOfMetadataLRUPriorityListL(CRsfwFileEntry *aFe)
    {
    // just try to remove the entry from the list and if it was on the list then append it again
    if ( iMetadataLRUPriorityList.RemoveNode(aFe) == KErrNone )
        {
        DEBUGSTRING(("memory cap: moving fid '%d' to the back of metadata LRU list",
                     aFe->Fid().iNodeId));
        iMetadataLRUPriorityList.AddNodeL(aFe, ECachePriorityNormal);
        }
    }



// ----------------------------------------------------------------------------
// CRsfwVolumeTable::CheckAndAddProcessStartMarker
//
// ----------------------------------------------------------------------------
//
TBool CRsfwVolumeTable::CheckAndAddProcessStartMarker()
    {
    DEBUGSTRING(("CRsfwVolumeTable::CheckAndAddProcessStartMarker"));
    TFileName path;
    path.Copy(*iCacheRoot);
    path.Append(KRsfwRestorePendingMark);
    
    if (BaflUtils::FileExists(iFs, path))
        {
        // file already exists, the previous attempt to restore metadata must have failed
        DEBUGSTRING(("returning EFalse; file already exists"));
        return EFalse;
        }
    else 
        {
        // create an empty file
        TInt err;
        RFile markerFile;
        err = markerFile.Create(iFs, path, EFileWrite);
        if (err) 
            {
            return EFalse;
            }
        else 
            {
            DEBUGSTRING(("returning ETrue; file created"));
            markerFile.Close();
            return ETrue;
            }
 
        }
    }
    
// ----------------------------------------------------------------------------
// CRsfwVolumeTable::DeleteTheMarker
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::DeleteTheMarker()
    {    
    TFileName path;
    path.Copy(*iCacheRoot);
    path.Append(KRsfwRestorePendingMark);
    
    // ignore the error
    // if this fails for some reason, lets allow the file to be there
    // as "something" must be wrong
    iFs.Delete(path);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::CleanupCorrutedCacheL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::CleanupCorruptedCacheL()
    {    
    // delete everything from the cache
    TFileName cachepath;
    cachepath.Copy(*iCacheRoot);
    CFileMan* fileMan = CFileMan::NewL(iFs);
    fileMan->Delete(cachepath, CFileMan::ERecurse);
    delete fileMan;
    }


// ----------------------------------------------------------------------------
// CRsfwVolumeTable::WillExternalizedLRUPriorityListBeUsed
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::WillLRUPriorityListBeInternalized()
    {
    DEBUGSTRING(("CRsfwVolumeTable::WillLRUPriorityListBeInternalized"));
    iUseExternalizedLRUList = EFalse;
    // check whether the file with externalized data exists
    TFileName path;
    path.Copy(*iCacheRoot);
    path.Append(KRsfwLruFileName);

    if (BaflUtils::FileExists(iFs, path))
        {
        iUseExternalizedLRUList = ETrue;
        }
    DEBUGSTRING(("...set to %d", iUseExternalizedLRUList)); 
    }


// ----------------------------------------------------------------------------
// CRsfwVolumeTable::ExternalizeLRUPriorityList
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::ExternalizeLRUPriorityList()
    {
    TRAPD(err, ExternalizeLRUPriorityListL());
    if (err)
        {
        DEBUGSTRING(("Externalizing LRU priority list failed!"));
        }
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::ExternalizeLRUPriorityList
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::ExternalizeLRUPriorityListL()
    {        
    // prepare temp path
    _LIT(KRsfwLruTempFileName, "lru.temp");
    TFileName tempPath;
    tempPath.Copy(*iCacheRoot);
    tempPath.Append(KRsfwLruTempFileName);
    
    // create temp file
    RFile file;
    CleanupClosePushL(file);
    User::LeaveIfError(file.Replace(iFs, tempPath, EFileShareAny | EFileWrite));

    // associate stream
    RFileWriteStream stream(file);
    CleanupClosePushL(stream);

    // externalize
    iLRUPriorityList.ExternalizeL(stream);
    stream.CommitL();

    // cleanup
    CleanupStack::PopAndDestroy(2); // stream, file        

    // everything went ok -> rename lru.temp into lru.dat
    TFileName path;
    path.Copy(*iCacheRoot);
    path.Append(KRsfwLruFileName);
    CFileMan* fm = CFileMan::NewL(iFs);
    CleanupStack::PushL(fm);
    TInt err = fm->Rename(tempPath, path, CFileMan::EOverWrite);
    if (err)
        {
        fm->Delete(tempPath);
        fm->Delete(path);
        User::Leave(err);
        }
    CleanupStack::PopAndDestroy(fm);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::InternalizeLRUPriorityList
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::InternalizeLRUPriorityListL()
    {
    if (!iUseExternalizedLRUList)
        {
        // it means LRU has been already populated when loading metadata
        // so nothing to do here
        return;
        }
    // prepare path
    TFileName path;
    path.Copy(*iCacheRoot);
    path.Append(KRsfwLruFileName);
    
    // open file
    RFile file;
    TInt err = file.Open(iFs, path, EFileShareAny | EFileRead);
    if ( err == KErrNone )
        {
        CleanupClosePushL(file);

        // associate stream
        RFileReadStream stream(file);
        CleanupClosePushL(stream);

        // internalize
        TRAP(err, iLRUPriorityList.InternalizeL(stream, this));

        // cleanup
        CleanupStack::PopAndDestroy(2); // stream, file        
        }

    DEBUGSTRING(("InternalizeLRUPriorityListL: status %d", err));
        
    // once internalizing is done, the file is not needed anymore 
    // ignore the result
    iFs.Delete(path);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::OperationCompleted()
// This function may shut down RFE.
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::OperationCompleted(CRsfwVolume* /* aVolume */)
    {
    DEBUGSTRING(("Volume operation completed"));
    // Shut down the whole server if all remaining mounts are dormant
    // and we are not still restoring dormant mounts
    if ((iAllEnginesIdle) && (!iDormantMountRestorePending))
        {
        iRfeServer->AllEnginesIdling(KRsfwDormantShutdownTimeout);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::VolumeStateChanged()
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::VolumeStateChanged(CRsfwVolume* aVolume)
    {
    DEBUGSTRING(("Volume state changed"));

    TBool allEnginesIdle = ETrue;
    TInt i = 0;
    do
        {
        if (iVolumes[i])
            {
            if (!IsMountIdle(iVolumes[i]->iMountInfo.iMountStatus))
                {
                // Do not shut down if there are connected mounts
                allEnginesIdle = EFalse;
                }
            }
        } while ((++i < KMaxVolumes) && allEnginesIdle);

    // one more thing is to check the current volume since 
    // if the drive was newly mounted, it will not be found from the volume table 
    if (allEnginesIdle)
        {
        TInt driveNumber = VolumeIdByDriveLetter(aVolume->MountInfo()->iMountConfig.iDriveLetter);     
        if ((driveNumber != KErrNotFound) && 
            (!IsMountIdle(aVolume->MountInfo()->iMountStatus)))
            {
            allEnginesIdle = EFalse;
            }         
        }

    DEBUGSTRING(("All engines idle = %d", allEnginesIdle));
    iAllEnginesIdle = allEnginesIdle;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::PublishConnectionStatus()
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::PublishConnectionStatus(CRsfwVolume* aVolume)
    {
    DEBUGSTRING(("Publishing connection status:"));
    TDriveList driveList;
    driveList.FillZ(driveList.MaxLength());
    TInt i;
    // (at least) record the state of the volume received as a parameter
    // (if the drive was newly mounted, it will not be found from the volume table 
    TInt driveNumber = VolumeIdByDriveLetter(aVolume->MountInfo()->iMountConfig.iDriveLetter);     
    if ((driveNumber != KErrNotFound) && 
       (aVolume->MountInfo()->iMountStatus.iMountState != KMountStateDormant))
        {
        DEBUGSTRING(("- connected: %c", TUint(aVolume->MountInfo()->iMountConfig.iDriveLetter)));
        driveList[driveNumber] = 1;
        }    
    
    // for convenience, record the states of other volumes too from the volume table
    for (i = 0; i < KMaxVolumes; i++)
        {
        if (iVolumes[i])
            {
            TRsfwMountInfo& mountInfo = iVolumes[i]->iMountInfo;
            if (mountInfo.iMountStatus.iMountState != KMountStateDormant)
                {
                driveNumber =
                    VolumeIdByDriveLetter(mountInfo.iMountConfig.iDriveLetter);
                if (driveNumber != KErrNotFound)
                    {
                    DEBUGSTRING(("- connected: %c",
                                 TUint(mountInfo.iMountConfig.iDriveLetter)));
                    driveList[driveNumber] = 1;
                    }
                }
            }
        }
          
    iMountStateProperty.Set(driveList);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::WaitNoteManager()
//
// ----------------------------------------------------------------------------
//
CRsfwWaitNoteManager* CRsfwVolumeTable::WaitNoteManager()
	{
	return iWaitNoteManager;
	}

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::IsCachedDataStillValid()
//
// ----------------------------------------------------------------------------
//
TBool CRsfwVolumeTable::IsCachedDataStillValid(TTime aCachedTime)
	{
	return IsCacheStillValid(aCachedTime,
						TTimeIntervalSeconds(iFileCacheTimeout));
	}

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::IsCachedAttrStillValid()
//
// ----------------------------------------------------------------------------
//
TBool CRsfwVolumeTable::IsCachedAttrStillValid(TTime aCachedTime)
	{
	return IsCacheStillValid(aCachedTime,
						TTimeIntervalSeconds(iDirCacheTimeout));
	}



// ----------------------------------------------------------------------------
// CRsfwVolumeTable::MountDormantL
//
// ----------------------------------------------------------------------------
//
void CRsfwVolumeTable::MountDormantL(const TRsfwMountConfig& aMountConfig,
                                 TInt aVolumeId)
    {
    // Bind a volume id to a file engine
    DEBUGSTRING16(("Restoring drive '%c' with uri '%S' and flags 0x%x",
                   TUint(aMountConfig.iDriveLetter),
                   &aMountConfig.iUri,
                   aMountConfig.iFlags));

    // Create a file engine for the volume
    CRsfwVolume* volume = new (ELeave) CRsfwVolume();
    CleanupStack::PushL(volume);
    volume->iMountInfo.iMountConfig = aMountConfig;
    volume->iMountInfo.iMountStatus.iVolumeId = aVolumeId;
    volume->iVolumeTable = this;
    volume->iMountInfo.iMountStatus.iPermanence = iPermanence;
    // We are working offline
    volume->iMountInfo.iMountStatus.iMountState = KMountStateDormant;
    volume->iMountInfo.iMountStatus.iConnectionState = KMountNotConnected;
    CRsfwFileEngine* fileEngine = CRsfwFileEngine::NewL(volume);
    volume->iFileEngine = fileEngine;
    delete iVolumes[aVolumeId];
    iVolumes[aVolumeId] = volume;
    CleanupStack::Pop(volume);
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::IsCachedAttrStillValid()
//
// ----------------------------------------------------------------------------
//
TBool CRsfwVolumeTable::IsCacheStillValid(TTime aCachedTime,
									  TTimeIntervalSeconds aValidity)
	{
	TTime now;
	TTime comp;

	now.UniversalTime();
	comp = now - aValidity;

    if (comp >= aCachedTime)
        {
        return EFalse;
        }
    else
        {
        return ETrue;
        }
	}
	
// ----------------------------------------------------------------------------
// CRsfwVolumeTable::PurgeFromCache()
//
// ----------------------------------------------------------------------------
//	
TInt CRsfwVolumeTable::PurgeFromCache(TDesC& aCachePath) 
    {
    // get the volume id for this path
    TParse parser;
    parser.Set(aCachePath, NULL, NULL);
    if (!(parser.DrivePresent())) 
        {
        return KErrArgument;
        }
    TPtrC drive = parser.Drive();
    CRsfwVolume* volume = VolumeByDriveLetter(drive[0]);
    if (!volume) 
        {
        return KErrNotFound;
        }
    
    if (!(parser.PathPresent())) 
        {
        return KErrArgument;
        }
            
    if (parser.NamePresent()) 
        {
        // this is a file
        return KErrArgument;
        }
    return volume->iFileEngine->PurgeFromCache(parser.Path());
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::CancelTransfer()
//
// ----------------------------------------------------------------------------
//	
TInt CRsfwVolumeTable::CancelTransferL(TDesC& aFilePath) 
    {
    DEBUGSTRING16(("CRsfwVolumeTable::CancelTransferL for %S", &aFilePath));
      // get the volume id for this path
    TParse parser;
    parser.Set(aFilePath, NULL, NULL);
    if (!(parser.DrivePresent())) 
        {
        return KErrArgument;
        }
    TPtrC drive = parser.Drive();
    CRsfwVolume* volume = VolumeByDriveLetter(drive[0]);
    if (!volume) 
        {
        return KErrNotFound;
        }
              
    if (!(parser.NamePresent())) 
        {
        // this is not a file
        return KErrArgument;
        }
       
    
    // mark the file entry as "cancelled"
    TPtrC pathPtr  = aFilePath.Right(aFilePath.Length() - 2); //drop the drive letter
    CRsfwFileEntry* targetFid = volume->iFileEngine->FetchFep(pathPtr);
    if (targetFid)
        {
        DEBUGSTRING(("setting KNodeWritingCancelled for fid %d", targetFid->Fid().iNodeId));
        targetFid->SetFlags(KNodeWritingCancelled);
        }
        
    volume->iFileEngine->CancelTransactionL(aFilePath);

    return KErrNone;
    }

// ----------------------------------------------------------------------------
// CRsfwVolumeTable::IsMountIdle()
//
// ----------------------------------------------------------------------------
//	
TBool CRsfwVolumeTable::IsMountIdle(TRsfwMountStatus& aMountStatus) 
    {
    if (aMountStatus.iMountState != KMountStateDormant
        || aMountStatus.iConnectionState == KMountConnecting)
        {
        return EFalse;
        }
    else
        {
        return ETrue;
        }
    }