Johny Mattsson c4e8b04fbf Cleaned up all uses of INTERNAL_FLASH_START_ADDRESS.
There was only one genuine use of this macro, all other places were
using it only as a necessary compensation. While this was fine as long as
it was the first meg of flash which was mapped, it became incorrect and
quite dangerous whenever this assumption did not hold (such as when
running from the second slot in an OTA scenario).

The flash API now uses actual addresses, not translated/mapped
addresses, and the users of this API have been adjusted accordingly.
This makes the flash API work correctly regardless of what flash mapping
is in use.

The old macro is still available under the new name
INTERNAL_FLASH_MAPPED_ADDRESS, and this is used to detect flash writes
where the source is mapped flash (and thus has to be bounced), and to
adjust the _flash_used_end linker symbol when used with
flassh_find_sector() by the filesystem code. The latter usage is not
OTA-proof, but in an OTA scenario the filesystem needs a fixed location
anyway and thus would not use this code path.
2015-12-12 13:20:22 +11:00

566 lines
16 KiB
C

// Filesystem implementation
#include "romfs.h"
#include "c_string.h"
// #include "c_errno.h"
#include "romfiles.h"
#include "c_stdio.h"
// #include "c_stdlib.h"
#include "c_fcntl.h"
#include "platform.h"
#if defined( BUILD_ROMFS ) || defined( BUILD_WOFS )
#define TOTAL_MAX_FDS 8
// DO NOT CHANGE THE ROMFS ALIGNMENT.
// UNLESS YOU _LIKE_ TO WATCH THE WORLD BURN.
#define ROMFS_ALIGN 4
#define fsmin( x , y ) ( ( x ) < ( y ) ? ( x ) : ( y ) )
static FD fd_table[ TOTAL_MAX_FDS ];
static int romfs_num_fd;
#define WOFS_END_MARKER_CHAR 0xFF
#define WOFS_DEL_FIELD_SIZE ( ROMFS_ALIGN )
#define WOFS_FILE_DELETED 0xAA
// Length of the 'file size' field for both ROMFS/WOFS
#define ROMFS_SIZE_LEN 4
static int romfs_find_empty_fd(void)
{
int i;
for( i = 0; i < TOTAL_MAX_FDS; i ++ )
if( fd_table[ i ].baseaddr == 0xFFFFFFFF &&
fd_table[ i ].offset == 0xFFFFFFFF &&
fd_table[ i ].size == 0xFFFFFFFF )
return i;
return -1;
}
static void romfs_close_fd( int fd )
{
if(fd<0 || fd>=TOTAL_MAX_FDS)
return;
c_memset( fd_table + fd, 0xFF, sizeof( FD ) );
fd_table[ fd ].flags = 0;
}
// Helper function: read a byte from the FS
static uint8_t romfsh_read8( uint32_t addr, const FSDATA *pfs )
{
uint8_t temp;
if( pfs->flags & ROMFS_FS_FLAG_DIRECT )
return pfs->pbase[ addr ];
pfs->readf( &temp, addr, 1, pfs );
return temp;
}
// Helper function: return 1 if PFS reffers to a WOFS, 0 otherwise
static int romfsh_is_wofs( const FSDATA* pfs )
{
return ( pfs->flags & ROMFS_FS_FLAG_WO ) != 0;
}
// Find the next file, returning FS_FILE_OK or FS_FILE_NOT_FOUND if there no file left.
static uint8_t romfs_next_file( uint32_t *start, char* fname, size_t len, size_t *act_len, FSDATA *pfs )
{
uint32_t i, j, n;
uint32_t fsize;
int is_deleted;
// Look for the file
i = *start;
*act_len = 0;
if( (i >= INTERNAL_FLASH_SIZE) || (romfsh_read8( i, pfs ) == WOFS_END_MARKER_CHAR )) // end of file system
{
*start = (i >= INTERNAL_FLASH_SIZE)?(INTERNAL_FLASH_SIZE-1):i;
return FS_FILE_NOT_FOUND;
}
// Read file name
len = len>MAX_FNAME_LENGTH?MAX_FNAME_LENGTH:len;
for( j = 0; j < len; j ++ )
{
fname[ j ] = romfsh_read8( i + j, pfs );
if( fname[ j ] == 0 )
break;
}
n = j; // save the file name length to n
// ' i + j' now points at the '0' byte
j = i + j + 1;
// Round to a multiple of ROMFS_ALIGN
j = ( j + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 );
// WOFS has an additional WOFS_DEL_FIELD_SIZE bytes before the size as an indication for "file deleted"
if( romfsh_is_wofs( pfs ) )
{
is_deleted = romfsh_read8( j, pfs ) == WOFS_FILE_DELETED;
j += WOFS_DEL_FIELD_SIZE;
}
else
is_deleted = 0;
// And read the size
fsize = romfsh_read8( j, pfs ) + ( romfsh_read8( j + 1, pfs ) << 8 );
fsize += ( romfsh_read8( j + 2, pfs ) << 16 ) + ( romfsh_read8( j + 3, pfs ) << 24 );
j += ROMFS_SIZE_LEN;
if( !is_deleted )
{
// Found the valid file
*act_len = n;
}
// Move to next file
i = j + fsize;
// On WOFS, all file names must begin at a multiple of ROMFS_ALIGN
if( romfsh_is_wofs( pfs ) )
i = ( i + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 );
*start = i; // modify the start address
return FS_FILE_OK;
}
// Open the given file, returning one of FS_FILE_NOT_FOUND, FS_FILE_ALREADY_OPENED
// or FS_FILE_OK
static uint8_t romfs_open_file( const char* fname, FD* pfd, FSDATA *pfs, uint32_t *plast, uint32_t *pnameaddr )
{
uint32_t i, j, n;
char fsname[ MAX_FNAME_LENGTH + 1 ];
uint32_t fsize;
int is_deleted;
// Look for the file
i = 0;
while( 1 )
{
if( i >= INTERNAL_FLASH_SIZE ){
*plast = INTERNAL_FLASH_SIZE - 1; // point to last one
return FS_FILE_NOT_FOUND;
}
if( romfsh_read8( i, pfs ) == WOFS_END_MARKER_CHAR )
{
*plast = i;
return FS_FILE_NOT_FOUND;
}
// Read file name
n = i;
for( j = 0; j < MAX_FNAME_LENGTH; j ++ )
{
fsname[ j ] = romfsh_read8( i + j, pfs );
if( fsname[ j ] == 0 )
break;
}
// ' i + j' now points at the '0' byte
j = i + j + 1;
// Round to a multiple of ROMFS_ALIGN
j = ( j + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 );
// WOFS has an additional WOFS_DEL_FIELD_SIZE bytes before the size as an indication for "file deleted"
if( romfsh_is_wofs( pfs ) )
{
is_deleted = romfsh_read8( j, pfs ) == WOFS_FILE_DELETED;
j += WOFS_DEL_FIELD_SIZE;
}
else
is_deleted = 0;
// And read the size
fsize = romfsh_read8( j, pfs ) + ( romfsh_read8( j + 1, pfs ) << 8 );
fsize += ( romfsh_read8( j + 2, pfs ) << 16 ) + ( romfsh_read8( j + 3, pfs ) << 24 );
j += ROMFS_SIZE_LEN;
if( !c_strncasecmp( fname, fsname, MAX_FNAME_LENGTH ) && !is_deleted )
{
// Found the file
pfd->baseaddr = j;
pfd->offset = 0;
pfd->size = fsize;
if( pnameaddr )
*pnameaddr = n;
return FS_FILE_OK;
}
// Move to next file
i = j + fsize;
// On WOFS, all file names must begin at a multiple of ROMFS_ALIGN
if( romfsh_is_wofs( pfs ) )
i = ( i + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 );
}
*plast = 0;
return FS_FILE_NOT_FOUND;
}
static int romfs_open( const char *path, int flags, int mode, void *pdata )
{
FD tempfs;
int i;
FSDATA *pfsdata = ( FSDATA* )pdata;
int must_create = 0;
int exists;
uint8_t lflags = ROMFS_FILE_FLAG_READ;
uint32_t firstfree, nameaddr;
if( romfs_num_fd == TOTAL_MAX_FDS )
{
return -1;
}
// Does the file exist?
exists = romfs_open_file( path, &tempfs, pfsdata, &firstfree, &nameaddr ) == FS_FILE_OK;
// Now interpret "flags" to set file flags and to check if we should create the file
if( flags & O_CREAT )
{
// If O_CREAT is specified with O_EXCL and the file already exists, return with error
if( ( flags & O_EXCL ) && exists )
{
return -1;
}
// Otherwise create the file if it does not exist
must_create = !exists;
}
if( ( flags & O_TRUNC ) && ( flags & ( O_WRONLY | O_RDWR ) ) && exists )
{
// The file exists, but it must be truncated
// In the case of WOFS, this effectively means "create a new file"
must_create = 1;
}
// ROMFS can't create files
if( must_create && ( ( pfsdata->flags & ROMFS_FS_FLAG_WO ) == 0 ) )
{
return -1;
}
// Decode access mode
if( flags & O_WRONLY )
lflags = ROMFS_FILE_FLAG_WRITE;
else if( flags & O_RDWR )
lflags = ROMFS_FILE_FLAG_READ | ROMFS_FILE_FLAG_WRITE;
if( flags & O_APPEND )
lflags |= ROMFS_FILE_FLAG_APPEND;
// If a write access is requested when the file must NOT be created, this
// is an error
if( ( lflags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) ) && !must_create )
{
return -1;
}
if( ( lflags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) ) && romfs_fs_is_flag_set( pfsdata, ROMFS_FS_FLAG_WRITING ) )
{
// At most one file can be opened in write mode at any given time on WOFS
return -1;
}
// Do we need to create the file ?
if( must_create )
{
if( exists )
{
// Invalidate the file first by changing WOFS_DEL_FIELD_SIZE bytes before
// the file length to WOFS_FILE_DELETED
uint8_t tempb[] = { WOFS_FILE_DELETED, 0xFF, 0xFF, 0xFF };
pfsdata->writef( tempb, tempfs.baseaddr - ROMFS_SIZE_LEN - WOFS_DEL_FIELD_SIZE, WOFS_DEL_FIELD_SIZE, pfsdata );
}
// Find the last available position by asking romfs_open_file to look for a file
// with an invalid name
romfs_open_file( "\1", &tempfs, pfsdata, &firstfree, NULL );
// Is there enough space on the FS for another file?
if( pfsdata->max_size - firstfree + 1 < c_strlen( path ) + 1 + WOFS_MIN_NEEDED_SIZE + WOFS_DEL_FIELD_SIZE )
{
return -1;
}
// Make sure we can get a file descriptor before writing
if( ( i = romfs_find_empty_fd() ) < 0 )
{
return -1;
}
// Write the name of the file
pfsdata->writef( path, firstfree, c_strlen( path ) + 1, pfsdata );
firstfree += c_strlen( path ) + 1; // skip over the name
// Align to a multiple of ROMFS_ALIGN
firstfree = ( firstfree + ROMFS_ALIGN - 1 ) & ~( ROMFS_ALIGN - 1 );
firstfree += ROMFS_SIZE_LEN + WOFS_DEL_FIELD_SIZE; // skip over the size and the deleted flags area
tempfs.baseaddr = firstfree;
tempfs.offset = tempfs.size = 0;
// Set the "writing" flag on the FS to indicate that there is a file opened in write mode
romfs_fs_set_flag( pfsdata, ROMFS_FS_FLAG_WRITING );
}
else // File must exist (and was found in the previous 'romfs_open_file' call)
{
if( !exists )
{
return -1;
}
if( ( i = romfs_find_empty_fd() ) < 0 )
{
return -1;
}
}
// Copy the descriptor information
tempfs.flags = lflags;
c_memcpy( fd_table + i, &tempfs, sizeof( FD ) );
romfs_num_fd ++;
return i;
}
static int romfs_close( int fd, void *pdata )
{
if(fd<0 || fd>=TOTAL_MAX_FDS)
return 0;
FD* pfd = fd_table + fd;
FSDATA *pfsdata = ( FSDATA* )pdata;
uint8_t temp[ ROMFS_SIZE_LEN ];
if( pfd->flags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) )
{
// Write back the size
temp[ 0 ] = pfd->size & 0xFF;
temp[ 1 ] = ( pfd->size >> 8 ) & 0xFF;
temp[ 2 ] = ( pfd->size >> 16 ) & 0xFF;
temp[ 3 ] = ( pfd->size >> 24 ) & 0xFF;
pfsdata->writef( temp, pfd->baseaddr - ROMFS_SIZE_LEN, ROMFS_SIZE_LEN, pfsdata );
// Clear the "writing" flag on the FS instance to allow other files to be opened
// in write mode
romfs_fs_clear_flag( pfsdata, ROMFS_FS_FLAG_WRITING );
}
romfs_close_fd( fd );
romfs_num_fd --;
return 0;
}
static _ssize_t romfs_write( int fd, const void* ptr, size_t len, void *pdata )
{
if(fd<0 || fd>=TOTAL_MAX_FDS)
return -1;
if(len == 0)
return 0;
FD* pfd = fd_table + fd;
FSDATA *pfsdata = ( FSDATA* )pdata;
if( ( pfd->flags & ( ROMFS_FILE_FLAG_WRITE | ROMFS_FILE_FLAG_APPEND ) ) == 0 )
{
return -1;
}
// Append mode: set the file pointer to the end
if( pfd->flags & ROMFS_FILE_FLAG_APPEND )
pfd->offset = pfd->size;
// Only write at the end of the file!
if( pfd->offset != pfd->size )
return 0;
// Check if we have enough space left on the device. Always keep 1 byte for the final 0xFF
// and ROMFS_ALIGN - 1 bytes for aligning the contents of the file data in the worst case
// scenario (so ROMFS_ALIGN bytes in total)
if( pfd->baseaddr + pfd->size + len > pfsdata->max_size - ROMFS_ALIGN )
len = pfsdata->max_size - ( pfd->baseaddr + pfd->size ) - ROMFS_ALIGN;
pfsdata->writef( ptr, pfd->offset + pfd->baseaddr, len, pfsdata );
pfd->offset += len;
pfd->size += len;
return len;
}
static _ssize_t romfs_read( int fd, void* ptr, size_t len, void *pdata )
{
if(fd<0 || fd>=TOTAL_MAX_FDS)
return -1;
if(len == 0)
return 0;
FD* pfd = fd_table + fd;
long actlen = fsmin( len, pfd->size - pfd->offset );
FSDATA *pfsdata = ( FSDATA* )pdata;
if( ( pfd->flags & ROMFS_FILE_FLAG_READ ) == 0 )
{
return -1;
}
if( pfsdata->flags & ROMFS_FS_FLAG_DIRECT )
c_memcpy( ptr, pfsdata->pbase + pfd->offset + pfd->baseaddr, actlen );
else
actlen = pfsdata->readf( ptr, pfd->offset + pfd->baseaddr, actlen, pfsdata );
pfd->offset += actlen;
return actlen;
}
// lseek
static int romfs_lseek( int fd, int off, int whence, void *pdata )
{
if(fd<0 || fd>=TOTAL_MAX_FDS)
return -1;
FD* pfd = fd_table + fd;
uint32_t newpos = 0;
switch( whence )
{
case SEEK_SET:
newpos = off;
break;
case SEEK_CUR:
newpos = pfd->offset + off;
break;
case SEEK_END:
newpos = pfd->size + off;
break;
default:
return -1;
}
if( newpos > pfd->size )
return -1;
pfd->offset = newpos;
return newpos;
}
// ****************************************************************************
// WOFS functions and instance descriptor for real hardware
#if defined( BUILD_WOFS )
static uint32_t sim_wofs_write( const void *from, uint32_t toaddr, uint32_t size, const void *pdata )
{
const FSDATA *pfsdata = ( const FSDATA* )pdata;
if(toaddr>=INTERNAL_FLASH_SIZE)
{
NODE_ERR("ERROR in flash op: wrong addr.\n");
return 0;
}
toaddr += ( uint32_t )pfsdata->pbase;
return platform_flash_write( from, toaddr, size );
}
static uint32_t sim_wofs_read( void *to, uint32_t fromaddr, uint32_t size, const void *pdata )
{
const FSDATA *pfsdata = ( const FSDATA* )pdata;
if(fromaddr>=INTERNAL_FLASH_SIZE)
{
NODE_ERR("ERROR in flash op: wrong addr.\n");
return 0;
}
fromaddr += ( uint32_t )pfsdata->pbase;
return platform_flash_read( to, fromaddr, size );
}
// This must NOT be a const!
static FSDATA wofs_fsdata =
{
NULL,
ROMFS_FS_FLAG_WO,
sim_wofs_read,
sim_wofs_write,
0
};
// WOFS formatting function
// Returns 1 if OK, 0 for error
int wofs_format( void )
{
uint32_t sect_first, sect_last;
FD tempfd;
platform_flash_get_first_free_block_address( &sect_first );
// Get the first free address in WOFS. We use this address to compute the last block that we need to
// erase, instead of simply erasing everything from sect_first to the last Flash page.
romfs_open_file( "\1", &tempfd, &wofs_fsdata, &sect_last, NULL );
sect_last = platform_flash_get_sector_of_address( sect_last + ( uint32_t )wofs_fsdata.pbase );
while( sect_first <= sect_last )
if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR )
return 0;
return 1;
}
int wofs_open(const char *_name, int flags){
return romfs_open( _name, flags, 0, &wofs_fsdata );
}
int wofs_close( int fd ){
return romfs_close( fd, &wofs_fsdata );
}
size_t wofs_write( int fd, const void* ptr, size_t len ){
return romfs_write( fd, ptr, len, &wofs_fsdata );
}
size_t wofs_read( int fd, void* ptr, size_t len){
return romfs_read( fd, ptr, len, &wofs_fsdata );
}
int wofs_lseek( int fd, int off, int whence ){
return romfs_lseek( fd, off, whence, &wofs_fsdata );
}
int wofs_eof( int fd ){
if(fd<0 || fd>=TOTAL_MAX_FDS)
return -1;
FD* pfd = fd_table + fd;
// NODE_DBG("off:%d, sz:%d\n",pfd->offset, pfd->size);
return pfd->offset == pfd->size;
}
int wofs_getc( int fd ){
char c = EOF;
if(!wofs_eof(fd)){
romfs_read( fd, &c, 1, &wofs_fsdata );
}
// NODE_DBG("c: %d\n", c);
return (int)c;
}
int wofs_ungetc( int c, int fd ){
return romfs_lseek( fd, -1, SEEK_CUR, &wofs_fsdata );
}
// Find the next file, returning FS_FILE_OK or FS_FILE_NOT_FOUND if there no file left.
uint8_t wofs_next( uint32_t *start, char* fname, size_t len, size_t *act_len ){
return romfs_next_file( start, fname, len, act_len, &wofs_fsdata );
}
#endif // #ifdef BUILD_WOFS
// Initialize both ROMFS and WOFS as needed
int romfs_init( void )
{
unsigned i;
for( i = 0; i < TOTAL_MAX_FDS; i ++ )
{
c_memset( fd_table + i, 0xFF, sizeof( FD ) );
fd_table[ i ].flags = 0;
}
#if defined( BUILD_WOFS )
// Get the start address and size of WOFS and register it
wofs_fsdata.pbase = ( uint8_t* )platform_flash_get_first_free_block_address( NULL );
wofs_fsdata.max_size = INTERNAL_FLASH_SIZE - ( ( uint32_t )wofs_fsdata.pbase );
NODE_DBG("wofs.pbase:%x,max:%x\n",wofs_fsdata.pbase,wofs_fsdata.max_size);
#endif // ifdef BUILD_WOFS
return 0;
}
#else // #if defined( BUILD_ROMFS ) || defined( BUILD_WOFS )
int romfs_init( void )
{
}
#endif // #if defined( BUILD_ROMFS ) || defined( BUILD_WOFS )
int test_romfs()
{
int fd;
int i, size;
fd = wofs_open("init.lua",O_RDONLY);
NODE_DBG("open file fd:%d\n", fd);
char r[128];
NODE_DBG("read from file:\n");
c_memset(r,0,128);
size = wofs_read(fd,r,128);
r[size]=0;
NODE_DBG(r);
NODE_DBG("\n");
wofs_close(fd);
fd = wofs_open("testm.lua",O_RDONLY);
NODE_DBG("open file fd:%d\n", fd);
NODE_DBG("read from file:\n");
c_memset(r,0,128);
size = wofs_read(fd,r,128);
r[size]=0;
NODE_DBG(r);
NODE_DBG("\n");
wofs_close(fd);
return 0;
}