A file system which can mount, read, and enumerate a file system image which has been appended to the compiled .bin code file before being uploaded to the mbed device.

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers FlashFileSystem.cpp Source File

FlashFileSystem.cpp

00001 /* Copyright 2011 Adam Green (http://mbed.org/users/AdamGreen/)
00002 
00003    Licensed under the Apache License, Version 2.0 (the "License");
00004    you may not use this file except in compliance with the License.
00005    You may obtain a copy of the License at
00006 
00007        http://www.apache.org/licenses/LICENSE-2.0
00008 
00009    Unless required by applicable law or agreed to in writing, software
00010    distributed under the License is distributed on an "AS IS" BASIS,
00011    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012    See the License for the specific language governing permissions and
00013    limitations under the License.
00014 */
00015 /* Specifies the classes used to implement the FlashFileSystem which is a
00016    read-only file system that exists in the internal FLASH of the mbed
00017    device.
00018 */
00019 #include <mbed.h>
00020 #include <assert.h>
00021 #include "FlashFileSystem.h"
00022 #include "ffsformat.h"
00023 
00024 
00025 // Set FFS_TRACE to 1 to enable tracing within the FlashFileSystem class.
00026 #define FFS_TRACE 0
00027 #if FFS_TRACE
00028     #define TRACE printf
00029 #else
00030     static void __trace(...)
00031     {
00032         return;
00033     }
00034     #define TRACE __trace
00035 #endif // FFS_TRACE
00036 
00037 
00038 
00039 /* Constructor for FlashFileSystemFileHandle which initializes to the specified
00040    file entry in the image.
00041    
00042    Parameters:
00043     pFileStart is the beginning offset of this file in FLASH memory.
00044     pFileEnd is the ending offset (1 bytes past last valid byte) of this file
00045         FLASH memory.
00046 */
00047 FlashFileSystemFileHandle::FlashFileSystemFileHandle(const char* pFileStart,
00048                                                      const char* pFileEnd)
00049 {
00050     m_pFileStart = pFileStart;
00051     m_pFileEnd = pFileEnd;
00052     m_pCurr = pFileStart;
00053 }
00054 
00055 
00056 // Constructs a blank FlashFileSystemFileHandle object.
00057 FlashFileSystemFileHandle::FlashFileSystemFileHandle()
00058 {
00059     FlashFileSystemFileHandle(NULL, NULL);
00060 }
00061     
00062 
00063 /* Write the contents of a buffer to the file
00064 
00065    Parameters:
00066     pBuffer is the buffer from which to write.
00067     Length is the number of characters to write.
00068 
00069    Returns
00070     The number of characters written (possibly 0) on success, -1 on error.
00071  */
00072 ssize_t FlashFileSystemFileHandle::write(const void* pBuffer, size_t Length)
00073 {
00074     // This file system doesn't support writing.
00075     return -1;
00076 }
00077 
00078 
00079 /* Close the file
00080 
00081    Returns
00082     Zero on success, -1 on error.
00083 */
00084 int FlashFileSystemFileHandle::close()
00085 {
00086     m_pFileStart = NULL;
00087     m_pFileEnd = NULL;
00088     m_pCurr = NULL;
00089     
00090     return 0;
00091 }
00092 
00093 /*  Reads the contents of the file into a buffer.
00094 
00095    Parameters
00096     pBuffer is the buffer into which the read should occur.
00097     Length is the number of characters to read into pBuffer.
00098 
00099    Returns
00100     The number of characters read (zero at end of file) on success, -1 on error.
00101 */
00102 ssize_t FlashFileSystemFileHandle::read(void* pBuffer, size_t Length)
00103 {
00104     unsigned int    BytesLeft;
00105 
00106     // Don't read more bytes than what are left in the file.
00107     BytesLeft = m_pFileEnd - m_pCurr;
00108     if (Length > BytesLeft)
00109     {
00110         Length = BytesLeft;
00111     }
00112     
00113     // Copy the bytes from FLASH into the caller provided buffer.
00114     memcpy(pBuffer, m_pCurr, Length);
00115     
00116     // Update the file pointer.
00117     m_pCurr += Length;
00118     
00119     return Length;
00120 }
00121 
00122 
00123 /* Check if the handle is for a interactive terminal device .
00124 
00125    Parameters
00126     None
00127    Returns
00128     1 if it is a terminal, 0 otherwise
00129 */
00130 int FlashFileSystemFileHandle::isatty()
00131 {
00132     return 0;
00133 }
00134 
00135 
00136 /* Move the file position to a given offset from a given location.
00137  
00138    Parameters
00139     offset is the offset from whence to move to.
00140     whence - SEEK_SET for the start of the file, 
00141              SEEK_CUR for the current file position, or 
00142              SEEK_END for the end of the file.
00143   
00144    Returns
00145     New file position on success, -1 on failure or unsupported
00146 */
00147 off_t FlashFileSystemFileHandle::lseek(off_t offset, int whence)
00148 {
00149     switch(whence)
00150     {
00151     case SEEK_SET:
00152         m_pCurr = m_pFileStart + offset;
00153         break;
00154     case SEEK_CUR:
00155         m_pCurr += offset;
00156         break;
00157     case SEEK_END:
00158         m_pCurr = (m_pFileEnd - 1) + offset;
00159         break;
00160     default:
00161         TRACE("FlashFileSytem: Received unknown origin code (%d) for seek.\r\n", whence);
00162         return -1;
00163     }
00164     
00165     return (m_pCurr - m_pFileStart);
00166 }
00167 
00168 
00169 /* Flush any buffers associated with the FileHandle, ensuring it
00170    is up to date on disk.  Since the Flash file system is read-only, there
00171    is nothing to do here.
00172   
00173    Returns
00174     0 on success or un-needed, -1 on error
00175 */
00176 int FlashFileSystemFileHandle::fsync()
00177 {
00178     return 0;
00179 }
00180 
00181 
00182 /* Returns the length of the file.
00183 
00184    Parameters:
00185     None
00186    Returns:
00187     Length of file.
00188 */
00189 off_t FlashFileSystemFileHandle::flen()
00190 {
00191     return (m_pFileEnd - m_pFileStart);
00192 }
00193 
00194 
00195 
00196 /* Construct and initialize a directory handle enumeration object.
00197 
00198    Parameters:
00199     pFLASHBase points to the beginning of the file system in FLASH memory.
00200     pFirstFileEntry is a pointer to the first entry found in this directory.
00201     DirectoryNameLength is the length of the directory name for which this
00202         handle is being used to enumerate.
00203         
00204    Returns:
00205     Nothing.
00206 */
00207 FlashFileSystemDirHandle::FlashFileSystemDirHandle(const char*              pFLASHBase,
00208                                                    const _SFileSystemEntry* pFirstFileEntry,
00209                                                    unsigned int             FileEntriesLeft,
00210                                                    unsigned int             DirectoryNameLength)
00211 {
00212     m_pFLASHBase = pFLASHBase;
00213     m_pFirstFileEntry = pFirstFileEntry;
00214     m_pCurrentFileEntry = pFirstFileEntry;
00215     m_FileEntriesLeft = FileEntriesLeft;
00216     m_DirectoryNameLength = DirectoryNameLength;
00217     m_DirectoryEntry.d_name[0] = '\0';
00218 }
00219 
00220 
00221 // Used to construct a closed directory handle.
00222 FlashFileSystemDirHandle::FlashFileSystemDirHandle()
00223 {
00224     FlashFileSystemDirHandle(NULL, NULL, 0, 0);
00225 }
00226 
00227                              
00228 /* Closes the directory enumeration object.
00229   
00230    Parameters:
00231     None.
00232     
00233    Returns:
00234     0 on success, or -1 on error.
00235 */
00236 int FlashFileSystemDirHandle::closedir()
00237 {
00238     m_pFLASHBase = NULL;
00239     m_pFirstFileEntry = NULL;
00240     m_pCurrentFileEntry = NULL;
00241     m_FileEntriesLeft = 0;
00242     m_DirectoryNameLength = 0;
00243     m_DirectoryEntry.d_name[0] = '\0';
00244     
00245     return 0;
00246 }
00247 
00248 
00249 /* Return the directory entry at the current position, and
00250    advances the position to the next entry.
00251   
00252    Parameters:
00253     None.
00254     
00255    Returns:
00256     A pointer to a dirent structure representing the
00257     directory entry at the current position, or NULL on reaching
00258     end of directory or error.
00259 */
00260 struct dirent* FlashFileSystemDirHandle::readdir()
00261 {
00262     const char*  pPrevEntryName;
00263     const char*  pCurrentEntryName;
00264     char*        pSlash;
00265     size_t       PrefixLength;
00266     unsigned int FileEntriesUsed;
00267     unsigned int FileEntriesLeft;  
00268     
00269     // Just return now if we have already finished enumerating the entries in
00270     // the directory.
00271     if (!m_pCurrentFileEntry)
00272     {
00273         m_DirectoryEntry.d_name[0] = '\0';
00274         return NULL;
00275     }
00276     
00277     // Calculate the number of valid entries are left in the file entry array.
00278     FileEntriesUsed = m_pCurrentFileEntry - m_pFirstFileEntry;
00279     FileEntriesLeft = m_FileEntriesLeft - FileEntriesUsed;
00280     
00281     // Fill in the directory entry structure for the current entry.
00282     pPrevEntryName = m_pFLASHBase + 
00283                      m_pCurrentFileEntry->FilenameOffset;
00284     strncpy(m_DirectoryEntry.d_name, 
00285             pPrevEntryName + m_DirectoryNameLength, 
00286             sizeof(m_DirectoryEntry.d_name));
00287     m_DirectoryEntry.d_name[sizeof(m_DirectoryEntry.d_name) - 1] = '\0';
00288     
00289     // If the entry to be returned contains a slash then this is a directory
00290     // entry.
00291     pSlash = strchr(m_DirectoryEntry.d_name, '/');
00292     if (pSlash)
00293     {
00294         // I am truncating everything after the slash but leaving the
00295         // slash so that I can tell it is a directory and not a file.
00296         pSlash[1] = '\0';
00297     }
00298     
00299     // Skip entries that have the same prefix as the current entry.  This
00300     // will skip the files in the same sub-tree.
00301     PrefixLength = strlen(m_DirectoryEntry.d_name) + m_DirectoryNameLength;
00302     do
00303     {
00304         m_pCurrentFileEntry++;
00305         FileEntriesLeft--;
00306         pCurrentEntryName = m_pFLASHBase + m_pCurrentFileEntry->FilenameOffset;
00307     } while (FileEntriesLeft && 
00308              0 == strncmp(pPrevEntryName, pCurrentEntryName, PrefixLength));
00309     
00310     // If we have walked past the end of all file entries in the file system or
00311     // the prefix no longer matches this directory, then there are no more files
00312     // for this directory enumeration.
00313     if (0 == FileEntriesLeft || 
00314         0 != strncmp(pPrevEntryName, pCurrentEntryName, m_DirectoryNameLength))
00315     {
00316         m_pCurrentFileEntry = NULL;
00317     }
00318     
00319     // Return a pointer to the directory entry structure that was previously
00320     // setup.
00321     return &m_DirectoryEntry;
00322 }
00323 
00324 
00325 //Resets the position to the beginning of the directory.
00326 void FlashFileSystemDirHandle::rewinddir()
00327 {
00328     m_pCurrentFileEntry = m_pFirstFileEntry;
00329 }
00330 
00331 
00332 /* Returns the current position of the DirHandle.
00333  
00334    Parameters:
00335     None.
00336     
00337    Returns:
00338     The current position, or -1 on error.
00339 */
00340 off_t FlashFileSystemDirHandle::telldir()
00341 {
00342     return (off_t)m_pCurrentFileEntry;
00343 }
00344 
00345 
00346 /* Sets the position of the DirHandle.
00347  
00348    Parameters:
00349     Location is the location to seek to. Must be a value returned
00350         by telldir.
00351         
00352    Returns;
00353     Nothing.
00354 */
00355 void FlashFileSystemDirHandle::seekdir(off_t Location)
00356 {
00357     SFileSystemEntry*   pLocation = (SFileSystemEntry*)Location;
00358     
00359     assert ( NULL != pLocation &&
00360              pLocation > m_pFirstFileEntry &&
00361              (pLocation - m_pFirstFileEntry) < m_FileEntriesLeft );
00362     
00363     m_pCurrentFileEntry = pLocation;
00364 }
00365 
00366 
00367 
00368 // Structure used to hold context about current filename search.
00369 struct SSearchContext
00370 {
00371     // Name of file to be found in file system image.
00372     const char* pKey;
00373     // Base pointer for the file system image.
00374     const char* pFLASHBase;
00375 };
00376 
00377 
00378 /* Internal routine used as callback for bsearch() when searching for a
00379    requested filename in the FLASH file system image.
00380    
00381    pvKey is a pointer to the SSearchContext object for this search.
00382    pvEntry is a pointer to the current file system entry being checked by
00383     bsearch().
00384     
00385    Returns <0 if filename to find is lower in sort order than current entry.
00386             0 if filename to find is the same as the current entry.
00387            >0 if filename to find is higher in sort order than current entry.
00388 */
00389 static int _CompareKeyToFileEntry(const void* pvKey, const void* pvEntry)
00390 {
00391     const SSearchContext*       pContext = (const SSearchContext*)pvKey;
00392     const char*                 pKey = pContext->pKey;
00393     const SFileSystemEntry*     pEntry = (const SFileSystemEntry*)pvEntry;
00394     const char*                 pEntryName = pContext->pFLASHBase + pEntry->FilenameOffset;
00395 
00396     return strcmp(pKey, pEntryName);
00397 }
00398 
00399 
00400 /* Constructor for FlashFileSystem
00401 
00402    Parameters:
00403     pName is the root name to be used for this file system in fopen()
00404         pathnames.
00405     pFlashDrive (optional) is a pointer to the read-only file system (const char array).
00406         When pFlashDrive is not specified, it is up to the user to append the
00407         read-only file system file to the compiled binary.
00408     FlashSize (optional) is the size of the FLASH (KB) on the device to
00409         search through for the file system signature (default = 512).
00410 */
00411 FlashFileSystem::FlashFileSystem(const char* pName, const uint8_t *pFlashDrive, const uint32_t FlashSize) : FileSystemLike(pName)
00412 {
00413     static const char   FileSystemSignature[] = FILE_SYSTEM_SIGNATURE;
00414     SFileSystemHeader*  pHeader = NULL;
00415     char*               pCurr = (char*)(FlashSize * 1024) - sizeof(pHeader->FileSystemSignature);
00416     
00417     // Initialize the members
00418     m_pFLASHBase = NULL;
00419     m_FileCount = 0;
00420     m_pFileEntries = NULL;
00421     
00422     // Scan backwards through 512k FLASH looking for the file system signature
00423     // NOTE: The file system image should be located after this code itself
00424     //       so stop the search.
00425     if(pFlashDrive == NULL)
00426     {
00427         while (pCurr > FileSystemSignature)
00428         {
00429             if (0 == memcmp(pCurr, FileSystemSignature, sizeof(pHeader->FileSystemSignature)))
00430             {
00431                 break;
00432             }
00433             pCurr--;
00434         }
00435         if (pCurr <= FileSystemSignature)
00436         {
00437             TRACE("FlashFileSystem: Failed to find file system image in ROM.\n");
00438             return;
00439         }
00440     }
00441     else
00442     {
00443         pCurr = (char *)pFlashDrive;
00444     }
00445     if (((unsigned int)pCurr & 0x3) != 0)
00446     {
00447         TRACE("FlashFileSystem: File system image at address %08X isn't 4-byte aligned.\n", pCurr);
00448         return;
00449     }
00450     
00451     // Record the location of the file system image in the member fields.
00452     m_pFLASHBase = pCurr;
00453     pHeader = (SFileSystemHeader*)m_pFLASHBase;
00454     m_FileCount = pHeader->FileCount;
00455     m_pFileEntries = (SFileSystemEntry*)(m_pFLASHBase + sizeof(*pHeader));
00456 }
00457 
00458 
00459 /* Opens specified file in FLASH file system when an appropriate call to
00460    fopen() is made.
00461    
00462    pFilename is the name of the file to be opened within the file system.
00463    Flags specify flags to determine open mode of file.  This file system only
00464     support O_RDONLY.
00465    
00466    Returns NULL if an error was encountered or a pointer to a FileHandle object
00467     representing the requrested file otherwise.
00468 */
00469 FileHandle* FlashFileSystem::open(const char* pFilename, int Flags)
00470 {
00471     const SFileSystemEntry*     pEntry = NULL;
00472     FlashFileSystemFileHandle*  pFileHandle = NULL;
00473     SSearchContext              SearchContext;
00474     
00475     TRACE("FlashFileSystem: Attempt to open file /FLASH/%s with flags:%x\r\n", pFilename, Flags);
00476     
00477     // Can't find the file if file system hasn't been mounted.
00478     if (!IsMounted())
00479     {
00480         return NULL;
00481     }
00482 
00483     // Can only open files in FLASH for read.
00484     if (O_RDONLY != Flags)
00485     {
00486         TRACE("FlashFileSystem: Can only open files for reading.\r\n");
00487     }
00488     
00489     // Attempt to find the specified file in the file system image.
00490     SearchContext.pKey = pFilename;
00491     SearchContext.pFLASHBase = m_pFLASHBase;
00492     pEntry = (const SFileSystemEntry*) bsearch(&SearchContext,
00493                                                m_pFileEntries,
00494                                                m_FileCount, 
00495                                                sizeof(*m_pFileEntries), 
00496                                                _CompareKeyToFileEntry);
00497     if(!pEntry)
00498     {
00499         // Create failure response.
00500         TRACE("FlashFileSystem: Failed to find '%s' in file system image.\n", pFilename);
00501         return NULL;
00502     }
00503 
00504     // Attempt to find a free file handle.
00505     pFileHandle = FindFreeFileHandle();
00506     if (!pFileHandle)
00507     {
00508         TRACE("FlashFileSystem: File handle table is full.\n");
00509         return NULL;
00510     }
00511     
00512     // Initialize the file handle and return it to caller.
00513     pFileHandle->SetEntry(m_pFLASHBase + pEntry->FileBinaryOffset,
00514                           m_pFLASHBase + pEntry->FileBinaryOffset + pEntry->FileBinarySize);
00515     return pFileHandle;
00516 }
00517 
00518 DirHandle*  FlashFileSystem::opendir(const char *pDirectoryName)
00519 {
00520     const SFileSystemEntry* pEntry = m_pFileEntries;
00521     unsigned int            DirectoryNameLength;
00522     unsigned int            i;
00523     
00524     assert ( pDirectoryName);
00525     
00526     // Removing leading slash since the file system image doesn't contain
00527     // leading slashes.
00528     if ('/' == pDirectoryName[0])
00529     {
00530         pDirectoryName++;
00531     }
00532     
00533     // Make sure that the directory name length would include the trailing
00534     // slash.
00535     DirectoryNameLength = strlen(pDirectoryName);
00536     if (0 != DirectoryNameLength && '/' != pDirectoryName[DirectoryNameLength-1])
00537     {
00538         // Add the implicit slash to this count.
00539         DirectoryNameLength++;
00540     }
00541     
00542     // Search through the file entries from the beginning to find the first
00543     // entry which has pDirectoryName/ as the prefix.
00544     for (i = 0 ; i < m_FileCount ; i++)
00545     {
00546         const char* pEntryFilename = pEntry->FilenameOffset + m_pFLASHBase;
00547         
00548         if (0 == DirectoryNameLength ||
00549             (pEntryFilename == strstr(pEntryFilename, pDirectoryName) &&
00550              '/' == pEntryFilename[DirectoryNameLength-1]) )
00551         {
00552             // Found the beginning of the list of files/folders for the
00553             // requested directory so return it to the caller.
00554             FlashFileSystemDirHandle* pDirHandle = FindFreeDirHandle();
00555             if (!pDirHandle)
00556             {
00557                 TRACE("FlashFileSystem: Dir handle table is full.\n");
00558                 return NULL;
00559             }
00560             
00561             pDirHandle->SetEntry(m_pFLASHBase,
00562                                  pEntry,
00563                                  m_FileCount - (pEntry - m_pFileEntries),
00564                                  DirectoryNameLength);
00565             
00566             return pDirHandle;
00567         }
00568         
00569         // Advance to the next file entry
00570         pEntry++;
00571     }
00572     
00573     // Get here when the requested directory wasn't found.
00574     TRACE("FlashFileSystem: Failed to find '%s' directory in file system image.\n", 
00575           pDirectoryName);
00576     return NULL;
00577 }
00578 
00579 
00580 /* Protected method which attempts to find a free file handle in the object's
00581    file handle table.
00582    
00583    Parameters:
00584     None
00585     
00586    Returns:
00587     Pointer to first free file handle entry or NULL if the table is full.
00588 */
00589 FlashFileSystemFileHandle* FlashFileSystem::FindFreeFileHandle()
00590 {
00591     size_t  i;
00592     
00593     // Iterate through the file handle array, looking for a close file handle.
00594     for (i = 0 ; i < sizeof(m_FileHandles)/sizeof(m_FileHandles[0]) ; i++)
00595     {
00596         if (m_FileHandles[i].IsClosed())
00597         {
00598             return &(m_FileHandles[i]);
00599         }
00600     }
00601     
00602     // If we get here, then no free entries were found.
00603     return NULL;
00604 }
00605 
00606 
00607 /* Protected method which attempts to find a free dir handle in the object's
00608    directory handle table.
00609    
00610    Parameters:
00611     None
00612     
00613    Returns:
00614     Pointer to first free directory handle entry or NULL if the table is full.
00615 */
00616 FlashFileSystemDirHandle* FlashFileSystem::FindFreeDirHandle()
00617 {
00618     size_t  i;
00619     
00620     // Iterate through the direcotry handle array, looking for a closed 
00621     // directory handle.
00622     for (i = 0 ; i < sizeof(m_DirHandles)/sizeof(m_DirHandles[0]) ; i++)
00623     {
00624         if (m_DirHandles[i].IsClosed())
00625         {
00626             return &(m_DirHandles[i]);
00627         }
00628     }
00629     
00630     // If we get here, then no free entries were found.
00631     return NULL;
00632 }