diff -r 000000000000 -r 3ad9d5175a89 remotestoragefw/remotefileengine/src/rsfwfiletable.cpp --- /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 + +#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 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 :. + 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(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(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 savetoRequest; + TBuf 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")); + } + } +