mFS file system library for EEPROM memory chips.

Revision:
12:928346513c87
Parent:
11:6c4fcb9d6193
Child:
13:142b6be3e3c8
--- a/mfs.cpp	Tue Feb 22 21:39:41 2011 +0000
+++ b/mfs.cpp	Thu Feb 24 00:02:36 2011 +0000
@@ -12,10 +12,10 @@
 #include "mfs.h"
 #include "i2c_eeprom.h"
 
-#define BLOCK_NLEN 1 /**< Block number length in bytes */
-#define RB 1+2*BLOCK_NLEN /**< Reseved bytes per block (1 attrb B, 2 B for next/prev pointers */
-#define I2C_SPEED 200000 /**< I2C bus speed in Hz */
-#define DEBUG /**< Adds extra safety in reading and writing */
+#define BLOCK_LLEN 2        /**< Block number link length in bytes */
+#define RB 1+2*BLOCK_LLEN   /**< Reseved bytes per block (1 attrb B, 2 B for next/prev pointers */
+#define I2C_SPEED 200000    /**< I2C bus speed in Hz */
+#define DEBUG               /**< Adds extra safety in reading and writing */
 
 DigitalOut FlushLed(LED2); /**< Flush led */
 
@@ -32,7 +32,7 @@
         return 1;
     #endif
     mem->read(BS*block+byte, n, data);
-    //wait_ms(1);
+
     return 0;
 }
 
@@ -44,6 +44,7 @@
         return 1;
     #endif
     mem->write(data, BS*block+byte, n);
+    
     return 0;
 }
 
@@ -129,9 +130,11 @@
         return 2; // Out of space
     
     char cData[RB+20];
-    cData[0] = '\xCC'; // Necessary flags for file
-    cData[1] = '\0'; // Only this block yet
-    cData[2] = '\0'; // First block there could't be prev blocks
+    cData[0] = '\xCC';  // Necessary flags for a file
+    cData[1] = '\0';    // Only this block yet
+    cData[2] = '\0';
+    cData[3] = '\0';    // First block there could't be prev blocks
+    cData[4] = '\0';
     
     for (char i=0; i < 20; i++)
         cData[RB+i] = filename[i];
@@ -145,32 +148,31 @@
 char mfs::removeFile(char filename[20])
 {
     uint32_t block;
-    char cData[3] = {'\0','\0','\0'};
+    char cData[RB-BLOCK_LLEN];
+    char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'};
+    uint32_t i=0;
 
     // Check if file exists
     if (getFirstBlockOfFile(filename, &block) != 0)
         return 1; // File not found
     
-    // Clear blocks reserver by the file
-    uint32_t i=0;
-    char tmp_cData[2];
+    read(cData, block, 0, RB-BLOCK_LLEN);
+    
+    // Check credentials
+     if (((cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3) & 0x04) != 0)
+        return 2; // RO file
+    
+    // Clear blocks reserved by the file    
     while(1)
     {
-        read(cData, block, 0, 2);
-        tmp_cData[0] = cData[0];
-        tmp_cData[1] = cData[1];
-        
-        // Clear the block
-        cData[0] = '\x04';
-        write(cData, block, 0, 3);
-        
-        if ((tmp_cData[0] & 0x4C) != 0)
-            break; // End of File found
-        else if (i >= BC)
-            return 2; // fs is corrupted
-        block = tmp_cData[1];
-
+        write(cDataNew, block, 0, RB);
+        if ((cData[0] & 0x4C) == 0x4C)
+            break; // End of file found
+        else block = (uint32_t)(cData[1])<<8|cData[2]; // Set next block number
         i++;
+        if (i > BC)
+            return 1; // fs is corrupted
+        read(cData, block, 0, RB-BLOCK_LLEN);
     }
     
     return 0; // Everything went better than expected
@@ -179,11 +181,18 @@
 char mfs::renameFile(char oldFilename[20], char newFilename[20])
 {
     uint32_t block;
+    char cData[1];
 
     // Check if file exists
     if (getFirstBlockOfFile(oldFilename, &block) != 0)
         return 1; // File not found
     
+    // Check credentials
+    read(cData, block, 0, 1);
+     char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3);
+     if ((flags & 0x04) != 0)
+        return 2; // RO file
+    
     write(newFilename, block, RB, 20);
     
     return 0; // Everything went better than expected
@@ -196,13 +205,15 @@
     
     uint32_t n;
     char cData[1] = {'\0'};
+    char cFlags;
 
     // Check if file exists
     if (getFirstBlockOfFile(filename, &n) != 0)
         return 1; // File not found
     
     read(cData, n, 0, 1);
-    cData[0] |= (flags[0] & 0x01)|((flags[0] & 0x02) << 3)|((flags[0] & 0x04) << 3);
+    cFlags    = ((flags[0] & 0x01)|((flags[0] & 0x02) << 3)|((flags[0] & 0x04) << 3));
+    cData[0]  = cData[0] & (~0x31) | cFlags;
     write(cData, n, 0, 1);
     
     return 0;
@@ -250,13 +261,13 @@
     uint32_t iAddr = 0;
     uint32_t i = 0;
     uint32_t bad = 0; // For counting bad block headers
-    char cFlags[] = {'\0', '\0', '\0'}, a[1];
+    char cFlags[RB] = {'\0', '\0', '\0', '\0', '\0'}, o[1];
     
     if (createLabel == true)
     {
         // Write Volume label
         cFlags[0] = '\x0E';
-        mem->write(cFlags, iAddr, 3);
+        mem->write(cFlags, iAddr, RB);
         iAddr = BS;
         i = 1;
     }
@@ -264,9 +275,9 @@
     cFlags[0] = '\x04';
     for (; i < BC; i++)
     {
-        mem->write(cFlags, iAddr, 3);
-        mem->read(iAddr, 1, a);
-        if (a[0] != cFlags[0])
+        mem->write(cFlags, iAddr, RB);
+        mem->read(iAddr, 1, o);
+        if (o[0] != cFlags[0])
             bad++;
         iAddr += BS;
     }
@@ -274,17 +285,14 @@
     return bad;
 }
 
-file::file(mfs *fs_ref, char filename[20], char operation)
+file::file(mfs *fs_ref, char filename[20], FileOpenMode operation)
 {
-    // Operations:
-    // 0 = Open in RO
-    // 1 = Open in RW
-    attr = operation;
+    fMode = operation;
     
     fs = fs_ref; // Don't forget this :)
     
     uint32_t n;
-    char cData[3] = {'\0','\0','\0'};
+    char cData[1] = {'\0'};
 
     // Check if file exists
     if (fs->getFirstBlockOfFile(filename, &n) != 0)
@@ -293,16 +301,14 @@
     fs->read(cData, n, 0, 1);
     char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3);
     
-    if (attr == 1)
-    {
-        if ((flags & 0x04) != 0)
-            error("Oops, cant open in RW mode!");
-    }
+    if ((fMode != RO) && ((flags & 0x04) != 0))
+        error("Oops, cant open in RW mode!");
 
     // Store FBOF number
     firstBlock = n;
     currBlock = n;
     blockPos = RB+20; // skip flags + pointers + filename
+    byteCount = 0; // First byte of the file
 
     // Initialize buffer
     for (unsigned int i=0; i < BUF; i++)
@@ -315,36 +321,92 @@
     flush();
 }
 
+char file::getBlockLink(BlockLinkType linkSelection, uint32_t *blockOut)
+{
+    char cData[1+BLOCK_LLEN];
+
+    if (linkSelection == NEXT)
+    {
+        // Fetch link to next block
+        fs->read(cData, currBlock, 0, 1+BLOCK_LLEN);
+        if ((cData[0] & 0x40) == 0)
+        {
+            *blockOut  = ((uint32_t)(cData[1])) << 8; // Hbyte of next block link
+            *blockOut |= (uint32_t)cData[2];          // Lbyte of next block link
+            return 0;
+        } else return 1; // Already at last block
+    } else if (linkSelection == PREV)
+    {
+        if (currBlock != firstBlock)
+        {
+            fs->read(cData, currBlock, 1+BLOCK_LLEN, BLOCK_LLEN);
+            *blockOut  = ((uint32_t)(cData[0])) << 8; // Hbyte of next block link
+            *blockOut |= (uint32_t)cData[1];          // Lbyte of next block link
+            return 0;
+        } else return 1; // Already at first block
+    }
+    
+    return 0;
+}
+
+char file::removeFollowingBlocks(uint32_t block)
+{
+    char cData[RB-BLOCK_LLEN];
+    char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'};
+    uint32_t i=0;
+    
+    while(1)
+    {
+        fs->read(cData, block, 0, RB-BLOCK_LLEN);
+        fs->write(cDataNew, block, 0, RB);
+        if ((cData[0] & 0x4C) == 0x4C)
+            break; // End of file found
+        else block = (uint32_t)(cData[0])<<8|cData[1]; // Set next block number
+        i++;
+        if (i > BC)
+            return 1; // fs is corrupted
+    }
+    
+    return 0;
+}
+
 void file::rewind()
 {
     flush();
     currBlock = firstBlock;
     blockPos = RB+20; // skip flags & pointers + filename
+    byteCount = 0;
 }
 
 char file::rewind(uint32_t n)
 {
     uint32_t i;
-    char cData[3];
+    uint32_t block;
+    uint32_t varBlockOffset;
     
     flush(); // Check if flush is needed
     
     for (i=0; i < n; i++)
     {
         blockPos--;
+        byteCount--;
     
         // Change Block?
-        if (blockPos < 3)
+        if (blockPos == firstBlock)
+            varBlockOffset = RB+20;
+        else
+            varBlockOffset = RB;
+        if ((blockPos < varBlockOffset) && (currBlock > firstBlock))
         {
-            // Fetch link to next block
-            fs->read(cData, currBlock, 0, RB);
-            if (!(cData[0] & 0x80))
+            // Fetch link to previous block
+            if(getBlockLink(PREV, &block) == 0)
             {
-                currBlock = cData[2];
-                blockPos = BS-1; // Set block postion offset at end of the block
+                currBlock = block;
+                blockPos  = BS-1; // blockPos should be set at the end of block
             } else {
                 blockPos++;
-                return 1; // This is the first block
+                byteCount++;
+                return 1; // This is the first block and byte
             }
         }
     }
@@ -360,32 +422,34 @@
 char file::forward(uint32_t n)
 {
     uint32_t i;
-    char cData[2];
+    uint32_t block; // Next block number
+    char cData[1];
 
     flush(); // Check if flush is needed
     
     for (i=0; i < n; i++)
     {
         blockPos++;
+        byteCount++;
     
         // Change Block?
-        if (blockPos > BS-1)
+        if (blockPos >= BS)
         {
             // Fetch link to next block
-            fs->read(cData, currBlock, 0, 2);
-            if (!(cData[0] & 0x40))
+            if (getBlockLink(NEXT, &block) == 0)
             {
-                currBlock = cData[1];
+                currBlock = block;
                 blockPos = RB; // Reset block position offset
             } else {
                 blockPos--;
-                return 1; // This is the last block
+                byteCount--;
+                return 1; // This is the last block & byte
             }
         }
         fs->read(cData, currBlock, blockPos, 1);
         if (cData[0] == mEOF)
         {
-            rewind(1);
+            rewind(1); // Get back to the byte before EOF
             return 1;
         }
     }
@@ -393,25 +457,34 @@
     return 0; // OK
 }
 
+char file::seek(uint32_t byte)
+{
+    if (byte > byteCount)
+        return forward(byte-byteCount);
+    else if (byte < byteCount)
+        return rewind(byteCount-byte);
+    else return 0;
+}
+
 // Respects mEOF and automatically sets '\0' at the end of string
 void file::read(char *data, uint32_t n)
 {
     uint32_t i;
-    char cData[2];
+    uint32_t block;
+    char cData[1];
     
     flush();
     
     for (i=0; i < n; i++)
     {
         // Change block?
-        if (blockPos == BS-1)
+        if (blockPos >= BS-1)
         {
             // Fetch link to next block
-            fs->read(cData, currBlock, 0, 2);
-            if (!(cData[0] & 0x40))
+            if (getBlockLink(NEXT, &block) == 0)
             {
-                currBlock = cData[1];
-                blockPos = RB; // Reset block position offset
+                currBlock = block;
+                blockPos  = RB; // Reset block position offset
             } else goto stop;
         }
         
@@ -425,17 +498,19 @@
         } else {
             data[i] = cData[0];
             blockPos++;
+            byteCount++;
         }
     }
-    if (data[i] != '\0')
-        data[i] = '\0';
+    if (data[n-1] != '\0')
+        data[n-1] = '\0';
 }
 
 // Ignores mEOF and doesn't set '\0' markings
 void file::readBin(char *data, uint32_t n)
 {
     uint32_t i;
-    char cData[2];
+    uint32_t block;
+    char cData[1];
     
     for (i=0; i < n; i++)
     {
@@ -443,10 +518,9 @@
         if (blockPos == BS-1)
         {
             // Fetch link to next block
-            fs->read(cData, currBlock, 0, 2);
-            if (!(cData[0] & 0x40))
+            if (getBlockLink(NEXT, &block) == 0)
             {
-                currBlock = cData[1];
+                currBlock = block;
                 blockPos = RB; // Reset block position offset
             } else return;
         }
@@ -456,14 +530,14 @@
         data[i] = cData[0];
         
         blockPos++;
+        byteCount++;
     }
-    data[i-1] = '\0';
 }
 
 // Always binary
 char file::write(char *data, uint32_t n)
 {
-    if (attr == 0) return 1;
+    if (fMode == RO) return 1;
     
     for (uint32_t i=0; i < n; i++)
     {
@@ -484,14 +558,18 @@
 
 char file::flush()
 {
-    char cData[3], c[1];
+    char cData[RB], cDataOB[RB-BLOCK_LLEN], c[1];
     uint32_t nextFree;
     uint32_t i;
+    uint32_t leftSpaceEOF;
+    
+    bool destructiveFlag = false; // Set this true if there is any data to be removed
+    uint32_t destructiveBlock=0;  // First block to be removed by DWRITE
     
     if (bufPos == 0) return 0; // File up-to date
-    if (attr == 0) return 1;
+    if (fMode == RO) return 1;
     
-    for (i=0; i < bufPos; i++)
+    for (i=0; i <= bufPos; i++)
     {
         if(i%2)
             FlushLed = 1;
@@ -499,44 +577,71 @@
             FlushLed = 0;
         
         // Change Block?
-        if (blockPos >= BS-2) // Must left space for mEOF
+        if ((bufPos - i) == 1)
+            leftSpaceEOF = 1;
+        else
+            leftSpaceEOF = 0;
+        if (blockPos >= BS-leftSpaceEOF)
         {
             // Fetch new unused block number
             if(fs->getNextFreeBlock(&nextFree) != 0)
-                return 1;
+                return 2; // No free space left
             
-            // Take new block into use
-            cData[0] = 0x4C; // New flags
-            cData[1] = '\0';
-            cData[2] = currBlock; // Prev Block
-            fs->write(cData, nextFree, 0, RB); // Update Block Data
+            // Read flags from current block
+            fs->read(cDataOB, currBlock, 0, RB-BLOCK_LLEN);
             
-            // Link old block with new block
-            fs->read(cData, currBlock, 0, RB);
-            cData[0] &= ~0x40; // Clear LBOF flag if set
-            cData[1] = nextFree;
-            cData[2] = '\0';
-            fs->write(cData, currBlock, 0, RB); // Update Block Data
+            /* If destructive write is set then check if there is something
+               to be marked for removal */
+            if (((cDataOB[0] & 0x40) != 0x40) && (fMode == DWRITE))
+            {
+                destructiveFlag = true;
+                destructiveBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2];
+                goto allocate_new_block;
+            } else if ((cDataOB[0] & 0x40) != 0x40) // fMode == AWRITE
+            {
+                // Update current block info
+                currBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2];
+                blockPos  = RB; // Reset block position offset
+            } else // There is no block to append so we allocate a new one
+            {
+                allocate_new_block:
+                // Allocate new block for use
+                cData[0] = 0x4C;                              // New flags
+                cData[1] = '\0';                              // Hbyte of Next Block link
+                cData[2] = '\0';                              // Lbyte of Next Block link
+                cData[3] = (char)((currBlock & 0xff00) >> 8); // Hbyte of Prev Block link
+                cData[4] = (char)(currBlock & 0x00ff);        // Lbyte of Prev Block link
+                fs->write(cData, nextFree, 0, RB);            // Update Block Data
             
-            // Update current block info
-            currBlock = nextFree;
-            blockPos = RB; // Reset block position offset
+                // Link old block with new block
+                cDataOB[0] &= ~0x40;                             // Clear LBOF flag if set
+                cDataOB[1]  = (char)((nextFree & 0xff00) >> 8);  // Hbyte of Next Block link
+                cDataOB[2]  = (char)(nextFree & 0x00ff);         // Lbyte of Next Block link
+                fs->write(cDataOB, currBlock, 0, RB-BLOCK_LLEN); // Update Block Data
             
+                // Update current block info
+                currBlock = nextFree;
+                blockPos  = RB; // Reset block position offset
+            }
         }
         
-        if (blockPos < 3)
-            error("");
-        
         // Write file
         c[0]=buffer[i];
         fs->write(c, currBlock, blockPos, 1);
         blockPos++;
+        byteCount++;
     }
     // Write mEOF
     fs->write((char[]){mEOF}, currBlock, blockPos+1, 1);
     
     bufPos = 0; // Reset buffer position counter
     
+    /* If destructive write flag is set
+       and there is data to be removed then remove data now */
+    if (destructiveFlag == true)
+        if(removeFollowingBlocks(destructiveBlock) != 0)
+            return 3;
+        
     FlushLed = 0;
     return 0;
 }
\ No newline at end of file