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

/*
* 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:  File server interface class representing an open file.
*                Allows to access a specific remote file.
*
*/


// INCLUDE FILES
#include "rsfwfsfilecb.h"
#include "rsfwfsmountcb.h"

// -----------------------------------------------------------------------------
// CRsfwFsFileCB::CRsfwFsFileCB
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CRsfwFsFileCB::CRsfwFsFileCB()
    {
    iLastFlushFailed = ETrue;
    iPartialWriteSupported = ETrue;
    }
 
// Destructor
CRsfwFsFileCB::~CRsfwFsFileCB()
    {   
    // close the cache file first so that RFE can move/delete it if upload fails
    iContFile.Close(); 
    TUint flags = 0;
    if (iFileName)
        {
        if (!iLastFlushFailed) 
            {
            // Now the container file has been changed,
            // tell Remote File Engine to update it on the servers
            // RSessionL() should not leave here as the remote session surely is created by now...
            if (iAtt & KEntryAttModified) 
                {
                flags |= ECloseModified;
         
                // File was modified use, flush to write data to the server
                // We write the whole file always, if flush was never called we cannot
                // know whether partial write is supported.
                TRAP_IGNORE(static_cast<CRsfwFsMountCB&>
                    (Mount()).RSessionL()->Flush(iThisFid, 0, iCachedSize, iCachedSize));
                }
            else 
                {
                flags |= ECloseNotModified;
                }
                
            }
        else 
            {
            // flush was called and failed
            // do not try to flush again if the application closes the file
            // instead indicate this to the close state machine
            flags |= ECloseLastFlushFailed;
            }
        }
        
     // close will release the write lock if possible
     // and also allows user to save the file locally if flush failed
     TRAP_IGNORE(static_cast<CRsfwFsMountCB&>
              (Mount()).RSessionL()->CloseFile(iThisFid, flags));
    
    }
    
          
// -----------------------------------------------------------------------------
// CRsfwFsFileCB::RenameL
// Renames the file with the full file name provided. Because the full name of 
// the file includes the path, the function can also be used to move the file.
//
// It can be assumed that no other sub-session has access to the file:
// i.e. the file has not been opened in EFileShareAny share mode.
// It can also be assumed that the file has been opened for writing. 
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::RenameL(
    const TDesC& aNewName)
    {
   	static_cast<CRsfwFsMountCB&>(Mount()).RenameFidL(iParentFid, *iFileName, aNewName);
   	delete iFileName;
   	iFileName = NULL;
   	iFileName = aNewName.AllocL();
    }

// -----------------------------------------------------------------------------
// CRsfwFsFileCB::ReadL
// Reads a specified number of bytes from the open file starting at
// the specified position, and writes the result into a descriptor.
//    
// It can be assumed that aPos is inside the file and aLength > 0.
// The file should only be read up to its end regardless of
// the value of aPos + aLength. The number of bytes read should be stored
// in aLength on return.
//
// Implemented by sending FETCH request to Remote File Engine for the
// specified data and subsequently reading the data from the local cache file.
// Reading the local cache file sets aLength.
// Note that we want to keept the cache file continuos. If the requested data
// starts behind the end of the current cache file, the function sends FETCHDATA
// and Remote File Engine puts this data into a temp cache file valid only 
// for the duration of this operation if the caching mode is something else than 
// Whole File Caching
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::ReadL(
    TInt aPos,
    TInt& aLength,
    const TAny* /*aDes*/,
    const RMessagePtr2& aMessage)

    { 
          
    if (iCachedSize == 0) 
        {
        // iCachedSize possibly not up-to-date...
        iContFile.Size(iCachedSize);
        }
    
    HBufC8* data = HBufC8::NewMaxLC(aLength);
    TPtr8 buf(data->Des());

    if (aPos > iCachedSize) 
        {
        // Depending on the caching mode this type of request may bypass the 
        // normal cache file.
        TBool useTempCache = EFalse;
        HBufC* tmpCacheFile = HBufC::NewLC(KMaxPath);
        TPtr tmpCache(tmpCacheFile->Des());
        User::LeaveIfError(static_cast<CRsfwFsMountCB&>
            (Mount()).RSessionL()->FetchData(iThisFid, 
            								aPos, 
            								aPos + aLength - 1, 
            								tmpCache, 
            								useTempCache));
        if (useTempCache) 
        	{
        	// use "temp" in the same directory instead of the cache file
        	RFile tempFile;
        	TParse parser;
        	parser.Set(tmpCache, NULL, NULL);
        	HBufC* tempPath = HBufC::NewLC(KMaxPath);
            TPtr tempptr = tempPath->Des();
            tempptr.Append(parser.DriveAndPath());
            tempptr.Append(KTempFileName);
        	User::LeaveIfError(tempFile.Open(*(RFs* )Dll::Tls(),
                                         tempptr, EFileRead));
            CleanupStack::PopAndDestroy(tempPath);                       
        	CleanupClosePushL(tempFile);
       		User::LeaveIfError(tempFile.Read(buf, aLength));
       		CleanupStack::PopAndDestroy(&tempFile);
        	}
        else 
        	{
        	// read from the normal container file (Whole File Caching mode).
        	iContFile.Size(iCachedSize);
        	User::LeaveIfError(iContFile.Read(aPos, buf, aLength));
        	}
        CleanupStack::PopAndDestroy(tmpCacheFile); // tempcacheFile
        }
    else if ((aPos + aLength) > iCachedSize)
        {
        User::LeaveIfError(static_cast<CRsfwFsMountCB&>
       		(Mount()).RSessionL()->Fetch(iThisFid, 
       									 aPos, 
       									 aPos + aLength - 1, 
       									 iCachedSize)); 
       									  									 
        User::LeaveIfError(iContFile.Read(aPos, buf, aLength));
        }
    else 
        {
        User::LeaveIfError(iContFile.Read(aPos, buf, aLength));
        }
        
    aMessage.WriteL(0, buf, 0);     
    CleanupStack::PopAndDestroy(data);
    if (iCachedSize == iSize) 
    	{
    	// clear the remote attribute if the whole file has now been fetched
    	iAtt &= ~KEntryAttRemote;
    	}
    }
    
// -----------------------------------------------------------------------------
// CRsfwFsFileCB::WriteL
// Writes data to the open file. iModified and iSize are set by the file server 
// after this function has completed successfully.
//
// It can be assumed that aPos is within the file range and aLength > 0.
// When aPos + aLength is greater than the file size then the file should
// be enlarged using SetSizeL(). The number of bytes written should be
// returned through the argument aLength.
//
// Implemented by writing to the local cache file. First requests Remote File
// Engine whether there is enough space in the cache to write the file (this
// also calls SysUtil::DiskSpaceBelowCritical()). FE attempts to free space 
// from the cache if necessary Implementation notice: writes a large file in 
// chunks of 64K.
//  
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::WriteL(
    TInt aPos,
    TInt& aLength,
    const TAny* /*aDes*/,
    const RMessagePtr2& aMessage)
    {
    
    if (iCachedSize == 0) 
        {
        // iCachedSize possibly not up-to-date...
        iContFile.Size(iCachedSize);
        }
    
   // if flush was cancelled, but we come again to write, again set iLastFlushFailed to EFalse
    iLastFlushFailed = EFalse;
    
 	// We must first fetch the file to the local cache
 	// unless aPos = 0 and aLength => iSize 
 	// Note that if files are written to they cannot be partially cached
 	// as the whole file will be sent to the server and overwrites the old file.
 	// That is why we must ensure that the whole file is cached as soon as we
 	// get the first write.
 	// in subsequent writes iCachedSize will equal iSize
 	// This may eventually change if we start to use some kind of "delta-PUT".
 	if (!((aPos == 0) && (aLength >= iSize)) && iCachedSize < iSize && !iFetchedBeforeWriting)
 		{
 		User::LeaveIfError(static_cast<CRsfwFsMountCB&>
 				(Mount()).RSessionL()->Fetch(iThisFid, iCachedSize, iSize-1, iCachedSize));
 		iFetchedBeforeWriting = ETrue;
        }

    // make sure that a potential cache addition still fits into the cache
    TInt sizeToBeWritten = 0;
    
 	if (iSize > iCachedSize)
 	    {
 	    // when current Write is executed as a part of Copy operation
 	    // then iSize has been set to final size, even before any writing started
 	    sizeToBeWritten = iSize - iCachedSize;
 	    }
    else if (aPos + aLength > iCachedSize)
        {
        sizeToBeWritten = aPos + aLength - iCachedSize + iWrittenSize;
        }
             	 
 	TBool okToWrite;
 	User::LeaveIfError(static_cast<CRsfwFsMountCB&> 
 			(Mount()).RSessionL()->OkToWrite(iThisFid, 
 											sizeToBeWritten,
 											okToWrite));
 											 											
    if (!okToWrite) 
        {
        User::Leave(KErrDiskFull);
        }
    
    TInt anOffset = 0;
    HBufC8* data = HBufC8::NewMaxLC(aLength);
    TPtr8 buf(data->Des());
   
    aMessage.ReadL(0, buf, anOffset);
  
    User::LeaveIfError(iContFile.Write(aPos, *data, aLength));
    User::LeaveIfError(iContFile.Flush());
    CleanupStack::PopAndDestroy(data);

    // update iCachedSize and iWrittenSize if the container file size has grown
    if (aPos + aLength > iCachedSize)
        {
        iCachedSize = aPos + aLength;
        iWrittenSize = sizeToBeWritten;
        }
    
    // for flush() calls after this call:
    // set iFlushedSize to aPos, so that changes will be flushed
    if (iFlushedSize > aPos) 
        {
        iFlushedSize = aPos;
        }
    
    }

// -----------------------------------------------------------------------------
// CRsfwFsFileCB::SetSizeL
// Emply implementation, upper class already set iSize
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::SetSizeL(
    TInt aSize)
    {
    iReportedSize = aSize;
    // we cannot set the actual size of the remote file, but also we do not
    // return KErrNotSupported as that would cause problems with CFileMan
    // and File Manager, which use SetSize() as an optimization to set the 
    // target file size in copy.
    // Propably calling setsize() on remote files when for example just writing
    // to an existing file would cause weird results. 
    }

// -----------------------------------------------------------------------------
// CRsfwFsFileCB::SetEntryL
// Sets the attribute mask, iAtt, and the modified time of the file, iModified.
// If aMask|aVal does not equal zero, then aMask should be OR'ed with iAtt,
// whilst the inverse of aVal should be AND'ed with iAtt.
// If the modified flag is set in aMask then iModified should be set to aTime.
//
// Implemented by calling CRsfwFsMountCB::SetEntryL().
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::SetEntryL(
    const TTime& aTime,
    TUint aMask,
    TUint aVal)
    {
    static_cast<CRsfwFsMountCB&>(Mount()).SetEntryL(*iFileName,
                                                      aTime,
                                                      aMask,
                                                      aVal);                                             
    }

// -----------------------------------------------------------------------------
// CRsfwFsFileCB::FlushAllL
// Flushes, to disk, all cached file data (e.g. attributes, modification time,
// file size). The modified bit in the file attributes mask should be cleared if
// the flush was successful.
//
// File Server calls this before reading directory entries, getting an entry
// details for a directory entry etc. The idea is to make sure that all the
// information to be retrieved is up to date. We don't need to implement this,
// as our framework does not have buffers that could cause this problem. 
//
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::FlushAllL()
    {
    }

// -----------------------------------------------------------------------------
// CRsfwFsFileCB::FlushDataL
// Flushes, to disk, the cached information necessary for the integrity
// of recently written data, such as the file size.
//
// Called by File Server as a result of RFile::Flush() (if the file has been 
// modified). In our framework the file should be written to the server.
// We should also clear KEntryAttModified here.
//
// (other items were commented in a header).
//
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::FlushDataL()
    {
    TInt err = KErrNone;
    
    // close the container file to make sure all the local changes are cached
    iContFile.Close();
        
    // if flush was cancelled, but we come again to write, 
    // again set lastflushfailed to EFale	
    iLastFlushFailed = EFalse;
    
    // may attempt to write part of the file or whole file
    // also we may or may not know for sure whether the 
    // server supports partial write
starttoflush:
    if (!iPartialWriteSupported) 
        {
        // If we know that partial write is not supported
        // AND the client has reported the full size of 
        // the file, do not really flush until all the data
        // has been written to the cache file.
        // *
        // If we do not know the total size flush whatever is cached
        if ((iCachedSize == iReportedSize)  || iReportedSize == 0)
            {
            err = static_cast<CRsfwFsMountCB&>
    			(Mount()).RSessionL()->Flush(iThisFid,
    			                            0,
    			                            iCachedSize, 
    			                            iCachedSize);  			                            
            }
         // else do not do anything yet, only when the whole file is in cache
        }
     else 
        {
        // we "still" assume that partial write is supported
        err = static_cast<CRsfwFsMountCB&>
    		(Mount()).RSessionL()->Flush(iThisFid,
    			                         iFlushedSize,
    			                         iCachedSize, 
    			                         iReportedSize);
        if (err == KErrNotSupported) 
            {
            err = KErrNone; // reset the error
            // flushing the file not supported
            // probably because the access protol plugin does not support partial write
            iPartialWriteSupported = EFalse;
            // apply the flush logic again
            // this time with the knowledge that the server does not support partial
            // write
            goto starttoflush;
            }
         else 
            {
            iAtt &= ~KEntryAttModified; // clear KEntryAttModified if flush worked
            }
    			                         
        }
        
    // re-open the container file    
     User::LeaveIfError(iContFile.Open(*(RFs* )Dll::Tls(),
                                      iCachePath,
                                      EFileWrite | EFileShareAny)); 
    
        
    // handling the results    
    if (err == KErrNone) 
        {
        // the operation was successful
        iFlushedSize = iCachedSize;			                            
        }
    else if (err != KErrNone)
        {
        iLastFlushFailed = ETrue;
        User::Leave(err);
        }
   
    }
  
// -----------------------------------------------------------------------------
// CRsfwFsFileCB::SetContainerFileL
// Opens the container file.
// Called in CRsfwFsMountCB when the file is opened.
// Implementation notice: If this file is only opened for reading, it would
// seem sensible to open  EFileRead|EFileShareAny here, but when Remote 
// File Engine then tries to open EFileWrite|EFileShareAny it fails unless
// EFileWrite is specified here also...
//
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CRsfwFsFileCB::SetContainerFileL(
    const TDesC& aPath)
    {
    iCachePath = aPath;
    User::LeaveIfError(iContFile.Open(*(RFs* )Dll::Tls(),
                                      aPath,
                                      EFileWrite | EFileShareAny)); 
    }

// End of File