remotestoragefw/remotefileengine/src/rsfwfiletable.cpp
changeset 0 3ad9d5175a89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/remotestoragefw/remotefileengine/src/rsfwfiletable.cpp	Thu Dec 17 09:07:59 2009 +0200
@@ -0,0 +1,816 @@
+/*
+* Copyright (c) 2003-2006 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:  metadata struct for remote files
+*
+*/
+
+
+#include <s32mem.h>
+
+#include "rsfwfiletable.h"
+#include "rsfwfileentry.h"
+#include "rsfwfileengine.h"
+#include "rsfwvolumetable.h"
+#include "rsfwvolume.h"
+#include "rsfwrfeserver.h"
+#include "rsfwwaitnotemanager.h"
+#include "mdebug.h"
+
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::NewL
+// ----------------------------------------------------------------------------
+//   
+CRsfwFileTable* CRsfwFileTable::NewL(CRsfwVolume* aVolume, TFileName& aCachePath)
+    {
+    CRsfwFileTable* self = new (ELeave) CRsfwFileTable();
+    DEBUGSTRING(("CRsfwFileTable: in NewL 0x%x", self));
+    CleanupStack::PushL(self);
+    self->ConstructL(aVolume, aCachePath);
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::ConstructL
+// ----------------------------------------------------------------------------
+//   
+void CRsfwFileTable::ConstructL(CRsfwVolume* aVolume, TFileName& aCachePath)
+    {
+    iFs = CRsfwRfeServer::Env()->iFs;
+    // The root will be number 1
+    iNodeId = 1;
+    iVolume = aVolume;
+    iRootFep = NULL;
+    iCachePath.Copy(aCachePath);
+    iPermanence = iVolume->iMountInfo.iMountStatus.iPermanence;
+    iMetaDataFilePath.Copy(aCachePath);
+    iMetaDataFilePath.Append(KMetaDataFileName);
+    iMetaDataEvents.Reset();
+    SetupCacheL();
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::~CRsfwFileTable
+// ----------------------------------------------------------------------------
+//   
+CRsfwFileTable::~CRsfwFileTable()
+    {
+    if (iRootFep)
+        {
+        // Delete the whole tree recursively
+        delete iRootFep;
+        iRootFep = NULL;
+        }
+    // Discard events
+    iMetaDataEvents.Close();
+    iMetaDataSlots.Close();
+    if (iMetaDataStore) 
+        {
+        delete iMetaDataStore;
+        }
+    }
+    
+    
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::AddL
+// this function associates aFep with this file table but does no
+// yet set to any other node's child (or root node), that must be done sepately
+// ----------------------------------------------------------------------------
+//   
+void CRsfwFileTable::AddL(CRsfwFileEntry* aFep)
+    {    
+    // Just assign a unique id
+    TFid newFid;
+    newFid.iVolumeId = iVolume->iMountInfo.iMountStatus.iVolumeId;
+    newFid.iNodeId = iNodeId;
+    aFep->SetFid(newFid);
+    aFep->iFileTable = this;
+    iNodeId++;
+    if (!iRootFep)
+        {
+        iRootFep = aFep;
+        }
+    // add item to metadata LRU list, 
+    // only add childless directories and non-cached files
+    if ( (aFep->Type() == KNodeTypeFile && aFep->iCachedSize == 0) 
+         || (aFep->Type() == KNodeTypeDir && aFep->Kids()->Count() == 0)
+         || (aFep->Type() == KNodeTypeUnknown) )
+        {
+        iVolume->iVolumeTable->AddToMetadataLRUPriorityListL(aFep, ECachePriorityNormal);        
+        }
+    // Note that the first added entry will always be the root
+    HandleMetaDataEvent(KNotifyNodeAdded, aFep);
+    }
+    
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::Remove
+// removed this fileentry and disconnects from the parent
+// does not delete the kid file/directory entries
+// in practise this means that if deleting a directory, all its entries must be deleted
+// recursively first
+// ----------------------------------------------------------------------------
+//   
+void CRsfwFileTable::RemoveL(CRsfwFileEntry* aFep)
+    {
+    DEBUGSTRING(("CRsfwFileTable::RemoveL"));
+    // remove item from metadata LRU list
+    iVolume->iVolumeTable->RemoveFromMetadataLRUPriorityList(aFep);        
+
+    if (aFep == iCurrentParent)
+        {
+        iCurrentParent = NULL;
+        }
+    if (iPermanence)
+        {
+        aFep->RemoveCacheFile();
+        HandleMetaDataEvent(KNotifyNodeRemoved, aFep);
+        }
+        
+    // remove this file entry from its parent node    
+    if (aFep->iParent)  
+        {
+        aFep->iParent->RemoveKidL(aFep);
+        }
+    }
+    
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::Lookup
+// ----------------------------------------------------------------------------
+//   
+CRsfwFileEntry* CRsfwFileTable::Lookup(const TFid& aFid)
+    {
+    if (!iRootFep)
+        {
+        return NULL;
+        }
+    if (iRootFep->Fid().iNodeId == aFid.iNodeId)
+        {
+        return iRootFep;
+        }
+    // Try to optimize by starting from the latest parent
+    CRsfwFileEntry* fep = NULL;
+    if (iCurrentParent)
+        {
+        fep = iCurrentParent->Lookup(aFid);
+        }
+    if (!fep)
+        {
+        fep = iRootFep->Lookup(aFid);
+        }
+    if (fep)
+        {
+        iCurrentParent = fep->Parent();
+        }
+    return fep;
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::DumpL
+// ----------------------------------------------------------------------------
+// 
+#ifdef _DEBUG
+void CRsfwFileTable::DumpL(TBool aAll)
+    {
+    if (iRootFep)
+        {
+        iRootFep->PrintL(0, ETrue, aAll);
+        }
+    }
+#else
+void CRsfwFileTable::DumpL(TBool /* aAll */)
+    {
+    }
+#endif //DEBUG
+ 
+ 
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::SetPermanenceL
+// ----------------------------------------------------------------------------
+// 
+void CRsfwFileTable::SetPermanenceL(TBool aPermanence)
+    {
+    if (iPermanence != aPermanence)
+        {
+        iPermanence = aPermanence;
+        if (!iPermanence)
+            {
+            delete iMetaDataStore;
+            iMetaDataStore = NULL;
+            }
+        SetupCacheL();
+        }
+    }
+       
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::HandleMetaDataEvent
+// ----------------------------------------------------------------------------
+// 
+void CRsfwFileTable::HandleMetaDataEvent(TInt aEvent, CRsfwFileEntry* aFep)
+    {
+    if (iMetaDataSaveState == EMetaDataSaveFailed)
+        {
+        // No use
+        return;
+        }
+
+    switch (aEvent)
+        {
+
+    case KNotifyNodeAdded:
+        {
+        // There should not be any previous additions
+        AddEvent(aEvent, aFep);
+        }
+        break;
+
+    case KNotifyNodeModified:
+        {
+        // There may appear spurious modifications
+        // to removed entries (like cache state set to false).
+        // We filter them out.
+        if (!NodeEvent(aFep->Fid().iNodeId))
+            {
+            AddEvent(aEvent, aFep);
+            }
+        }
+        break;
+
+    case KNotifyNodeRemoved:
+        {
+        TMetaDataEvent* oldEvent = NodeEvent(aFep->Fid().iNodeId);
+        if (oldEvent)
+            {
+            if (oldEvent->iEvent == KNotifyNodeAdded)
+                {
+                // just remove a previous "added"
+                RemoveEvent(oldEvent->iNodeId);
+                AddEvent(aEvent, aFep);
+                }
+            else
+                {
+                // Just replace "modified" (or duplicate "deleted")
+                // with "deleted"
+                oldEvent->iEvent = KNotifyNodeRemoved;
+                }
+            }
+        else
+            {
+            AddEvent(aEvent, aFep);
+            }
+        }
+        break;
+        
+    default:
+        break;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::LoadMetaDataL
+// ----------------------------------------------------------------------------
+// 
+CRsfwFileEntry* CRsfwFileTable::LoadMetaDataL()
+    {
+    // When this function is called the root node
+    // must already be created in the file table
+    iMetaDataStore->CompactL();
+    iMetaDataStore->ResetL(EFalse);
+
+    RPointerArray<CRsfwFileEntry> feps;
+    CleanupClosePushL(feps);
+
+    TBool done = EFalse;
+    while (!done)
+        {
+        CRsfwFileEntry* fep;
+        TMetaDataSlot slot;
+        TRAPD(err, LoadNodeL(fep, slot.iSlotId));
+        if (err == KErrNone)
+            {
+            if (fep != NULL) 
+                {
+                feps.Append(fep);
+                slot.iNodeId = fep->Fid().iNodeId;
+                iMetaDataSlots.Append(slot);
+                if (!fep->iParentNodeId)
+                    {
+                    // This must be the root
+                    DEBUGSTRING(("Root found at slot %d",
+                             iMetaDataSlots.Count() - 1));
+                    iRootFep = fep;
+                    }
+                }
+            }
+        else
+            {
+            // All or nothing ...
+            DEBUGSTRING(("LoadNode returned with err = %d", err));
+            if (err != KErrEof)
+                {
+                User::Leave(err);
+                }
+            done = ETrue;
+            }
+        }
+
+    // Now we have the restored the file entries
+    TInt i;
+    for (i = 0; i < feps.Count(); i++)
+        {
+        CRsfwFileEntry* fep = feps[i];
+        // Determine the next free node id
+        if (fep->Fid().iNodeId >= iNodeId)
+            {
+            iNodeId = fep->Fid().iNodeId + 1;
+            }
+
+        if (fep->iParentNodeId == 0)
+            {
+            // This is the root node
+            fep->SetParent(NULL);
+            }
+        else if (fep->iParentNodeId == 1)
+            {
+            // The parent is the root node
+            fep->SetParent(iRootFep);
+            iRootFep->iKids.Append(fep);
+            }
+        else
+            {
+            TInt j;
+            // This is O(n**2)
+            for (j = 0; j < feps.Count(); j++)
+                {
+                if (j != i)
+                    {
+                    // Find the parent for the node
+                    CRsfwFileEntry* parent = feps[j];
+                    if (fep->iParentNodeId == parent->Fid().iNodeId)
+                        {
+                        // Set up the two-way linkage
+                        fep->SetParent(parent);
+                        parent->iKids.Append(fep);
+                        break;
+                        }
+                    }
+                }
+            }
+        }
+
+    // Final fixes
+    for (i = 0; i < feps.Count(); i++)
+        {
+        CRsfwFileEntry* fep = feps[i];
+        // Fix volume ids and such ...
+        TFid fid;
+        fid = fep->Fid();
+        fid.iVolumeId = iVolume->iMountInfo.iMountStatus.iVolumeId;
+        fep->SetFid(fid);
+        fep->iFileTable = this;
+        // Add to LRU list (only cached files)
+        if ( fep->Type() == KNodeTypeFile && fep->IsCached() 
+            && (!iVolume->iVolumeTable->iUseExternalizedLRUList))
+            {
+            iVolume->iVolumeTable->AddToLRUPriorityListL(fep, ECachePriorityNormal);
+            }
+        // add item to metadata LRU list, 
+        // only add childless directories and non-cached files
+        if ( (fep->Type() == KNodeTypeFile && fep->iCachedSize == 0) 
+             || (fep->Type() == KNodeTypeDir && fep->Kids()->Count() == 0) )
+            {
+            iVolume->iVolumeTable->AddToMetadataLRUPriorityListL(fep, ECachePriorityNormal);        
+            }
+  
+        // Check consistency
+        if ((fep != iRootFep) && (!fep->Parent()))
+            {
+            // Should never happen
+            DEBUGSTRING16(("LodaMetaDataL() - parent missing for '%S'",
+                           fep->Name()));
+            }
+        }
+        
+    // Now we don't need the file entry pointer array any more
+    CleanupStack::PopAndDestroy(&feps); // feps
+    return iRootFep;
+    }
+        
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::SaveMetaDataDelta
+// ----------------------------------------------------------------------------
+// 
+TInt CRsfwFileTable::SaveMetaDataDelta()
+    {
+    DEBUGSTRING16(("CRsfwFileTable::SaveMetaDataDelta"));
+    TRAPD(err, SaveMetaDataDeltaL());
+    if (err != KErrNone)
+        {
+        DEBUGSTRING(("SaveMetaDataDeltaL() returns %d", err));
+        // Stop recording meta data
+        iMetaDataEvents.Reset();
+        iMetaDataSaveState = EMetaDataSaveFailed;
+        }
+    return err;
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::SetupCacheL
+// ----------------------------------------------------------------------------
+// 
+void CRsfwFileTable::SetupCacheL()
+    {
+    if (iPermanence)
+        {
+        iMetaDataStore = CRsfwMetaDataStore::NewL(iMetaDataFilePath); 
+        DEBUGSTRING(("SetupCacheL()"));
+        // The format of label is <drive_letter>:<uri>.
+        TRsfwMountConfig mountConfig;
+        TRAPD(err, iMetaDataStore->GetMountConfigL(mountConfig));
+        if ((err != KErrNone) ||
+            mountConfig.iUri.Compare(iVolume->iMountInfo.iMountConfig.iUri) !=
+            0)
+            {
+            // The saved metadata is not current - delete all
+            DEBUGSTRING(("Clearing Metadata ..."));
+            delete iMetaDataStore;
+            iMetaDataStore = NULL;
+            ClearCacheL();
+            // Start from scratch
+            iMetaDataStore = CRsfwMetaDataStore::NewL(iMetaDataFilePath);
+            iMetaDataStore->SetMountConfigL(iVolume->iMountInfo.iMountConfig);
+            }
+        }
+    else
+        {
+        ClearCacheL();
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::TotalCachedSize
+// ----------------------------------------------------------------------------
+// 
+TInt CRsfwFileTable::TotalCachedSize() 
+    {
+    if (!iRootFep)
+        {
+        return 0;
+        }
+    return iRootFep->TotalCachedSize(); 
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::TotalEntryCount
+// ----------------------------------------------------------------------------
+// 
+TInt CRsfwFileTable::TotalEntryCount() 
+    {
+    if (!iRootFep)
+        {
+        return 0;
+        }
+    return iRootFep->TotalEntryCount(); 
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::ClearCacheL
+// ----------------------------------------------------------------------------
+// 
+void CRsfwFileTable::ClearCacheL()
+    {
+    DEBUGSTRING(("Clearing cache ..."));
+    TFileName cachePath = iCachePath;
+    _LIT(KWild, "*");
+    cachePath.Append(KWild);
+    
+    CFileMan* fM = CFileMan::NewL(iFs);
+    CleanupStack::PushL(fM);
+    TInt err = fM->Delete(cachePath, CFileMan::ERecurse);
+    CleanupStack::PopAndDestroy(fM); // fM
+    if (err != KErrNone)
+        {
+        DEBUGSTRING16(("Cache cleaning of '%S' failed with err=%d",
+                       &cachePath,
+                       err));
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::NodeEvent
+// ----------------------------------------------------------------------------
+//   
+TMetaDataEvent* CRsfwFileTable::NodeEvent(TInt aNodeId)
+    {
+    // Search downwards (for efficiency)
+    TInt count = iMetaDataEvents.Count();
+    if (count)
+        {
+        TInt i;
+        for (i = count - 1; i >= 0; i--)
+            {
+            if (iMetaDataEvents[i].iNodeId == aNodeId)
+                {
+                return &iMetaDataEvents[i];
+                }
+            }
+        }
+    return NULL;
+    }
+    
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::AddEvent
+// ----------------------------------------------------------------------------
+//   
+void CRsfwFileTable::AddEvent(TInt aEvent, CRsfwFileEntry* aFep)
+    {
+    TMetaDataEvent event;
+    event.iEvent = aEvent;
+    event.iEntry = aFep;
+    event.iNodeId= aFep->Fid().iNodeId;
+    // For searching efficiency insert at the head
+    if (iMetaDataEvents.Append(event) != KErrNone)
+        {
+        iMetaDataEvents.Close();
+        iMetaDataSaveState = EMetaDataSaveFailed;
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::RemoveEvent
+// ----------------------------------------------------------------------------
+//   
+void CRsfwFileTable::RemoveEvent(TInt aNodeId)
+    {
+    TInt i;
+    for (i = 0; i < iMetaDataEvents.Count(); i++)
+        {
+        if (iMetaDataEvents[i].iNodeId == aNodeId)
+            {
+            iMetaDataEvents.Remove(i);
+            return;
+            }
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::LoadNodeL
+// ----------------------------------------------------------------------------
+// 
+void CRsfwFileTable::LoadNodeL(CRsfwFileEntry*& aFep, TInt &aSlot)
+    {
+    // Internalize a file entry
+    // Read data from the file at the specified slot
+    HBufC8* buf = HBufC8::NewLC(KMaxExternalizedFileEntrySize);
+    TPtr8 ptr = buf->Des();
+    TUint8* data = const_cast<TUint8 *>(ptr.Ptr());
+    TInt dataLength;
+    iMetaDataStore->GetNextDataL(data, dataLength, aSlot);
+    RMemReadStream stream(data, dataLength);
+    CleanupClosePushL(stream);
+    CRsfwFileEntry* fep = CRsfwFileEntry::NewL(stream);
+    DEBUGSTRING16(("CRsfwFileTable::LoadNodeL: Loaded node '%S'(id=%d, pid=%d, cn='%S')",
+                   fep->Name(),
+                   fep->Fid().iNodeId,
+                   fep->iParentNodeId,
+                   &fep->iCacheName));
+    CleanupStack::PopAndDestroy(2); // stream, buf
+
+    aFep = fep;
+    }
+    
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::SaveNodeL
+// ----------------------------------------------------------------------------
+// 
+void CRsfwFileTable::SaveNodeL(CRsfwFileEntry* aFep, TInt& aSlot)
+    {
+    // Externalize the file entry
+    HBufC8* buf = HBufC8::NewLC(KMaxExternalizedFileEntrySize);
+    TPtr8 ptr = buf->Des();
+    TUint8* data = const_cast<TUint8 *>(ptr.Ptr());
+    TInt dataLen;
+
+    RMemWriteStream stream(data, KMaxExternalizedFileEntrySize);
+    CleanupClosePushL(stream);
+
+    if (aFep)
+        {
+        // dump the externalized data in the memory buffer
+        // stream << *aFep;
+        aFep->ExternalizeL(stream);
+        MStreamBuf* streamBuf = stream.Sink();
+        dataLen = streamBuf->TellL(MStreamBuf::EWrite).Offset();
+        stream.CommitL();
+        }
+    else
+        {
+
+        DEBUGSTRING(("Removing slot %d", aSlot));
+        // This will clear the slot
+        data = NULL;
+        dataLen = 0;
+        }
+
+    // Write data to the file at the specified slot
+    iMetaDataStore->PutDataL(data, dataLen, aSlot);
+
+    CleanupStack::PopAndDestroy(2, buf); // stream, buf
+    }
+    
+// ----------------------------------------------------------------------------
+// CRsfwFileTable::SaveMetaDataDeltaL
+// ----------------------------------------------------------------------------
+// 
+void CRsfwFileTable::SaveMetaDataDeltaL()
+    {
+    DEBUGSTRING(("CRsfwFileTable::SaveMetaDataDeltaL"));
+    if (!iPermanence)
+        {
+        return;
+        }
+
+    if (iMetaDataEvents.Count() == 0)
+
+        {
+        // Nothing to do
+        return;
+        }
+
+    switch (iMetaDataSaveState)
+        {
+    case EMetaDataSaveNone:
+        iMetaDataStore->ResetL(ETrue);
+        iMetaDataSaveState = EMetaDataSaveStarted;
+        break;
+
+    case EMetaDataSaveStarted:
+        break;
+
+    case EMetaDataSaveFailed:
+        DEBUGSTRING(("EMetaDataSaveFailed!"));
+        User::Leave(KErrGeneral);
+        break;
+
+    default:
+        break;
+        }
+
+    TInt i;
+    for (i = 0; i < iMetaDataEvents.Count(); i++)
+        {
+        TInt slotPos;
+        TMetaDataEvent *event = &iMetaDataEvents[i];
+        
+        DEBUGSTRING(("SaveMetaDataDeltaL: id=%d, event=%d",
+                     event->iNodeId,
+                     event->iEvent));
+
+        switch (event->iEvent)
+            {
+        case KNotifyNodeModified:
+        case KNotifyNodeAdded:
+            {
+            TMetaDataSlot s; // dummy for finding
+            s.iNodeId = event->iNodeId;
+            slotPos = iMetaDataSlots.Find(s);
+            TInt slotId;
+            if (slotPos != KErrNotFound)
+                {
+                slotId = iMetaDataSlots[slotPos].iSlotId;
+                }
+            else
+                {
+                // We don't have a slot yet
+                slotId = -1;
+                }
+            SaveNodeL(event->iEntry, slotId);
+            if (slotPos == KErrNotFound)
+                {
+                TMetaDataSlot slot;
+                slot.iNodeId = event->iEntry->Fid().iNodeId;
+                slot.iSlotId = slotId;
+                iMetaDataSlots.Append(slot);
+                }
+            else
+                {
+                // The index may have changed
+                iMetaDataSlots[slotPos].iSlotId = slotId;
+                }
+            }
+            break;
+            
+        case KNotifyNodeRemoved:
+            {
+            TMetaDataSlot s; // dummy for finding
+            s.iNodeId = event->iNodeId;
+            slotPos = iMetaDataSlots.Find(s);
+            if (slotPos != KErrNotFound)
+                {
+                TInt slotId = iMetaDataSlots[slotPos].iSlotId;
+                iMetaDataSlots.Remove(slotPos);
+                // Saving null is the same as removing
+                SaveNodeL(NULL, slotId);              
+                }
+            }
+            break;
+            
+        default:
+            break;
+            }
+        }
+    iMetaDataEvents.Reset();
+
+    User::LeaveIfError(iMetaDataStore->Commit());
+#if 0
+    iMetaDataStore->CompactL();
+#endif
+    }
+
+
+void CRsfwFileTable::ResolveDirtyFilesL() 
+    {
+    DEBUGSTRING(("CRsfwFileTable::ResolveDirtyFilesL"));
+    if (iRootFep)
+        {
+        iRootFep->ResolveDirtyFilesL(); 
+        }   
+    SaveMetaDataDeltaL();    
+    }
+
+void CRsfwFileTable::ResolveDirtyFileL(CRsfwFileEntry *aFileEntry)
+    {
+    DEBUGSTRING(("CRsfwFileTable::ResolveDirtyFileL"));
+	if ((aFileEntry->IsOpenedForWriting() && (aFileEntry->CacheFileName())))
+		{
+		
+		DEBUGSTRING16(("file %S has uncommitted modifications, must be saved locally", aFileEntry->Name()));
+		// file with uncommitted modifications
+		// (i.e. saving changes to a remote server failed
+		// show "save as" dialog for this file
+		TRequestStatus status;
+		TInt err;
+    	TPckgBuf<TRsfwSaveToDlgRequest>   savetoRequest;
+		TBuf<KRsfwMaxFileSizeString> fileSizeString;
+        TEntry fEntry;
+        CRsfwRfeServer::Env()->iFs.Entry((*(aFileEntry->CacheFileName())), fEntry);
+        fileSizeString.Num(fEntry.iSize);
+		TPtrC cacheDriveLetter = aFileEntry->CacheFileName()->Left(1);
+	
+	    savetoRequest().iMethod = TRsfwNotPluginRequest::ESaveToDlg;
+        savetoRequest().iDriveName = Volume()->MountInfo()->iMountConfig.iName;								
+        savetoRequest().iFileName = *(aFileEntry->Name());
+        savetoRequest().iCacheDrive = cacheDriveLetter;
+        savetoRequest().iFileSize = fileSizeString;
+
+		
+		RNotifier notifier;
+		User::LeaveIfError(notifier.Connect());
+		notifier.StartNotifierAndGetResponse(status, KRsfwNotifierPluginUID,
+                    savetoRequest, savetoRequest);
+        User::WaitForRequest(status);
+		notifier.NotifyCancel();
+		notifier.Close();
+	
+        if (status.Int() != KErrCancel) 
+            {
+            // move the file from cache to the new location
+		    HBufC* newName = HBufC::NewMaxLC(KMaxPath);
+            TPtr pathPtr = newName->Des();
+            pathPtr = savetoRequest().iFileName;
+            CFileMan* fman = CFileMan::NewL(CRsfwRfeServer::Env()->iFs);
+            // we assume that this is local-to-local move, and can be synch. call
+            err = fman->Move(*(aFileEntry->CacheFileName()), pathPtr, CFileMan::EOverWrite);
+            delete fman;
+            if (err == KErrNone) 
+                {
+                Volume()->iVolumeTable->WaitNoteManager()->ShowFileSavedToDialogL(pathPtr);
+                }
+            else 
+                {
+                Volume()->iVolumeTable->WaitNoteManager()->ShowFailedSaveNoteL();
+                }
+   
+            CleanupStack::PopAndDestroy(newName);
+            }
+        
+        // in any case, remove the file entry from the file table and cache
+        // (has been moved or deleted)
+        RemoveL(aFileEntry);
+        delete aFileEntry;
+        aFileEntry = NULL;
+        DEBUGSTRING(("possible uncommitted modifications resolved"));  
+		}
+    }
+