diff -r 88ee4cf65e19 -r 1aa8c82cb4cb remotestoragefw/remotefileengine/src/rsfwfileengine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/remotestoragefw/remotefileengine/src/rsfwfileengine.cpp Wed Sep 01 12:15:08 2010 +0100 @@ -0,0 +1,1763 @@ +/* +* 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: Operation independent remote file handling functions +* +*/ + + +#include +#include + +#include "rsfwfileentry.h" +#include "rsfwfiletable.h" +#include "rsfwvolumetable.h" +#include "rsfwvolume.h" +#include "rsfwrfestatemachine.h" +#include "rsfwinterface.h" +#include "rsfwcontrol.h" +#include "rsfwremoteaccess.h" +#include "rsfwfileengine.h" +#include "rsfwrfeserver.h" +#include "rsfwlockmanager.h" +#include "mdebug.h" +#include "rsfwdirent.h" +#include "rsfwdirentattr.h" +#include "rsfwinterface.h" + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::NewL +// ---------------------------------------------------------------------------- +// +CRsfwFileEngine* CRsfwFileEngine::NewL(CRsfwVolume* aVolume) + { + CRsfwFileEngine* self = CRsfwFileEngine::NewLC(aVolume); + CleanupStack::Pop(self); + return self; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::NewLC +// ---------------------------------------------------------------------------- +// +CRsfwFileEngine* CRsfwFileEngine::NewLC(CRsfwVolume* aVolume) + { + DEBUGSTRING(("CRsfwFileEngine::NewLC")); + CRsfwFileEngine* self = new (ELeave) CRsfwFileEngine(); + DEBUGSTRING(("CRsfwFileEngine: in NewLC 0x%x", self)); + CleanupStack::PushL(self); + self->ConstructL(aVolume); + return self; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::ConstructL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::ConstructL(CRsfwVolume* aVolume) + { + iRemoteAccess = NULL; + iRootFid = NULL; + iRootFep = NULL; + iVolume = aVolume; + iFs = CRsfwRfeServer::Env()->iFs; + iConnectionState = KMountNotConnected; + __ASSERT_ALWAYS(iVolume != NULL, User::Panic(KRfeServer, EConstructingServerStructs)); + iInactivityTimeout = + iVolume->iMountInfo.iMountConfig.iInactivityTimeout * 1000000; + PrepareCacheL(); + // Create file table + iFileTable = CRsfwFileTable::NewL(aVolume, iCacheRoot); + __ASSERT_ALWAYS(iVolume->iVolumeTable != NULL, User::Panic(KRfeServer, + EConstructingServerStructs)); + SetupRootL(iVolume->iVolumeTable->iPermanence); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::~CRsfwFileEngine +// ---------------------------------------------------------------------------- +// +CRsfwFileEngine::~CRsfwFileEngine() + { + DEBUGSTRING(("CRsfwFileEngine destructor")); + delete iFileTable; + delete iRemoteAccess; + delete iLockManager; + StopInactivityTimer(); + delete iInactivityTimer; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::DispatchL +// we should only come here with some synchronous requests +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::DispatchL(TRfeInArgs& aIn, TRfeOutArgs& aOut) + { + + switch(aIn.iOpCode) + { + case EFsIoctl: + DEBUGSTRING(("IOCTL")); + DoIoctlL(static_cast(aIn), + aOut); + break; + + case EFsRoot: + DEBUGSTRING(("ROOT")); + DoRootL(static_cast(aIn), + static_cast(aOut)); + break; + + case ESetAttr: + DEBUGSTRING(("SETATTR")); + DoSetAttrL(static_cast(aIn), + aOut); + break; + + default: + DEBUGSTRING(("WHAT??? - %d", aIn.iOpCode)); + User::Leave(KErrArgument); + break; + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::FullNameLC +// ---------------------------------------------------------------------------- +// +HBufC* CRsfwFileEngine::FullNameLC(CRsfwFileEntry& aFe) + { + HBufC* fn = aFe.FullNameLC(); + return fn; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::FullNameL +// ---------------------------------------------------------------------------- +// +HBufC* CRsfwFileEngine::FullNameL(CRsfwFileEntry& aFe) + { + HBufC* fn = FullNameLC(aFe); + CleanupStack::Pop(fn); + return fn; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::SetupAttributes +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::SetupAttributes(CRsfwFileEntry& aFe) + { + DEBUGSTRING(("CRsfwFileEngine::SetupAttributes")); + // Construct the attributes for a newly created file or directory, + // or a file that that was locally modified and just written to the server, + // based on local knowledge of time and file size. + // We assume that either the file is cached or it is an empty file. + // We do not touch the local or protection attributes. + + TUint att; + + // Assume that the file type has already been setup + if (aFe.Type() == KNodeTypeDir) + { + att = KEntryAttDir; + } + else + { + att = 0; + } + + TTime time; + if (aFe.IsCached()) + { + TDesC* cacheNamep = aFe.CacheFileName(); + RFile f; + if (f.Open(iFs, *cacheNamep, EFileShareAny) == KErrNone) + { + // attribute bits + TUint a; + f.Att(a); + + att |= a & KEntryAttReadOnly; + + if (aFe.Type() == KNodeTypeDir) + { + aFe.SetSize(0); + } + else + { + if (aFe.IsFullyCached()) + { + // size + TInt siz; + f.Size(siz); + DEBUGSTRING(("File is fully cached, setting size to %d", siz)); + aFe.SetSize(siz); + aFe.SetCachedSize(siz); + } + else + { + DEBUGSTRING(("File is not fully cached, not touching the size")); + // file is not fully cached + // the size cannot be set from the local cache container + } + } + // modification time + f.Modified(time); + + f.Close(); + aFe.iUseCachedData = ETrue; + } + else + { + // No cache + aFe.SetSize(0); + time.HomeTime(); + } + + } + else + { + // No cache + aFe.SetSize(0); + time.HomeTime(); + } + + aFe.SetAtt(att); + + aFe.SetModified(time); + aFe.SetAttribValidationTime(); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::MakeDirectoryEntry +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::MakeDirectoryEntry(CRsfwFileEntry& aFe, TDirEnt& aDirEnt) + { + DEBUGSTRING(("CRsfwFileEngine::MakeDirectoryEntry")); + DEBUGSTRING16(("name %S, att %d, size %d", aFe.Name(), aFe.Att(), aFe.Size()));; + aDirEnt.Clear(); + aDirEnt.iName.Copy(*aFe.Name()); + aDirEnt.iAttr.iAtt = aFe.Att(); + aDirEnt.iAttr.iSize = aFe.Size(); + aDirEnt.iAttr.iModified = aFe.Modified(); + aDirEnt.iAttr.iUid3 = aFe.iUid; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::UpdateDirectoryContainerL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::UpdateDirectoryContainerL(CRsfwFileEntry& aFe) + { + // Construct the directory container based on + // file table information + DEBUGSTRING16(("Update directory container of %d (%S)", aFe.Fid().iNodeId, aFe.Name())); + + TDesC* cacheNamep = aFe.CacheFileName(); + if (!cacheNamep) + { + // There was no prior cache. + DEBUGSTRING(("Cache missing!")); + User::Leave(KErrGeneral); + } + + RFile f; + CleanupClosePushL(f); + User::LeaveIfError(f.Replace(iFs, + *cacheNamep, + EFileShareAny | EFileWrite)); + RFileWriteStream fStream(f); + CleanupClosePushL(fStream); + + RPointerArray* kidsp = aFe.Kids(); + TInt i; + if (!(iVolume->iVolumeTable->EnsureCacheCanBeAddedL( + sizeof(TEntry) * kidsp->Count()))) + { // pessimistic estimate + User::Leave(KErrDiskFull); + } + for (i = 0; i < kidsp->Count(); i++) + { + CRsfwFileEntry* kidFep = (*kidsp)[i]; + TDirEnt dirEnt; + MakeDirectoryEntry(*kidFep, dirEnt); + dirEnt.ExternalizeL(fStream); + } + CleanupStack::PopAndDestroy(2, &f); // f + + aFe.ResetLocallyDirty(); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::DataChanged +// ---------------------------------------------------------------------------- +// +TInt CRsfwFileEngine::DataChanged(const CRsfwDirEntAttr& aOldAttr, + const CRsfwDirEntAttr& aNewAttr) + { + // Based on attributes or metadata in general, + // tell whether the actual data, if cached, + // should be updated + if (aOldAttr.Att() == KEntryAttDir) + { + // use Last Modified (a weak entity tag) + if (aOldAttr.Modified() == aNewAttr.Modified()) + { + return EFalse; + } + else + { + return ETrue; + } + } + else + { + // use ETags if available + // a strong entity tag + if (aOldAttr.ETag() && aNewAttr.ETag()) + { + if (*aOldAttr.ETag() == *aNewAttr.ETag()) + { + return EFalse; + } + else + { + return ETrue; + } + } + + // use Last Modified (a weak entity tag) + // we assume it's file and compare also iSize... + if ((aOldAttr.Modified() == aNewAttr.Modified()) && + (aOldAttr.Size() == aNewAttr.Size())) + { + return EFalse; + } + else + { + return ETrue; + } + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::UseCachedData +// ---------------------------------------------------------------------------- +// +TBool CRsfwFileEngine::UseCachedData(CRsfwFileEntry& aFe) + { + if (!Disconnected()) + { + return aFe.UseCachedData(); + } + else + { + return ETrue; + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::UseCachedAttributes +// ---------------------------------------------------------------------------- +// +TBool CRsfwFileEngine::UseCachedAttributes(CRsfwFileEntry& aFe) + { + if (!Disconnected()) + { + if (aFe.Type() == KNodeTypeDir) + { + return iFileTable->Volume()->iVolumeTable-> + IsCachedAttrStillValid(aFe.iAttribValidation); + } + else + { // file + return iFileTable->Volume()->iVolumeTable-> + IsCachedDataStillValid(aFe.iAttribValidation); + + } + } + else + { + return ETrue; + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::GetAttributesL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::GetAttributesL(CRsfwFileEntry& aFe, + CRsfwDirEntAttr*& aAttr, + TUint aNodeType, + CRsfwRfeStateMachine* aCaller) + { + // Gets attributes for File Entry aFe. + // Uses either cached attributes (if they are still deemed to be valid), or + // fetches the attributes from the server*/ + DEBUGSTRING(("GetAttributesL")); + if ((aFe.Type() == aNodeType) && UseCachedAttributes(aFe)) + { + // Nothing to do + + if (aFe.IsOpenedForWriting()) + { + // update attributes when we are writing to the file + DEBUGSTRING(("volatile attributes")); + SetupAttributes(aFe); + } + else + { + DEBUGSTRING(("using cached attributes")); + } + aCaller->HandleRemoteAccessResponse(0, KErrNone); // "file exists" + } + else + { + // Refresh attributes + UpdateAttributesL(aFe, aAttr, aNodeType, aCaller); + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::UpdateAttributesL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::UpdateAttributesL(CRsfwFileEntry& aFe, + CRsfwDirEntAttr*& aAttr, + TUint aNodeType, + MRsfwRemoteAccessResponseHandler* aCaller) + { + // UpdateAttributes doesn't attempt to use cached attributes + HBufC* path = FullNameLC(aFe); + TPtr p = path->Des(); + DEBUGSTRING16(("UpdateAttributesL of '%S'", &p)); + + + UpdateAttributesL(*path, aAttr, aNodeType, aCaller); + + CleanupStack::PopAndDestroy(path); // path + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::UpdateAttributesL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::UpdateAttributesL(TDesC& aPath, + TDesC& aName, + CRsfwDirEntAttr*& aAttr, + TUint aNodeType, + MRsfwRemoteAccessResponseHandler* aCaller) + { + HBufC* pn = HBufC::NewLC(KMaxPath); + TPtr pnPtr = pn->Des(); + + if (aPath.Length()) + { + pnPtr.Copy(aPath); + pnPtr.Append('/'); + } + pnPtr.Append(aName); + + DEBUGSTRING16(("UpdateKidAttributes of '%S'", &pnPtr)); + + UpdateAttributesL(pnPtr, aAttr, aNodeType, aCaller); + + CleanupStack::PopAndDestroy(pn); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::UpdateAttributesL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::UpdateAttributesL(TDesC& aFullPath, + CRsfwDirEntAttr*& aAttr, + TUint aNodeType, + MRsfwRemoteAccessResponseHandler* aCaller) + { + + // If we have "recently" found out that this file/dir does NOT exist + // we cache even this negative result. Time limit is cache expiry for + // directory attributes + if ((aFullPath.Length() > 0) && // do not compare root folder (always exists) + (iLastFailedLookup == aFullPath) && + (iFileTable->Volume()->iVolumeTable-> + IsCachedAttrStillValid(iLookupTime))) + { + if (aNodeType == KNodeTypeDir) + { + aCaller->HandleRemoteAccessResponse(0, KErrPathNotFound); + } + else if (aNodeType == KNodeTypeFile) + { + aCaller->HandleRemoteAccessResponse(0, KErrNotFound); + } + return; + + } + + if (!Disconnected()) + { + if (aNodeType == KNodeTypeDir) + { + RemoteAccessL()->GetDirectoryAttributesL(aFullPath, aAttr, aCaller); + } + else if (aNodeType == KNodeTypeFile) + { + RemoteAccessL()->GetFileAttributesL(aFullPath, aAttr, aCaller); + } + } + else + { + User::Leave(KErrNotFound); + } + + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::CreateContainerFileL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::CreateContainerFileL(CRsfwFileEntry& aFe) + { + // Create a container file for the Fid. + // If the cache file already exists, it will be deleted + + RFile f; + HBufC* cachePath = HBufC::NewMaxLC(KMaxPath); + TPtr pathPtr = cachePath->Des(); + BuildContainerPathL(aFe, pathPtr); + + TInt err = f.Replace(iFs, *cachePath, EFileShareAny | EFileWrite); + f.Close(); + if (err != KErrNone) + { + DEBUGSTRING(("Error when creating container file! err=%d", err)); + User::Leave(KErrGeneral); + } + aFe.SetCacheFileName(cachePath); + CleanupStack::PopAndDestroy(cachePath); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::FetchAndCacheL +// ---------------------------------------------------------------------------- +// +TUint CRsfwFileEngine::FetchAndCacheL(CRsfwFileEntry& aFe, + TInt aFirstByte, + TInt* aLength, + RPointerArray* aDirEntsp, + CRsfwRfeStateMachine* aCaller) + { + // Fetch a file from the remote store and decrypt it if necessary. + // The assumption is that the file has not yet been fetched + // or has been cached up to the byte indicated by (aFe.iCachedSize - 1) + // and filling the cache will continue linearly + // i.e. aFirstByte = 0 || aFirstByte = aFe.iCachedSize + // Access modules can fetch more than requested, so aLastByte might change. + + DEBUGSTRING(("Fetch fid %d, bytes %d - %d", + aFe.Fid().iNodeId, + aFirstByte, + aFirstByte + *aLength)); + + TUint transactionId = 0; + RFile f; + HBufC* fullName = NULL; + HBufC* cacheName = HBufC::NewMaxLC(KMaxPath); + TPtr cachePtr = cacheName->Des(); + TInt err; + + TInt usedCache = iVolume->iVolumeTable->TotalCachedSize(); + + // This much will be added to the cache by this fetch + if (!iVolume->iVolumeTable->EnsureCacheCanBeAddedL(*aLength)) + { + User::Leave(KErrDiskFull); + } + + if (!Disconnected()) + { + if (aFe.CacheFileName()) + { + // modify an existing cachefile ... + cachePtr = *(aFe.CacheFileName()); + + if (aFe.Type() == KNodeTypeFile) + { + // If the cache file exists, + // we will just continue filling it... + err = f.Open(iFs, *cacheName, EFileShareAny | EFileWrite); + if (err) + { + User::LeaveIfError(f.Replace(iFs, + *cacheName, + EFileShareAny | EFileWrite)); + } + } + else + { + User::LeaveIfError(f.Replace(iFs, + *cacheName, + EFileShareAny | EFileWrite)); + } + } + else + { + // create a new cache file + CreateContainerFileL(aFe, cachePtr, f); + } + + CleanupClosePushL(f); + fullName = FullNameLC(aFe); + if (aFe.Type() == KNodeTypeDir) + { + transactionId = GetDirectoryL(aFe, + *fullName, + f, + aDirEntsp, + aCaller); + } + else if (aFe.Type() == KNodeTypeFile) + { + f.Close(); + transactionId = RemoteAccessL()->GetFileL(*fullName, + *cacheName, + aFirstByte, + aLength, + 0, + aCaller); + } + + // fullName, f (duplicate close in the case of files) + CleanupStack::PopAndDestroy(2, &f); + } + CleanupStack::PopAndDestroy(cacheName); + return transactionId; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::RequestConnectionStateL +// ---------------------------------------------------------------------------- +// +TUint CRsfwFileEngine::RequestConnectionStateL(TUint aConnectionState, + CRsfwRfeStateMachine* aCaller) + { + DEBUGSTRING16(("CRsfwFileEngine::RequestConnectionStateL %d", aConnectionState)); + DEBUGSTRING16(("current connection state is %d", iConnectionState)); + TUint transactionId = 0; + if (aConnectionState != iConnectionState) + { + switch (aConnectionState) + { + case KMountNotConnected: + DisconnectL(); + break; + case KMountStronglyConnected: + transactionId = ConnectL(ETrue, aCaller); + break; + + default: + break; + } + } + // else does not do anything (if iConnectionState == aConnectionState) + return transactionId; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::EnteredConnectionStateL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::EnteredConnectionStateL(TUint aConnectionState, + TBool aRequested) + { + DEBUGSTRING16(("CRsfwFileEngine::EnteredConnectionStateL %d", aConnectionState)); + DEBUGSTRING16(("current connection state is %d", iConnectionState)); + if (aConnectionState != iConnectionState) + { + iConnectionState = aConnectionState; + iVolume->ConnectionStateChanged(iConnectionState); + + switch (aConnectionState) + { + case KMountNotConnected: + if (!aRequested) + { + iRemoteAccess->Cancel(0); + } + break; + + case KMountStronglyConnected: + if (aRequested) + { + if (iLockManager) + { + iLockManager->PopulateExternalLockTokenCacheL(iRootFep); + } + } + break; + + default: + break; + } + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::ConnectionState +// ---------------------------------------------------------------------------- +// +TUint CRsfwFileEngine::ConnectionState() + { + return iConnectionState; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::LockManager +// ---------------------------------------------------------------------------- +// +CRsfwLockManager* CRsfwFileEngine::LockManager() + { + return iLockManager; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::SetPermanenceL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::SetPermanenceL(TBool aPermanence) + { + iFileTable->SetPermanenceL(aPermanence); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::Disconnected +// ---------------------------------------------------------------------------- +// +TBool CRsfwFileEngine::Disconnected() + { + return (iConnectionState == KMountNotConnected); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::WriteDisconnected +// ---------------------------------------------------------------------------- +// +TBool CRsfwFileEngine::WriteDisconnected() + { + // This also encompasses disconnected mode + return (iConnectionState != KMountStronglyConnected); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::AddToCacheL +// ---------------------------------------------------------------------------- +// +TInt CRsfwFileEngine::AddToCacheL(CRsfwFileEntry& aFe, + RPointerArray* aDirEnts, + CRsfwFileEngine *aFileEngine, + TUint cachedSize) + { + // returns the size of the cached data + RFs fs = CRsfwRfeServer::Env()->iFs; + TInt err; + TInt kidsCount = 0; + TInt containerSize = cachedSize; + // holds true for files, will be overwritten for directories + + if (aFe.Type() == KNodeTypeDir) + { + // *********** originally from CRsfwFileEngine::GetDirectoryL() + // ********************************************************** + // Unmark and mark kids only when getdirectory returns KErrNone + // otherwise (i.e. KErrUpdateNotRequired) let's just keep + // the cached kids... + aFe.UnmarkKids(); + + RApaLsSession lsSession; + User::LeaveIfError(lsSession.Connect()); + CleanupClosePushL(lsSession); + + RFileWriteStream fStream; + // Dump to the local cache + User::LeaveIfError( + fStream.Open(fs, + *(aFe.CacheFileName()), + EFileWrite | EFileShareAny)); + CleanupClosePushL(fStream); + + containerSize = fStream.Sink()->SizeL(); + TInt i; + TLex lex; + for (i = 0; i < aDirEnts->Count(); i++) + { + CRsfwDirEnt* d = (*aDirEnts)[i]; + TUid appUid; + // For each TDirEnt we just read... + // ... if the server returned content-type + if (d->Attr()->MimeType() && d->Attr()->MimeType()->Length()) + { + err = lsSession.AppForDataType(*(d->Attr()->MimeType()), + appUid); + if (err == KErrNone) + { + d->Attr()->SetUid(appUid); + } + } + + d->Attr()->SetAttFlags(KEntryAttRemote); + CRsfwFileEntry* kidFep = aFe.FindKidByName(*d->Name()); + if (kidFep) + { + // We already know this kid + // However we must check whether the kid has been modified + CRsfwDirEntAttr* oldAttr = CRsfwDirEntAttr::NewLC(); + kidFep->GetAttributesL(*oldAttr); + if (DataChanged(*oldAttr, *d->Attr())) + { + kidFep->RemoveCacheFile(); + } + CleanupStack::PopAndDestroy(oldAttr); + if (kidFep->IsFullyCached()) + { + // Mark the kid as cached + d->Attr()->ResetAttFlags(KEntryAttRemote); + } + // as this entry is "used", move it to the back of metadata LRU list + iVolume->iVolumeTable->MoveToTheBackOfMetadataLRUPriorityListL(kidFep); + } + + // As a side effect, + // insert this kid into the file table and + // set its attributes + if (!kidFep) + { + if (!iVolume->iVolumeTable->EnsureMetadataCanBeAddedL(&aFe)) + { + User::Leave(KErrNoMemory); + } + kidFep = CRsfwFileEntry::NewL(*d->Name(), &aFe); + // Attach the new kid + aFileEngine->iFileTable->AddL(kidFep); + aFe.AddKid(*kidFep); + } + + kidFep->Mark(); + + // set attributes if getting directory listing also supports getting file attributes + if (DirectoryListingContainsFileMetadata()) + { + kidFep->SetAttributesL(*d->Attr(), ETrue); + } + else + { + kidFep->SetAttributesL(*d->Attr(), EFalse); + } + + TDirEnt dirEnt; + MakeDirectoryEntry(*kidFep, dirEnt); + dirEnt.ExternalizeL(fStream); + kidsCount++; + } + + aFe.DropUnmarkedKidsL(); + + containerSize = fStream.Sink()->SizeL(); + // assumes that this fetch will write the whole directory, + + // i.e. there is no partial fetching for the directories + if(!iFileTable->Volume()->iVolumeTable-> + EnsureCacheCanBeAddedL(containerSize)) + { + User::Leave(KErrDiskFull); + } + fStream.CommitL(); + + CleanupStack::PopAndDestroy(2, &lsSession); // fStream, lsSession + + // if the directory appeared to be childless add it to metadata LRU list + if ( aDirEnts->Count() == 0 ) + { + iVolume->iVolumeTable->AddToMetadataLRUPriorityListL(&aFe, ECachePriorityNormal); + } + + }// if directory + + // assumes the files are cached in continuos chunks, + // i.e. always cached up to the last byte fetched + aFe.SetCachedSize(containerSize); + + aFe.SetCached(ETrue); + + // We have to update locally dirty bit for the parent container + if (aFe.Parent()) + { + aFe.Parent()->SetLocallyDirty(); + } + // But the object itself cannot be remotely dirty any more + aFe.ResetRemotelyDirty(); + + // *** from CRsfwFileEngine::DoFetch *** + if (aFe.Type() == KNodeTypeDir) + { + // the reason why kidsCount may be different than aFe.Kids.Count is that for big directories + // some kids could have been removed when adding the others to memory. this is due to memory management cap. + // however this should not happen so often + aFe.KidsCount() == kidsCount ? aFe.iUseCachedData = ETrue : aFe.iUseCachedData = EFalse; + } + else + { + aFe.iUseCachedData = ETrue; + } + + return containerSize; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::RemoteAccessL +// ---------------------------------------------------------------------------- +// +CRsfwRemoteAccess* CRsfwFileEngine::RemoteAccessL() + { + DEBUGSTRING(("CRsfwFileEngine::RemoteAccessL")); + if (!iRemoteAccess) + { + User::Leave(KErrNotReady); + } + + // Prevent the inactivity timer from triggering + // in the middle of a remote access operation + StopInactivityTimer(); + + return iRemoteAccess; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::OperationCompleted +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::OperationCompleted() + { + DEBUGSTRING(("File engine operation completed")); + if (iVolume->iVolumeTable->iPermanence) + { + iFileTable->SaveMetaDataDelta(); + } + + if (iLockManager && (iLockManager->LockedCount() == 0)) + { + // Start timer only if we don't have files open for writing + StartInactivityTimer(); + } + + iVolume->OperationCompleted(); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::CancelTransaction +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::CancelTransaction(TUint iTransactionId) + { + DEBUGSTRING(("CRsfwFileEngine::CancelTransactionL")); + if (iRemoteAccess) + { + iRemoteAccess->Cancel(iTransactionId); + } + + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::CancelTransaction +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::CancelTransactionL(TDesC& aPathName) + { + DEBUGSTRING(("CRsfwFileEngine::CancelTransactionL")); + TPtrC testPtr; + testPtr.Set(aPathName.Right(aPathName.Length() - 3)); + HBufC* cancelPath = HBufC::NewLC(KMaxPath); + TPtr cancelPathPtr = cancelPath->Des(); + // change '\\' to '/' so the path matches + TLex parser(testPtr); + TChar theChar; + + for (int i = 0; i < testPtr.Length(); i++) + { + theChar = parser.Get(); + if (theChar == 0) + { + break; + } + // assumes that the input string always has "\\" and not just "\" + // this is true as the input is a file path + if (theChar != '\\') + { + cancelPathPtr.Append(theChar); + } + else + { + cancelPathPtr.Append('/'); + } + } + + if (iRemoteAccess) + { + iRemoteAccess->Cancel(*cancelPath); + } + + CleanupStack::PopAndDestroy(cancelPath); + + } + + + + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::SetFailedLookup +// Caches the last failed lookup result +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::SetFailedLookup(TDesC& aPath, TDesC& aKidName) + { + iLastFailedLookup = aPath; + iLastFailedLookup.Append('/'); + iLastFailedLookup.Append(aKidName); + iLookupTime.HomeTime(); + DEBUGSTRING16(("SetFailedLookup: %S", &iLastFailedLookup)); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::ResetFailedLookup +// Clears the last failed lookup result +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::ResetFailedLookup() + { + DEBUGSTRING16(("ResetFailedLookup: %S", &iLastFailedLookup)); + iLastFailedLookup.Zero(); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::Volume +// ---------------------------------------------------------------------------- +// +CRsfwVolume* CRsfwFileEngine::Volume() + { + return iVolume; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::PrepareCacheL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::PrepareCacheL() + { + // make sure the file cache (of this volume) exists + iCacheRoot.Copy(CRsfwRfeServer::Env()->iCacheRoot); + iCacheRoot.Append('C'); + iCacheRoot.AppendNum(iVolume->iMountInfo.iMountStatus.iVolumeId); + iCacheRoot.Append('\\'); + + if (! BaflUtils::FileExists(iFs, iCacheRoot)) + { + // There was no prior cache directory + TInt err = iFs.MkDirAll(iCacheRoot); + DEBUGSTRING(("Cache directory created with err=%d", err)); + User::LeaveIfError(err); + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::GetDirectoryL +// ---------------------------------------------------------------------------- +// +TUint CRsfwFileEngine::GetDirectoryL(CRsfwFileEntry& /*aFe*/, + TDesC& aFullName, + RFile& /*aF*/, + RPointerArray* aDirEntsp, + MRsfwRemoteAccessResponseHandler* aCaller) + { + return RemoteAccessL()->GetDirectoryL(aFullName, *aDirEntsp, aCaller); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::BuildContainerPathL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::BuildContainerPathL(CRsfwFileEntry& aFe, TDes& aPath) + { + if (aPath.MaxLength() < (aPath.Length() + iCacheRoot.Length())) + { + aPath.Copy(iCacheRoot); + } + else + { + User::Leave(KErrOverflow); + } + + ApplyMultiDirCacheL(aPath); + // This filename tagging based on container type is just for convenience + if (aFe.Type() == KNodeTypeFile) + { + aPath.Append('F'); + } + else + { + aPath.Append('D'); + } + aPath.AppendNum((TInt)aFe.Fid().iNodeId); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::ApplyMultiDirCachePathL +// Due to Symbian performance problems with huge directories, items will not +// be stored in one directory in the cache. +// Now instead one dir like: +// C:\system\data\rsfw_cache\C16 +// there will be dirs like: +// C:\system\data\rsfw_cache\C16\M0 +// C:\system\data\rsfw_cache\C16\M1 +// ... and so on +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::ApplyMultiDirCacheL(TDes& aPath) + { + // maximum number of items in a single dir in the cache + const TInt KRsfwMaxItemsInDir = 100; + TInt i; + // this loop will surely break (or leave) at some point + for ( i = 0; ; i++ ) + { + // create path like "C:\system\data\rsfw_cache\C16\M0" + HBufC* trypath = HBufC::NewMaxL(KMaxPath); + TPtr pathPtr = trypath->Des(); + pathPtr.Copy(aPath); + pathPtr.Append('M'); + pathPtr.AppendNum(i); + pathPtr.Append('\\'); + + // check whether dir exists and if so, how many items it contains + CDir* dir = NULL; + // note that KEntryAttDir att means files & directories + TInt err = iFs.GetDir(*trypath, KEntryAttDir, ESortNone, dir); + if ( err == KErrNone ) + { + // count the items + TInt count = dir->Count(); + delete dir; + dir = NULL; + + //limit is not exceeded -> return the path + if ( count < KRsfwMaxItemsInDir ) + { + aPath.Copy(pathPtr); + delete trypath; + break; + } + // limit exceeded -> let's try the next dir + else + { + delete trypath; + continue; + } + } + else if ( err == KErrPathNotFound ) + { + // create dir and return the path to empty dir + err = iFs.MkDir(*trypath); + if (!err) + { + aPath.Copy(pathPtr); + delete trypath; + } + else + { + delete trypath; + DEBUGSTRING(("Error when creating cache dir! err=%d", err)); + User::Leave(KErrGeneral); + } + + break; + } + else + { + delete trypath; + DEBUGSTRING(("Cache directory cannot be created! err=%d", err)); + User::Leave(KErrGeneral); + } + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::CreateContainerFileL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::CreateContainerFileL(CRsfwFileEntry& aFe, + TDes& aPath, + RFile& aF) + { + // Create a container file for the Fid. + // If the cache file already exists, it will be deleted + + BuildContainerPathL(aFe, aPath); + + TInt err = aF.Replace(iFs, aPath, EFileShareAny | EFileWrite); + if (err != KErrNone) + { + User::Leave(KErrGeneral); + } + aF.Close(); + + aFe.SetCacheFileName(&aPath); + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::DoIoctlL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::DoIoctlL(TRfeIoctlInArgs& aIn, TRfeOutArgs& /* aOut */) + { + TFid fidp = aIn.iFid; + TInt cmd = aIn.iCmd; + + TInt err = KErrNone; + + DEBUGSTRING(("ioctl fid %d - command=%d, data=%d", + fidp.iNodeId, + cmd, + aIn.iData32[0])); + + CRsfwFileEntry* fep = iFileTable->Lookup(fidp); + if (fep) + { + switch (cmd) + { + case ERemoteFsIoctlRefresh: + + + if (fep->Type() == KNodeTypeFile) + { + + fep->SetCacheFileName(NULL); + fep->SetCached(EFalse); + + // There is a change in the parent's container + fep->Parent()->SetLocallyDirty(); + } + break; + + case ERemoteFsHighCachePriority: + default: + err = KErrArgument; + break; + } + } + else + { + err = KErrNotFound; + } + + if (err != KErrNone) + { + User::Leave(err); + } + + return; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::DoRootL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::DoRootL(TRfeRootInArgs& /* aIn */, TRfeRootOutArgs& aOut) + { + SetupRootL(iVolume->iVolumeTable->iPermanence); + aOut.iFid.iVolumeId = iRootFid->iVolumeId; + aOut.iFid.iNodeId = iRootFid->iNodeId; + return; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::DoSetAttrL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::DoSetAttrL(TRfeSetAttrInArgs& aIn, TRfeOutArgs& /* aOut */) + // We cannot really set anything but this is the way to implement this + // note that if this is implemented, it should really be a state machine + { + TInt err = KErrNone; + TFid fidp = aIn.iFid; +#ifdef _DEBUG + TDirEntAttr* attrp = &(aIn.iAttr); +#endif + + DEBUGSTRING(("setting attributes of fid %d, attr=0x%x, size=%d, time=", + fidp.iNodeId, + attrp->iAtt, + attrp->iSize)); + DEBUGTIME((attrp->iModified)); + + // Get the file or directory to setattr + CRsfwFileEntry* fep = iFileTable->Lookup(fidp); + if (fep) + { + err = KErrNotSupported; + } + else + { + err = KErrNotFound; + } + + User::Leave(err); + return; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::SetupRootL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::SetupRootL(TBool aPermanence) + { + _LIT(KRootPath, "."); // dummy + + if (!iRootFid) + { + CRsfwFileEntry* root = NULL; + TInt err; + if (aPermanence) + { + TRAP(err, root = iFileTable->LoadMetaDataL()); + } + if (err == KErrCorrupt) + { + DEBUGSTRING(("Metadata corrupted! Recreating cache file...")); + // corrupted cache file, recreate filetable and cache file + delete iFileTable; + iFileTable = NULL; + CleanupCorruptedCacheL(); + PrepareCacheL(); + iFileTable = CRsfwFileTable::NewL(iVolume, iCacheRoot); + } + if (!aPermanence || (err != KErrNone)) + { + root = CRsfwFileEntry::NewL(KRootPath, NULL); + // Insert root into the file table + iFileTable->AddL(root); + root->SetType(KNodeTypeDir); + } + if (aPermanence) + { + iFileTable->SaveMetaDataDelta(); + } + iRootFep = root; + iRootFid = &(root->Fid()); + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::CleanupCorruptedCacheL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::CleanupCorruptedCacheL() + { + // delete everything from the cache + TFileName cachepath; + cachepath.Copy(iCacheRoot); + CFileMan* fileMan = CFileMan::NewL(iFs); + fileMan->Delete(cachepath, CFileMan::ERecurse); + delete fileMan; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::ConnectL +// ---------------------------------------------------------------------------- +// +TUint CRsfwFileEngine::ConnectL(TBool aRestart, CRsfwRfeStateMachine* aCaller) + { + // Assume parameter format: + // protocol://username:password@server:port/rootdir or + // The ":password", ":port", and "[/]rootdir" can be omitted. + // If the length of password parameter is bigger than 1, + // it overrides the one in uri, if any. + // Characters can be quoted with % format + TUint transactionId = 0; + + if (iRemoteAccess) + { + // We already have a remote accessor + if (aRestart) + { + // Restarting + delete iLockManager; + iLockManager = NULL; + delete iRemoteAccess; + iRemoteAccess = NULL; + } + else + { + User::Leave(KErrAlreadyExists); + } + } + + DEBUGSTRING16(("ConnectL(): '%S'", + &iVolume->iMountInfo.iMountConfig.iUri)); + + TUriParser uriParser; + User::LeaveIfError(uriParser.Parse(iVolume->iMountInfo.iMountConfig.iUri)); + + TPtrC userName; + TPtrC password; + TPtrC friendlyName; + + if (uriParser.IsPresent(EUriUserinfo)) + { + TPtrC userInfo(uriParser.Extract(EUriUserinfo)); + // Split the user info into user name and password (seprated by ':') + TInt pos = userInfo.Locate(':'); + if (pos != KErrNotFound) + { + password.Set(userInfo.Mid(pos + 1)); + userName.Set(userInfo.Left(pos)); + } + else + { + userName.Set(userInfo); + } + } + + HBufC* userNameBuf = NULL; + if (!userName.Length() && + iVolume->iMountInfo.iMountConfig.iUserName.Length()) + { + // separate user name overwrites the username embedded in the URI + userName.Set(iVolume->iMountInfo.iMountConfig.iUserName); + } + + HBufC* passwordBuf = NULL; + if (!password.Length() && + (iVolume->iMountInfo.iMountConfig.iPassword.Length() > 1)) + { + // separate password overwrites the password embedded in the URI + password.Set(iVolume->iMountInfo.iMountConfig.iPassword); + } + + friendlyName.Set(iVolume->iMountInfo.iMountConfig.iName); + + TPtrC scheme(uriParser.Extract(EUriScheme)); + HBufC8* protocol = HBufC8::NewLC(scheme.Length()); + TPtr8 protocolPtr = protocol->Des(); + protocolPtr.Copy(scheme); + iRemoteAccess = CRsfwRemoteAccess::NewL(protocolPtr); + CleanupStack::PopAndDestroy(protocol); + + // user name and password are conveyed separately from the URI + CUri* uri = CUri::NewLC(uriParser); + uri->RemoveComponentL(EUriUserinfo); + + // leaves if error + iRemoteAccess->SetupL(this); + transactionId = iRemoteAccess-> + OpenL(uri->Uri(), + friendlyName, + userName, + password, + iVolume->iMountInfo.iMountConfig.iAuxData, + aCaller); + + CleanupStack::PopAndDestroy(uri); + if (passwordBuf) + { + CleanupStack::PopAndDestroy(passwordBuf); + } + if (userNameBuf) + { + CleanupStack::PopAndDestroy(userNameBuf); + } + + // lock manager can be created before we know whether connecting was + // succesful - however it must be deleted upon unsuccesful connect + if (!iLockManager) + { + iLockManager = CRsfwLockManager::NewL(iRemoteAccess); + } + + if ((iInactivityTimeout > 0) && !iInactivityTimer) + { + iInactivityTimer = CPeriodic::NewL(CActive::EPriorityLow); + } + return transactionId; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::DisconnectL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::DisconnectL() + { + DEBUGSTRING(("CRsfwFileEngine::DisconnectL")); + if (iRemoteAccess) + { + iRemoteAccess->Cancel(0); + delete iRemoteAccess; + iRemoteAccess = NULL; + } + + if (iLockManager) + { + delete iLockManager; + iLockManager = NULL; + } + + + // Set open file count to zero + // If there are open files, after disconnecting we do not necessarily + // get close events. + // Note that this variable is not "dirty bit" (file has currently + // uncommitted modifications), so it is safe to set it to zero + TInt openfiles = iFileTable->OpenFileCount(); + iFileTable->UpdateOpenFileCount(-openfiles); + + EnteredConnectionStateL(KMountNotConnected, ETrue); + + // publish connection status when disconnecting + iVolume->iVolumeTable->PublishConnectionStatus(iVolume); + + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::StartInactivityTimer +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::StartInactivityTimer() + { + if (iInactivityTimer) + { + DEBUGSTRING(("inactivity timer started (%d us)", + iInactivityTimeout)); + iInactivityTimer->Cancel(); + TCallBack callBack(CRsfwFileEngine::InactivityTimerExpired, this); + iInactivityTimer->Start(iInactivityTimeout, + iInactivityTimeout, + callBack); + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::StopInactivityTimer +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::StopInactivityTimer() + { + DEBUGSTRING(("CRsfwFileEngine::StopInactivityTimer")); + if (iInactivityTimer) + { + DEBUGSTRING(("inactivity timer stopped")); + iInactivityTimer->Cancel(); + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::InactivityTimerExpired +// ---------------------------------------------------------------------------- +// +TInt CRsfwFileEngine::InactivityTimerExpired(TAny* aArg) + { + DEBUGSTRING(("CRsfwFileEngine::InactivityTimerExpired")); + CRsfwFileEngine* fileEngine = static_cast(aArg); + if (fileEngine->iFileTable->OpenFileCount() == 0) + { + fileEngine->StopInactivityTimer(); + TRAP_IGNORE(fileEngine->DisconnectL()); + // "Simulate" operation completion (which may result in RFE shutdown) + fileEngine->OperationCompleted(); + } + else + { + // if there are open files on this volume, just restart the inactivity timer + fileEngine->StartInactivityTimer(); + } + + return 0; + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::HandleRemoteAccessEventL +// ---------------------------------------------------------------------------- +// +void CRsfwFileEngine::HandleRemoteAccessEventL(TInt aEventType, + TInt aEvent, + TAny* /* aArg */) + { + DEBUGSTRING(("Handle remote access event: %d/%d in connection state %d", + aEventType, + aEvent, + iConnectionState)); + switch (aEventType) + { + case ERsfwRemoteAccessObserverEventConnection: + switch (aEvent) + { + case ERsfwRemoteAccessObserverEventConnectionDisconnected: + EnteredConnectionStateL(KMountNotConnected, EFalse); + break; + + case ERsfwRemoteAccessObserverEventConnectionWeaklyConnected: +#if 0 + // This event does not appear + EnteredConnectionStateL(KMountWeaklyConnected, EFalse); +#endif + break; + + case ERsfwRemoteAccessObserverEventConnectionStronglyConnected: +#if 0 + // This event does not appear + EnteredConnectionStateL(KMountStronglyConnected, EFalse); +#endif + break; + + default: + break; + } + break; + + default: + break; + } + } + +// ---------------------------------------------------------------------------- +// CRsfwFileEngine::PurgeFromCacheL +// ---------------------------------------------------------------------------- +// +TInt CRsfwFileEngine::PurgeFromCache(const TDesC& aPath) + { + // get the fid of the entry for which the cached data is removed + CRsfwFileEntry* targetFid = FetchFep(aPath); + if (!targetFid) + { + return KErrPathNotFound; + } + // only directories can be refreshed currently + if (targetFid->Type() != KNodeTypeDir) + { + return KErrArgument; + } + targetFid->SetCached(EFalse); + return KErrNone; + } + +CRsfwFileEntry* CRsfwFileEngine::FetchFep(const TDesC& aPath) + { + DEBUGSTRING16(("CRsfwFileEngine::FetchFep for file %S", &aPath)); + if (aPath.Length() <= 1) + { + DEBUGSTRING(("returning rootFep")); + return iRootFep; + } + else + { + TInt delimiterPos = aPath.LocateReverse(KPathDelimiter); + if (delimiterPos == (aPath.Length() - 1)) + { + // The path ends with a slash, + //i.e. this is a directory - continue parsing + TPtrC nextdelimiter; + nextdelimiter.Set(aPath.Left(delimiterPos)); + delimiterPos = nextdelimiter.LocateReverse(KPathDelimiter); + } + TPtrC entry(aPath.Right(aPath.Length() - (delimiterPos + 1))); + TPtrC path(aPath.Left(delimiterPos + 1)); + + // strip a trailing backslash if found + delimiterPos = entry.LocateReverse(KPathDelimiter); + if (delimiterPos == (entry.Length() - 1)) + { + TPtrC stripped(entry.Left(entry.Length() - 1)); + return (FetchFep(path)->FindKidByName(stripped)); + } + else + { + return (FetchFep(path)->FindKidByName(entry)); + } + + } + + } + +HBufC8* CRsfwFileEngine::GetContentType(TDesC& aName) + { + TInt err; + RApaLsSession lsSession; + err = lsSession.Connect(); + if (err) + { + return NULL; + } + + RFs fsSession; + err = fsSession.Connect(); + if (err) + { + lsSession.Close(); + return NULL; + } + fsSession.ShareProtected(); + TDataRecognitionResult dataType; + RFile theFile; + // the mode must mach the mode that is used in the file system plugin + // (EFileWrite|EFileShareAny) + err = theFile.Open(fsSession, aName, EFileWrite|EFileShareAny); + if (err) + { + lsSession.Close(); + fsSession.Close(); + return NULL; + } + err = lsSession.RecognizeData(theFile, dataType); + lsSession.Close(); + theFile.Close(); + fsSession.Close(); + if (err) + { + return NULL; + } + + return dataType.iDataType.Des8().Alloc(); + } + + // The purpose of these functions is to give capability info + // for the access protocol plugin used. + + // Currently this information is hard coded and takes into account webdav and upnp + // access modules. New function should be added to the access plugin api to get + // this information from the protocol module + + // whether getting the directory listing also gives reliable file metadata +TBool CRsfwFileEngine::DirectoryListingContainsFileMetadata() + { + _LIT(KUPnP, "upnp"); + if (iVolume->MountInfo()->iMountConfig.iUri.Left(4) == KUPnP) + { + return EFalse; + } + else + { + return ETrue; + } + + } +