remotestoragefw/remotefileengine/src/rsfwpermanentstore.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) 2004-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:  Provides persistent storage for memory blocks
*
*/


// This is a simple block based permanent storage, in which transient
// indices are computed on the fly when all data is sequentially read
// from the storage.
//
// The data is put into "slots" that consist of one or more "blocks".
// Subsequent updates to the data slots only use the current slot
// if the required slot size (block count) has not changed.
//
// The storage contains a header that may contain any data.
// In addition, Each slot contains a header (of type TInt) that
// describes the size of the data in the slot.
//
// The file can be compressed for space efficiency.
//
// The assumption is that the store is first opened for reading for loading
// the data, and then it is only opened for writing until the file is closed.
//
// (We don't use CRsfwPermanentStore because besides data, we should
//  also maintain the indices in a stream of its own)

// INCLUDE FILES
#include <s32strm.h>
#include <s32buf.h>

#include "rsfwpermanentstore.h"
#include "mdebug.h"


// CONSTANTS
const TUint KHeaderStart      = 0xbabefade;
const TInt  KSlotHeaderSize   = 4;          // sizeof(TInt)
const TInt  KDefaultBlockSize = 128;        // default block size in bytes
// max value for integer read from the stream, subsequently used to create descriptor
const TInt  KMaxDescriptorSize = 16384;         


// ============================ MEMBER FUNCTIONS ==============================

// ----------------------------------------------------------------------------
// TFileHeader::ExternalizeL
// ----------------------------------------------------------------------------
//
void TFileHeader::ExternalizeL(RWriteStream& aStream) const
    {
    aStream.WriteInt32L(iHeaderStart);
    aStream.WriteInt32L(iBlockSize);
    aStream.WriteInt32L(iHeaderSize);
    aStream << *iHeader;
    }

// ----------------------------------------------------------------------------
// TFileHeader::InternalizeL
// ----------------------------------------------------------------------------
//
void TFileHeader::InternalizeL(RReadStream& aStream)
    {
    iHeaderStart = aStream.ReadInt32L();
    iBlockSize = aStream.ReadInt32L();
    if (iBlockSize < 0 || iBlockSize > KMaxDescriptorSize)
        {
        User::Leave(KErrCorrupt);
        }    
    iHeaderSize = aStream.ReadInt32L();
    if (iHeaderSize < 0 || iHeaderSize > KMaxDescriptorSize)
        {
        User::Leave(KErrCorrupt);
        }     
    iHeader = HBufC8::NewL(aStream, iHeaderSize);
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::NewL
// ----------------------------------------------------------------------------
//
CRsfwPermanentStore* CRsfwPermanentStore::NewL(const TDesC& aPath,
                                       TInt aHeaderSize,
                                       TInt aBlockSize)
    {
    CRsfwPermanentStore* self = new (ELeave) CRsfwPermanentStore();
    CleanupStack::PushL(self);
    self->ConstructL(aPath, aHeaderSize, aBlockSize);
    CleanupStack::Pop(self);
    return self;
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::~CRsfwPermanentStore
// ----------------------------------------------------------------------------
//
CRsfwPermanentStore::~CRsfwPermanentStore()
    {
    ClearFreeBlockLists();
    iSlots.Close();
    iFileReadStream.Close();
    iFileWriteStream.Close();
    iFile.Close();
    iFs.Close();
    delete iFileHeader.iHeader;
    delete iZeroBlock;
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::ConstructL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::ConstructL(const TDesC& aPath,
                                 TInt aHeaderSize,
                                 TInt aBlockSize)
    {
    DEBUGSTRING(("CRsfwPermanentStore::ConstructL()"));
    User::LeaveIfError(iFs.Connect());
    iPath.Copy(aPath);

    iHeaderSize = aHeaderSize;
    // Offset to the block data
    iFileHeaderSize = iHeaderSize + sizeof(TFileHeader);
    TRAPD(err, LoadHeaderL());
    if (err == KErrNone)
        {
        // Read parameters from the existing file, if any
        iHeaderSize = iFileHeader.iHeaderSize;
        iBlockSize = iFileHeader.iBlockSize;
        }
    else
        {
        // There was no existing file
        DEBUGSTRING(("LoadHeaderL returns %d", err));
        if (aBlockSize)
            {
            iBlockSize = aBlockSize;
            }
        else
            {
            iBlockSize = KDefaultBlockSize;
            }
        iFileHeader.iHeaderStart = KHeaderStart;
        iFileHeader.iHeaderSize = iHeaderSize;
        iFileHeader.iBlockSize = iBlockSize;
        }
    // Compute the offset to data blocks
    // (this is 4 bytes too many but that is OK ...)
    iFileHeaderSize = iHeaderSize + sizeof(TFileHeader);
    // Make a block of zeroes
    iZeroBlock = HBufC8::NewL(iBlockSize);
    iZeroBlock->Des().FillZ();
    DEBUGSTRING(("ConstructL() done, blocksize=%d", iBlockSize));
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::ResetL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::ResetL(TBool aWriting)
    {
    // This function is called by the client
    // before starting to read or write data slots
    if (aWriting)
        {
        SetFileStateL(EFileStateWriting);
        // Set write pointer to the end of the file
        MStreamBuf* streamBuf = iFileWriteStream.Sink();
        TInt size = streamBuf->SizeL();
        iWriteBlockNumber = (size - iFileHeaderSize) / iBlockSize;
        if ((iWriteBlockNumber * iBlockSize) != (size - iFileHeaderSize))
            {
            DEBUGSTRING(("ResetL(): file size incorrect (%d)",
                         size));
            }
        }
    else
        {
        // reading
        SetFileStateL(EFileStateReading);
        // Skip file header
        MStreamBuf* streamBuf = iFileReadStream.Source();
        streamBuf->SeekL(MStreamBuf::ERead,
                         EStreamBeginning,
                         iFileHeaderSize);
        iReadBlockNumber = 0;
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::Commit
// ----------------------------------------------------------------------------
//
TInt CRsfwPermanentStore::Commit()
    {
    TInt err = KErrNone;
    // Commit writing
    if (iFileState == EFileStateWriting)
        {
        MStreamBuf* streamBuf = iFileWriteStream.Sink();
        err = streamBuf->Synch();
        }
    return err;
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::Purge
// ----------------------------------------------------------------------------
//
TInt CRsfwPermanentStore::Purge()
    {
    iFileReadStream.Close();
    iFileWriteStream.Close();
    iFile.Close();
    return iFs.Delete(iPath);
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::CompactL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::CompactL()
    {
    // Compact the file by dropping empty slots.
    // The slot table is also fixed and the free block lists are cleared
    // to reflect the new file layout.

    // This function must not be called while the file is opened for reading.
    // It is most efficient to call this function before any data is read
    // (then there is no slot table to fix)

    // This function must be called before doing GetNextDataL()
    // (must be followed by ResetL()).
    // However, this function can be called while doing PutDataL(). 

    DEBUGSTRING(("CompactL()"));
    SetFileStateL(EFileStateClosed);
    SetFileStateL(EFileStateReading);
    
    TBuf<KMaxPath> dstPath;
    dstPath.Copy(iPath);
    dstPath.Append('a');
    RFile dstFile;
    User::LeaveIfError(dstFile.Replace(iFs,
                                       dstPath,
                                       EFileShareAny | EFileWrite));
    CleanupClosePushL(dstFile);
    RFileWriteStream dstFileWriteStream(dstFile);
    CleanupClosePushL(dstFileWriteStream);
    MStreamBuf* streamBuf = iFileReadStream.Source();

    // Copy header
    dstFileWriteStream.WriteL(iFileReadStream, iFileHeaderSize);

    RArray<TSlot> gaps;
    CleanupClosePushL(gaps);

    TInt blockNumber = 0;
    TInt lastReadBlockNumber = 0;
    TInt nextWriteBlockNumber = 0;
    TInt err = KErrNone;
    while (err == KErrNone)
        {
        TSlot gap;
        TInt dataLength = 0;
        TRAP(err, dataLength = iFileReadStream.ReadInt32L());
        if (err == KErrNone)
            {
            TInt blockCount;
            if (dataLength >= 0)
                {
                // real data
                blockCount = BlockCount(dataLength);
#if 0
                DEBUGSTRING(("Copying at block %d, count %d",
                             blockNumber,
                             blockCount));
#endif
                // Back off four bytes
                streamBuf->SeekL(MStreamBuf::ERead,
                                 EStreamMark,
                                 -KSlotHeaderSize);
                dstFileWriteStream.WriteL(iFileReadStream,
                                          blockCount * iBlockSize);
                lastReadBlockNumber = blockNumber;
                nextWriteBlockNumber += blockCount;
                }
            else
                {
                // empty slot
                DEBUGSTRING(("Compacting at block %d", blockNumber));
                blockCount = -dataLength;
                streamBuf->SeekL(MStreamBuf::ERead,
                                 EStreamMark,
                                 blockCount * iBlockSize - KSlotHeaderSize);
                // Mark block position
                gap.iIndex = 0;  // not needed here
                gap.iBlockNumber = blockNumber;
                gap.iBlockCount = blockCount;
                gaps.Append(gap);
                }
            blockNumber += blockCount;
            }
        }
    if (err == KErrEof)
        {
        err = KErrNone;
        }
    // Replace old file with the compressed file
    SetFileStateL(EFileStateClosed);
    dstFileWriteStream.Close();
    dstFile.Close();
    if ((err == KErrNone) && gaps.Count())
        {
        // No errors and some compaction was achieved
        err = iFs.Delete(iPath);
        if (err == KErrNone)
            {
            err = iFs.Rename(dstPath, iPath);
            if (err == KErrNone)
                {
                DEBUGSTRING(("CompactL(): gaps %d slots %d",
                             gaps.Count(),
                             iSlots.Count()));
                if (gaps.Count() && iSlots.Count())
                    {
                    // Fix slot table (0(n**2))
                    TInt oldBlockNumber = 0;
                    TInt newBlockNumber = 0;
                    while (oldBlockNumber <= lastReadBlockNumber)
                        {
                        if (gaps.Count())
                            {
                            // still more gaps ...
                            if (oldBlockNumber == gaps[0].iBlockNumber)
                                {
                                oldBlockNumber += gaps[0].iBlockCount;
                                gaps.Remove(0);
                                }
                            }
                        if (oldBlockNumber != newBlockNumber)
                            {
                            FixSlot(oldBlockNumber, newBlockNumber);
                            }
                        oldBlockNumber++;
                        newBlockNumber++;
                        }
                    }
                }
            }
        // Clear free block lists
        ClearFreeBlockLists();
        // Setup next block to write
        iWriteBlockNumber = nextWriteBlockNumber;
        }
    else
        {
        // Some error occured or no compaction was achieved
        TInt r = iFs.Delete(dstPath);
#ifdef _DEBUG
        DEBUGSTRING(("CompactL(): destination file deletion returns %d ", r));
#endif
        }

    CleanupStack::PopAndDestroy(3, &dstFile); // gaps, dstFileWriteStream, dstFile
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::SetHeaderL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::SetHeaderL(TDesC8& aHeader)
    {
    delete iFileHeader.iHeader;
    iFileHeader.iHeader = NULL;
    iFileHeader.iHeader = aHeader.AllocL();
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::Header
// ----------------------------------------------------------------------------
//
const HBufC8* CRsfwPermanentStore::Header()
    {
    if (iFileHeader.iHeader)
        {
        return iFileHeader.iHeader;
        }
    else
        {
        return NULL;
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::GetNextDataL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::GetNextDataL(TUint8* aData,
                                   TInt& aDataLength,
                                   TInt& aIndex)
    {
    // Return the next slotful of data.
    // This function must only be used for sequential access
    SetFileStateL(EFileStateReading);

    MStreamBuf* streamBuf = iFileReadStream.Source();
    
    TBool done = EFalse;
    while (!done)
        {
        TInt dataLength = 0;
        // Eventually leaves with KErrEof
        dataLength = iFileReadStream.ReadInt32L();
        TInt blockCount;
        if (dataLength >= 0)
            {
            // Fill data
            iFileReadStream.ReadL(aData, dataLength);
            aDataLength = dataLength;
            aIndex = iIndex;

            // Update block map
            blockCount = BlockCount(dataLength);
#if 0
            DEBUGSTRING((
                            "GetNextDataL(): index=%d, block=%d, count=%d, len=%d",
                            iIndex,
                            iReadBlockNumber,
                            blockCount,
                            dataLength));
#endif

            ReserveSlot(iIndex, iReadBlockNumber, blockCount); 
            iIndex++;
            iReadBlockNumber += blockCount;

            // Update read position to the start of next block
            TInt offset = iBlockSize -
                (KSlotHeaderSize + dataLength) % iBlockSize;
            if (offset != iBlockSize)
                {
                streamBuf->SeekL(MStreamBuf::ERead, EStreamMark, offset);
                }
            
            done = ETrue;
            }
        else
            {
            // Negative length indicates a number of free blocks.
            // Put such empty blocks into the free block list
            blockCount = -dataLength;
            PutToFreeBlockList(iReadBlockNumber, blockCount);
            
            // Seek to the next slot
            streamBuf->SeekL(MStreamBuf::ERead, 
                             EStreamMark,
                             blockCount * iBlockSize - KSlotHeaderSize);
            iReadBlockNumber += blockCount;
            }
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::PutDataL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::PutDataL(const TUint8* aData,
                               TInt aDataLength,
                               TInt& aIndex)
    {
    // Write data with the given index
    // If the index < 0, this a new data
    SetFileStateL(EFileStateWriting);

    if (aDataLength == 0)
        {
        // We just want to dispose of the slot
        ClearSlotL(aIndex);
        return;
        }

    TBool done = EFalse;
    TInt blockCount = BlockCount(aDataLength);
    if (aIndex >= 0)
        {
        // Check if we have space in the existing slot
        TSlot* slot = Slot(aIndex);
        // We should always find the slot
        if (slot)
            {
            if (slot->iBlockCount == blockCount)
                {
                // We can use the current slot
                WriteBlocksL(aData, aDataLength, slot->iBlockNumber);
                done = ETrue;
                }
            else
                {
                // Clear the slot
                ClearSlotL(aIndex);
                }
            }
        else
            {
            DEBUGSTRING(("Slot %d not found!", aIndex));
            }
        }
    else
        {
        // Allocate new index
        aIndex = iIndex++;
        }
    
    if (!done)
        {
        // Try to get a free slot
        TInt blockNumber = GetFromFreeBlockList(blockCount);
        if (blockNumber == KErrNotFound)
            {
            // We have to append to the file
            blockNumber = iWriteBlockNumber;
            iWriteBlockNumber += blockCount;
            }
        
        ReserveSlot(aIndex, blockNumber, blockCount);
        WriteBlocksL(aData, aDataLength, blockNumber);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::BlockCount
// ----------------------------------------------------------------------------
//
TInt CRsfwPermanentStore::BlockCount(TInt aDataLength)
    {
    // Get the block count for a given data size
    return (KSlotHeaderSize + aDataLength - 1) / iBlockSize + 1;
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::StreamPosition
// ----------------------------------------------------------------------------
//
TInt CRsfwPermanentStore::StreamPosition(TInt aBlockNumber)
    {
    // Get the stream position from block position
    return (iFileHeaderSize + aBlockNumber * iBlockSize);
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::Slot
// ----------------------------------------------------------------------------
//
TSlot* CRsfwPermanentStore::Slot(TInt aIndex)
    {
    // Find the slot with the given index
    TSlot slot;             // dummy slot for Find()
    slot.iIndex = aIndex;
    TInt i = iSlots.Find(slot);
    if (i != KErrNotFound)
        {
        return &iSlots[i];
        }
    else
        {
        return NULL;
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::FixSlot
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::FixSlot(TInt aOldBlockNumber, TInt aNewBlockNumber)
    {
    // Assign a new starting block to the slot that used to
    // start at aOldBlockNumber.
    // The block numbers are changed due to compaction -
    // thus the block numbers can only become smaller.
    TInt i;
    for (i = 0; i < iSlots.Count(); i++)
        {
        TSlot& slot = iSlots[i];
        // Note that this function can also be called with block numbers
        // that do not start a slot - then there will be no match
        if (slot.iBlockNumber == aOldBlockNumber)
            {
            DEBUGSTRING(("Fixing slot %d = %d",
                         aOldBlockNumber,
                         aNewBlockNumber));
            slot.iBlockNumber = aNewBlockNumber;
            return;
            }
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::SetFileStateL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::SetFileStateL(TInt aFileState)
    {
    DEBUGSTRING(("CRsfwPermanentStore::SetFileStateL"));
    DEBUGSTRING(("iFileState = %d, aFileState = %d", iFileState, aFileState));
    if (iFileState != aFileState)
        {
        switch (iFileState)
            {
        case EFileStateClosed:
            {
            if (aFileState == EFileStateReading)
                {
                DEBUGSTRING(("opening a closed file for reading"));
                DEBUGSTRING16(("path is %S", &iPath));
                User::LeaveIfError(iFile.Open(iFs,
                                              iPath,
                                              EFileShareAny | EFileRead));
                TInt size;
                iFile.Size(size);
                DEBUGSTRING(("opening successfull, file size is %d", size));
                DEBUGSTRING(("header size %d", iFileHeaderSize));
                // sanity
                if (size < iFileHeaderSize)
                    {
                    // Close the file and leave the file state as "closed"
                    iFile.Close();
                    User::Leave(KErrNotFound);
                    }
                else
                    {
                    iFileReadStream.Attach(iFile);
                    }
                }
            else
                {
                // EFileStateWriting
                TInt err = iFile.Open(iFs, iPath, EFileShareAny | EFileWrite);
                if (err != KErrNone)
                    {
                    // The file did not exist
                    User::LeaveIfError(
                        iFile.Create(iFs,
                                     iPath,
                                     EFileShareAny | EFileWrite));
                    }
                TInt size;
                User::LeaveIfError(iFile.Size(size));
                iFileWriteStream.Attach(iFile);
                if (size < iFileHeaderSize)
                    {
                    // Store header if this was a new file
                    SaveHeaderL();
                    }
                }
            }
            break;
        
        case EFileStateReading:
        case EFileStateWriting:
            {
            // close and redo
            if (iFileState == EFileStateReading)
                {
                iFileReadStream.Close();
                }
            else
                {
                iFileWriteStream.Close();
                }
            iFile.Close();
            iFileState = EFileStateClosed;
            SetFileStateL(aFileState);
            }
            break;
    
        default:
            break;
            }
        
        iFileState = aFileState;
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::LoadHeaderL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::LoadHeaderL()
    {
    DEBUGSTRING(("CRsfwPermanentStore::LoadHeaderL"));
    SetFileStateL(EFileStateReading);
    iFileHeader.InternalizeL(iFileReadStream);
    ResetL(EFalse);
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::SaveHeaderL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::SaveHeaderL()
    {
    // Write header with filler
    iFileHeader.ExternalizeL(iFileWriteStream);
    MStreamBuf* streamBuf = iFileWriteStream.Sink();
    TInt fileHeaderSize = streamBuf->TellL(MStreamBuf::EWrite).Offset();
    TInt residue = iFileHeaderSize - fileHeaderSize;
    HBufC8* fill = HBufC8::NewLC(residue);
    TPtr8 fillZ = fill->Des();
    fillZ.SetLength(residue);
    fillZ.FillZ();
    DEBUGSTRING(("SaveHeader(): header=%d, filler=%d",
                 iFileHeaderSize,
                 fillZ.Length()));
    iFileWriteStream.WriteL(fillZ);
    Commit();
    CleanupStack::PopAndDestroy(fill); // fill
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::ClearFreeBlockLists
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::ClearFreeBlockLists()
    {
    TInt i;
    for (i = 0; i < iFreeBlockLists.Count(); i++)
        {
        iFreeBlockLists[i].iFreeBlockList.Close();
        }
    iFreeBlockLists.Close();
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::ClearSlotL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::ClearSlotL(TInt aIndex)
    {
    // Mark a slot as unused in the file and
    // add the slot in the free block list

    TSlot s;                // dummy slot for iSlots.Find()
    s.iIndex = aIndex;
    TInt i = iSlots.Find(s);
    if (i != KErrNotFound)
        {
        TSlot& slot = iSlots[i];
        // Mark the slot in the file as empty
        TInt pos = iFileHeaderSize +  slot.iBlockNumber * iBlockSize;

        DEBUGSTRING(("ClearSlotL(): index=%d, block=%d, count=%d, pos=%d",
                     aIndex,
                     slot.iBlockNumber,
                     slot.iBlockCount,
                     pos));

        MStreamBuf* streamBuf = iFileWriteStream.Sink();
        streamBuf->SeekL(MStreamBuf::EWrite, EStreamBeginning, pos);
        iFileWriteStream.WriteInt32L(-slot.iBlockCount);
        
        // Add the slot in the free block list
        PutToFreeBlockList(slot.iBlockNumber, slot.iBlockCount);

        // Delete the slot from the slot table
        iSlots.Remove(i);
        }
    else
        {
        DEBUGSTRING(("ClearSlotL(): index=%d not found!", aIndex));
        User::Leave(KErrNotFound);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::WriteBlocksL
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::WriteBlocksL(const TUint8* aData,
                                   TInt aDataLength,
                                   TInt aBlockNumber)
    {
    // Put the given data in the slot specified by the index
    TInt pos = iFileHeaderSize +  aBlockNumber * iBlockSize;

#if 0
    DEBUGSTRING(("WriteBlocksL(): block=%d, len=%d, pos=%d",
                 aBlockNumber,
                 aDataLength,
                 pos));
#endif
#if 0
    TInt size;
    iFile.Size(size);

    DEBUGSTRING(("size=%d", size));
#endif
    
    MStreamBuf* streamBuf = iFileWriteStream.Sink();
    streamBuf->SeekL(MStreamBuf::EWrite, EStreamBeginning, pos);
    
    // Write the data in the file (preceded by the length)
    iFileWriteStream.WriteInt32L(aDataLength);
    iFileWriteStream.WriteL(aData, aDataLength);

    // We have to fill the whole slot if this is the last slot
    // in the file. Well, we will will the last bock of all slots
    TInt residue = iBlockSize - (KSlotHeaderSize + aDataLength) % iBlockSize;
    if (residue != iBlockSize)
        {
        iFileWriteStream.WriteL(iZeroBlock->Des().Ptr(), residue);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::ReserveSlot
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::ReserveSlot(TInt aIndex,
                                  TInt aBlockNumber,
                                  TInt aBlockCount)
    {
    TSlot slot;

    slot.iIndex = aIndex;
    slot.iBlockNumber = aBlockNumber;
    slot.iBlockCount = aBlockCount;
    iSlots.Append(slot);
    }


// ----------------------------------------------------------------------------
// CRsfwPermanentStore::PutToFreeBlockList
// ----------------------------------------------------------------------------
//
void CRsfwPermanentStore::PutToFreeBlockList(TInt aBlockPos, TInt aBlockCount)
    {
    while (iFreeBlockLists.Count() < aBlockCount)
        {
        // Construct list until blockCount size
        TFreeBlockList freeBlockList;
        iFreeBlockLists.Append(freeBlockList);
        }
    iFreeBlockLists[aBlockCount - 1].iFreeBlockList.Append(aBlockPos);
    }

// ----------------------------------------------------------------------------
// CRsfwPermanentStore::GetFromFreeBlockList
// ----------------------------------------------------------------------------
//
TInt CRsfwPermanentStore::GetFromFreeBlockList(TInt aBlockCount)
    {
    // Only support exact matches.
    // That is, bigger slots are not broken into smaller slots
    if (iFreeBlockLists.Count() < aBlockCount)
        {
        // no list
        return KErrNotFound;
        }

    TFreeBlockList& freeBlockList = iFreeBlockLists[aBlockCount - 1]; 
    if (freeBlockList.iFreeBlockList.Count() == 0)
        {
        // no entries in the list
        return KErrNotFound;
        }

    // Get the first entry in the list
    TInt blockPos = freeBlockList.iFreeBlockList[0];
    freeBlockList.iFreeBlockList.Remove(0);
    return blockPos;
    }

// End of File