mFS file system library for EEPROM memory chips.

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mfs.cpp Source File

mfs.cpp

Go to the documentation of this file.
00001 /** @file mfs.cpp */
00002 /*CPP**************************************************************************
00003  * FILENAME :        mfs.cpp                                                  *
00004  *                                                                            *
00005  * DESCRIPTION :                                                              *
00006  *       mFS file system implementation for mBED with external I2C EEEPROM.   *
00007  *                                                                            *
00008  * AUTHOR :    Olli Vanhoja        START DATE :    2011-02-21                 *
00009  *****************************************************************************/
00010 
00011 #include "mbed.h"
00012 #include "mfs.h "
00013 #include "i2c_eeprom.h"
00014 
00015 #define BLOCK_LLEN 2        /**< Block number link length in bytes */
00016 #define RB 1+2*BLOCK_LLEN   /**< Reseved bytes per block (1 attrb B, 2 B for next/prev pointers */
00017 #define I2C_SPEED 200000    /**< I2C bus speed in Hz */
00018 #define DEBUG               /**< Adds extra safety in reading and writing */
00019 
00020 DigitalOut FlushLed(LED2); /**< Flush led */
00021 
00022 mfs::mfs(int i2c_address)
00023 {
00024     mem = new i2c_eeprom(i2c_address, I2C_SPEED);
00025 }
00026 
00027 char mfs::read(char *data, uint32_t block, uint32_t byte, uint32_t n)
00028 {
00029     // Faster reading without DEBUG mode
00030     #ifdef DEBUG
00031     if ((byte+n-1 >= BS))
00032         return 1;
00033     #endif
00034     mem->read(BS*block+byte, n, data);
00035 
00036     return 0;
00037 }
00038 
00039 char mfs::write(char *data, uint32_t block, uint32_t byte, uint32_t n)
00040 {
00041     // Faster writing without DEBUG mode
00042     #ifdef DEBUG
00043     if (byte+n >= BS)
00044         return 1;
00045     #endif
00046     mem->write(data, BS*block+byte, n);
00047     
00048     return 0;
00049 }
00050 
00051 char mfs::getNextFreeBlock(uint32_t *blockOut)
00052 {    
00053     // Locate free block by seeking EERPOM
00054     char cFlags[1];
00055     
00056     for (*blockOut=0; *blockOut < BC; (*blockOut)++)
00057     {
00058         read(cFlags, *blockOut, 0, 1);
00059         if (cFlags[0] == 0x04)
00060             break;
00061         if (*blockOut >= BC-1)
00062             return 1;
00063     }
00064     
00065     return 0;
00066 }
00067 
00068 char mfs::findNextFile(uint32_t block, char *filenameOut, uint32_t *blockOut)
00069 {
00070     uint32_t i=block;
00071     char cFlags[1];
00072     
00073     while (i < BC)
00074     {
00075         read(cFlags, i, 0, 1);
00076         
00077         if ((cFlags[0] & 0x8C) == 0x8C)
00078             break; // File found
00079         else
00080             i++;
00081     }
00082     
00083     if(i == BC)
00084     {
00085         strcpy(filenameOut, "");
00086         return 1; // Empty fs
00087     }
00088     
00089     // Read filename
00090     read(filenameOut, i, RB, 20);
00091     *blockOut = i; // Return block number
00092     return 0;
00093 }
00094 
00095 char mfs::getFirstBlockOfFile(char filename[20], uint32_t *blockOut)
00096 {
00097     *blockOut=0;
00098     char tmpFilename[20]="";
00099 
00100     while (1)
00101     {
00102         if (findNextFile(*blockOut, tmpFilename, blockOut) == 0)
00103         {
00104             if(strcmp(tmpFilename, filename) == 0)
00105                 return 0; // File exists
00106         }
00107         else return 1; // File doesn't exist
00108         (*blockOut)++;
00109     }
00110 }
00111 
00112 char mfs::createFile(char filename[20])
00113 {
00114     char tmpFilename[20];
00115     uint32_t n;
00116     uint32_t fb;
00117 
00118     for (n=0; n < BC; n++)
00119     {
00120         if(findNextFile(n, tmpFilename, &n) == 0)
00121         {
00122             if(strcmp(tmpFilename, filename) == 0)
00123                 return 1; // File exist
00124         }
00125         else break; // We already reached the edge of the universe
00126         n++;
00127     }
00128 
00129     if(getNextFreeBlock(&fb) != 0)
00130         return 2; // Out of space
00131     
00132     char cData[RB+20+1];
00133     cData[0] = '\xCC';   // Necessary flags for a file
00134     cData[1] = '\0';     // No more blocks yet
00135     cData[2] = '\0';
00136     cData[3] = '\0';     // First block so there is no prev blocks
00137     cData[4] = '\0';
00138     cData[RB+20] = mEOF; // Set EOF at the begining
00139     
00140     for (char i=0; i < 20; i++)
00141         cData[RB+i] = filename[i];
00142     
00143     // Create file
00144     write(cData, fb, 0, RB+20+1);
00145         
00146     return 0;
00147 }
00148 
00149 char mfs::removeFile(char filename[20])
00150 {
00151     uint32_t block;
00152     char cData[RB-BLOCK_LLEN];
00153     char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'};
00154     uint32_t i=0;
00155 
00156     // Check if file exists
00157     if (getFirstBlockOfFile(filename, &block) != 0)
00158         return 1; // File not found
00159     
00160     read(cData, block, 0, RB-BLOCK_LLEN);
00161     
00162     // Check credentials
00163      if (((cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3) & 0x04) != 0)
00164         return 2; // RO file
00165     
00166     // Clear blocks reserved by the file    
00167     while(1)
00168     {
00169         write(cDataNew, block, 0, RB);
00170         if ((cData[0] & 0x4C) == 0x4C)
00171             break; // End of file found
00172         else block = (uint32_t)(cData[1])<<8|cData[2]; // Set next block number
00173         i++;
00174         if (i > BC)
00175             return 1; // fs is corrupted
00176         read(cData, block, 0, RB-BLOCK_LLEN);
00177     }
00178     
00179     return 0; // Everything went better than expected
00180 }
00181 
00182 char mfs::renameFile(char oldFilename[20], char newFilename[20])
00183 {
00184     uint32_t block;
00185     char cData[1];
00186 
00187     // Check if file exists
00188     if (getFirstBlockOfFile(oldFilename, &block) != 0)
00189         return 1; // File not found
00190     
00191     // Check credentials
00192     read(cData, block, 0, 1);
00193      char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3);
00194      if ((flags & 0x04) != 0)
00195         return 2; // RO file
00196     
00197     write(newFilename, block, RB, 20);
00198     
00199     return 0; // Everything went better than expected
00200 }
00201 
00202 char mfs::setFileFlags(char *flags, char filename[20])
00203 {
00204     /* RO|HIDDEN|LOCK  *
00205      * H            L */
00206     
00207     uint32_t n;
00208     char cData[1] = {'\0'};
00209     char cFlags;
00210 
00211     // Check if file exists
00212     if (getFirstBlockOfFile(filename, &n) != 0)
00213         return 1; // File not found
00214     
00215     read(cData, n, 0, 1);
00216     cFlags    = ((flags[0] & 0x01)|((flags[0] & 0x02) << 3)|((flags[0] & 0x04) << 3));
00217     cData[0]  = cData[0] & (~0x31) | cFlags;
00218     write(cData, n, 0, 1);
00219     
00220     return 0;
00221 }
00222 
00223 char mfs::getFileFlags(char *flags, char filename[20])
00224 {
00225     /* RO|HIDDEN|LOCK  *
00226      * H            L */
00227     
00228     uint32_t n;
00229     char cData[1] = {'\0'};
00230 
00231     // Check if file exists
00232     if (getFirstBlockOfFile(filename, &n) != 0)
00233         return 1; // File not found
00234     
00235     read(cData, n, 0, 1);
00236     flags[0] = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3);
00237     
00238     return 0;
00239 }
00240 
00241 // Return number of free blocks
00242 uint32_t mfs::free()
00243 {
00244     uint32_t blocks=0;
00245     uint32_t r;
00246     char cFlags[1];
00247     
00248     for (r=0; r < BC; r++)
00249     {
00250         read(cFlags, r, 0, 1);
00251         if (cFlags[0] == 0x04)
00252             blocks++;
00253         if (r >= BC-1)
00254             return blocks;
00255     }
00256     
00257     return 0;
00258 }
00259 
00260 uint32_t mfs::mkfs(bool createLabel)
00261 {
00262     uint32_t iAddr = 0;
00263     uint32_t i = 0;
00264     uint32_t bad = 0; // For counting bad block headers
00265     char cFlags[RB] = {'\0', '\0', '\0', '\0', '\0'}, o[1];
00266     
00267     if (createLabel == true)
00268     {
00269         // Write Volume label
00270         cFlags[0] = '\x0E';
00271         mem->write(cFlags, iAddr, RB);
00272         iAddr = BS;
00273         i = 1;
00274     }
00275     
00276     cFlags[0] = '\x04';
00277     for (; i < BC; i++)
00278     {
00279         mem->write(cFlags, iAddr, RB);
00280         mem->read(iAddr, 1, o);
00281         if (o[0] != cFlags[0])
00282             bad++;
00283         iAddr += BS;
00284     }
00285     
00286     return bad;
00287 }
00288 
00289 file::file(mfs *fs_ref, char filename[20], FileOpenMode operation)
00290 {
00291     fMode = operation;
00292     
00293     fs = fs_ref; // Don't forget this :)
00294     
00295     uint32_t n;
00296     char cData[1] = {'\0'};
00297 
00298     // Check if file exists
00299     if (fs->getFirstBlockOfFile(filename, &n) != 0)
00300         error("Oops, file \"%s\" not found! n=0x%X", filename, n); // File not found
00301     
00302     fs->read(cData, n, 0, 1);
00303     char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3);
00304     
00305     if ((fMode != RO) && ((flags & 0x04) != 0))
00306         error("Oops, cant open in RW mode!");
00307 
00308     // Store FBOF number
00309     firstBlock = n;
00310     currBlock = n;
00311     blockPos = RB+20; // skip flags + pointers + filename
00312     byteCount = 0; // First byte of the file
00313 
00314     // Initialize buffer
00315     for (unsigned int i=0; i < BUF; i++)
00316         buffer[i] = '\0';
00317     bufPos = 0;
00318 }
00319 
00320 file::~file()
00321 {
00322     flush();
00323 }
00324 
00325 char file::getBlockLink(BlockLinkType linkSelection, uint32_t *blockOut)
00326 {
00327     char cData[1+BLOCK_LLEN];
00328 
00329     if (linkSelection == NEXT)
00330     {
00331         // Fetch link to next block
00332         fs->read(cData, currBlock, 0, 1+BLOCK_LLEN);
00333         if ((cData[0] & 0x40) == 0)
00334         {
00335             *blockOut  = ((uint32_t)(cData[1])) << 8; // Hbyte of next block link
00336             *blockOut |= (uint32_t)cData[2];          // Lbyte of next block link
00337             return 0;
00338         } else return 1; // Already at last block
00339     } else if (linkSelection == PREV)
00340     {
00341         if (currBlock != firstBlock)
00342         {
00343             fs->read(cData, currBlock, 1+BLOCK_LLEN, BLOCK_LLEN);
00344             *blockOut  = ((uint32_t)(cData[0])) << 8; // Hbyte of next block link
00345             *blockOut |= (uint32_t)cData[1];          // Lbyte of next block link
00346             return 0;
00347         } else return 1; // Already at first block
00348     }
00349     
00350     return 0;
00351 }
00352 
00353 char file::removeFollowingBlocks(uint32_t block)
00354 {
00355     char cData[RB-BLOCK_LLEN];
00356     char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'};
00357     uint32_t i=0;
00358     
00359     while(1)
00360     {
00361         fs->read(cData, block, 0, RB-BLOCK_LLEN);
00362         fs->write(cDataNew, block, 0, RB);
00363         if ((cData[0] & 0x4C) == 0x4C)
00364             break; // End of file found
00365         else block = (uint32_t)(cData[0])<<8|cData[1]; // Set next block number
00366         i++;
00367         if (i > BC)
00368             return 1; // fs is corrupted
00369     }
00370     
00371     return 0;
00372 }
00373 
00374 void file::rewind()
00375 {
00376     flush();
00377     currBlock = firstBlock;
00378     blockPos = RB+20; // skip flags & pointers + filename
00379     byteCount = 0;
00380 }
00381 
00382 char file::rewind(uint32_t n)
00383 {
00384     uint32_t i;
00385     uint32_t block;
00386     uint32_t varBlockOffset;
00387     
00388     flush(); // Check if flush is needed
00389     
00390     for (i=0; i < n; i++)
00391     {
00392         blockPos--;
00393         byteCount--;
00394     
00395         // Change Block?
00396         if (blockPos == firstBlock)
00397             varBlockOffset = RB+20;
00398         else
00399             varBlockOffset = RB;
00400         if ((blockPos < varBlockOffset) && (currBlock > firstBlock))
00401         {
00402             // Fetch link to previous block
00403             if(getBlockLink(PREV, &block) == 0)
00404             {
00405                 currBlock = block;
00406                 blockPos  = BS-1; // blockPos should be set at the end of block
00407             } else {
00408                 blockPos++;
00409                 byteCount++;
00410                 return 1; // This is the first block and byte
00411             }
00412         }
00413     }
00414     
00415     return 0; // OK
00416 }
00417 
00418 char file::forward()
00419 {
00420     return forward(1);
00421 }
00422 
00423 char file::forward(uint32_t n)
00424 {
00425     uint32_t i;
00426     uint32_t block; // Next block number
00427     char cData[1];
00428 
00429     flush(); // Check if flush is needed
00430     
00431     for (i=0; i < n; i++)
00432     {
00433         // If this is empty file EOF will be at first byte
00434         fs->read(cData, currBlock, blockPos, 1);
00435         if (cData[0] == mEOF)
00436         {
00437             return 1;
00438         }
00439         
00440         blockPos++;
00441         byteCount++;
00442     
00443         // Change Block?
00444         if (blockPos >= BS)
00445         {
00446             // Fetch link to next block
00447             if (getBlockLink(NEXT, &block) == 0)
00448             {
00449                 currBlock = block;
00450                 blockPos = RB; // Reset block position offset
00451             } else {
00452                 blockPos--;
00453                 byteCount--;
00454                 return 1; // This is the last block & byte
00455             }
00456         }
00457         fs->read(cData, currBlock, blockPos, 1);
00458         if (cData[0] == mEOF)
00459         {
00460             rewind(1); // Get back to the byte before EOF
00461             return 1;
00462         }
00463     }
00464     
00465     return 0; // OK
00466 }
00467 
00468 char file::seek(uint32_t byte)
00469 {
00470     if (byte > byteCount)
00471         return forward(byte-byteCount);
00472     else if (byte < byteCount)
00473         return rewind(byteCount-byte);
00474     else return 0;
00475 }
00476 
00477 // Respects mEOF and automatically sets '\0' at the end of string
00478 void file::read(char *data, uint32_t n)
00479 {
00480     uint32_t i;
00481     uint32_t block;
00482     char cData[1];
00483     
00484     flush();
00485     
00486     for (i=0; i < n; i++)
00487     {
00488         // Change block?
00489         if (blockPos >= BS-1)
00490         {
00491             // Fetch link to next block
00492             if (getBlockLink(NEXT, &block) == 0)
00493             {
00494                 currBlock = block;
00495                 blockPos  = RB; // Reset block position offset
00496             } else goto stop;
00497         }
00498         
00499         // Read data
00500         fs->read(cData, currBlock, blockPos, 1);
00501         if (cData[0] == mEOF)
00502         {
00503             stop:
00504             data[i]='\0';
00505             return;
00506         } else {
00507             data[i] = cData[0];
00508             blockPos++;
00509             byteCount++;
00510         }
00511     }
00512     if (data[n-1] != '\0')
00513         data[n-1] = '\0';
00514 }
00515 
00516 // Ignores mEOF and doesn't set '\0' markings
00517 void file::readBin(char *data, uint32_t n)
00518 {
00519     uint32_t i;
00520     uint32_t block;
00521     char cData[1];
00522     
00523     for (i=0; i < n; i++)
00524     {
00525         // Change block?
00526         if (blockPos == BS-1)
00527         {
00528             // Fetch link to next block
00529             if (getBlockLink(NEXT, &block) == 0)
00530             {
00531                 currBlock = block;
00532                 blockPos = RB; // Reset block position offset
00533             } else return;
00534         }
00535         
00536         // Read data
00537         fs->read(cData, currBlock, blockPos, 1);
00538         data[i] = cData[0];
00539         
00540         blockPos++;
00541         byteCount++;
00542     }
00543 }
00544 
00545 // Always binary
00546 char file::write(char *data, uint32_t n)
00547 {
00548     if (fMode == RO) return 1;
00549     
00550     for (uint32_t i=0; i < n; i++)
00551     {
00552         // write to the buffer
00553         buffer[bufPos] = data[i];
00554         bufPos++;
00555         
00556         // If the buffer is full then flush
00557         if(bufPos == BUF)
00558         {
00559             if(flush() != 0);
00560                 return 1; // Flush failed
00561         }
00562     }
00563     
00564     return 0;
00565 }
00566 
00567 char file::flush()
00568 {
00569     char cData[RB], cDataOB[RB-BLOCK_LLEN], c[1];
00570     uint32_t nextFree;
00571     uint32_t i;
00572     uint32_t leftSpaceEOF;
00573     
00574     bool destructiveFlag = false; // Set this true if there is any data to be removed
00575     uint32_t destructiveBlock=0;  // First block to be removed by DWRITE
00576     
00577     static bool fEOFow = false;   /* END of file found while appending (and overwritten).
00578                                      Must be static because we don't want to found many EOF's. */
00579     
00580     
00581     if (bufPos == 0) return 0; // File up-to date
00582     if (fMode == RO) return 1;
00583     
00584     for (i=0; i <= bufPos; i++)
00585     {
00586         if(i%2)
00587             FlushLed = 1;
00588         else
00589             FlushLed = 0;
00590         
00591         // Change Block?
00592         if ((bufPos - i) == 1)
00593             leftSpaceEOF = 1;
00594         else
00595             leftSpaceEOF = 0;
00596         if (blockPos >= BS-leftSpaceEOF)
00597         {
00598             // Fetch new unused block number
00599             if(fs->getNextFreeBlock(&nextFree) != 0)
00600                 return 2; // No free space left
00601             
00602             // Read flags from current block
00603             fs->read(cDataOB, currBlock, 0, RB-BLOCK_LLEN);
00604             
00605             /* If destructive write is set then check if there is something
00606                to be marked for removal */
00607             if (((cDataOB[0] & 0x40) != 0x40) && (fMode == DWRITE))
00608             {
00609                 destructiveFlag = true;
00610                 destructiveBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2];
00611                 goto allocate_new_block;
00612             } else if ((cDataOB[0] & 0x40) != 0x40) // fMode == AWRITE
00613             {
00614                 // Update current block info
00615                 currBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2];
00616                 blockPos  = RB; // Reset block position offset
00617             } else // There is no block to append so we allocate a new one
00618             {
00619                 allocate_new_block:
00620                 // Allocate new block for use
00621                 cData[0] = 0x4C;                              // New flags
00622                 cData[1] = '\0';                              // Hbyte of Next Block link
00623                 cData[2] = '\0';                              // Lbyte of Next Block link
00624                 cData[3] = (char)((currBlock & 0xff00) >> 8); // Hbyte of Prev Block link
00625                 cData[4] = (char)(currBlock & 0x00ff);        // Lbyte of Prev Block link
00626                 fs->write(cData, nextFree, 0, RB);            // Update Block Data
00627             
00628                 // Link old block with new block
00629                 cDataOB[0] &= ~0x40;                             // Clear LBOF flag if set
00630                 cDataOB[1]  = (char)((nextFree & 0xff00) >> 8);  // Hbyte of Next Block link
00631                 cDataOB[2]  = (char)(nextFree & 0x00ff);         // Lbyte of Next Block link
00632                 fs->write(cDataOB, currBlock, 0, RB-BLOCK_LLEN); // Update Block Data
00633             
00634                 // Update current block info
00635                 currBlock = nextFree;
00636                 blockPos  = RB; // Reset block position offset
00637             }
00638         }
00639         
00640         if (fMode == AWRITE) // Check if EOF is here
00641         {
00642             fs->read(c, currBlock, blockPos, 1);
00643             if ((c[0] == mEOF) && (fEOFow == false))
00644                 fEOFow = true;
00645         }
00646         
00647         // Write file
00648         c[0]=buffer[i];
00649         fs->write(c, currBlock, blockPos, 1);
00650         blockPos++;
00651         byteCount++;
00652         
00653         // For fail safe, write EOF now
00654         if ((fMode == DWRITE)||(fEOFow == true))
00655             fs->write((char[]){mEOF}, currBlock, blockPos, 1); // Write mEOF
00656     }
00657     
00658     bufPos = 0; // Reset buffer position counter
00659     
00660     /* If destructive write flag is set
00661        and there is data to be removed then remove data now */
00662     if (destructiveFlag == true)
00663         if(removeFollowingBlocks(destructiveBlock) != 0)
00664             return 3;
00665         
00666     FlushLed = 0;
00667     return 0;
00668 }