mirror of
https://github.com/nodemcu/nodemcu-firmware.git
synced 2025-01-16 20:52:57 +08:00
526d21dab4
The PR removed the bulk of non-newlib headers from the NodeMCU source base. app/libc has now been cut down to the bare minimum overrides to shadow the corresponding functions in the SDK's libc. The old c_xyz.h headerfiles have been nuked in favour of the standard <xyz.h> headers, with a few exceptions over in sdk-overrides. Again, shipping a libc.a without headers is a terrible thing to do. We're still living on a prayer that libc was configured the same was as a default-configured xtensa gcc toolchain assumes it is. That part I cannot do anything about, unfortunately, but it's no worse than it has been before. This enables our source files to compile successfully using the standard header files, and use the typical malloc()/calloc()/realloc()/free(), the strwhatever()s and memwhatever()s. These end up, through macro and linker magic, mapped to the appropriate SDK or ROM functions.
476 lines
9.2 KiB
C
476 lines
9.2 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include "vfs.h"
|
|
#include "vfs_int.h"
|
|
|
|
#define LDRV_TRAVERSAL 0
|
|
|
|
// This interferes with our clearerr member in our ops struct
|
|
#undef clearerr
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// RTC system interface
|
|
//
|
|
static int32_t (*rtc_cb)( vfs_time *tm ) = NULL;
|
|
|
|
// called by operating system
|
|
void vfs_register_rtc_cb( int32_t (*cb)( vfs_time *tm ) )
|
|
{
|
|
// allow NULL pointer to unregister callback function
|
|
rtc_cb = cb;
|
|
}
|
|
|
|
// called by file system drivers
|
|
int32_t vfs_get_rtc( vfs_time *tm )
|
|
{
|
|
if (rtc_cb) {
|
|
return rtc_cb( tm );
|
|
}
|
|
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
|
|
static int dir_level = 1;
|
|
|
|
|
|
#if ! LDRV_TRAVERSAL
|
|
|
|
#define normalize_path(p) (p)
|
|
|
|
#else
|
|
static const char *normalize_path( const char *path )
|
|
{
|
|
const char *temp = path;
|
|
size_t len;
|
|
|
|
while ((len = strlen( temp )) >= 2) {
|
|
if (temp[0] == '.' && temp[1] == '.') {
|
|
--dir_level;
|
|
if (len >= 4 && dir_level > 0) {
|
|
// prepare next step
|
|
temp = &(temp[4]);
|
|
} else {
|
|
// we're at top, the remainder is expected be an absolute path
|
|
temp = &(temp[3]);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dir_level > 0) {
|
|
// no traversal on root level
|
|
return path;
|
|
} else {
|
|
// path traverses via root
|
|
return temp;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// file system functions
|
|
//
|
|
vfs_vol *vfs_mount( const char *name, int num )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normname = normalize_path( name );
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
return fs_fns->mount( outname, num );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
vfs_vol *r = fs_fns->mount( outname, num );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int vfs_open( const char *name, const char *mode )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normname = normalize_path( name );
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
return (int)fs_fns->open( outname, mode );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
int r = (int)fs_fns->open( outname, mode );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
vfs_dir *vfs_opendir( const char *name )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normname = normalize_path( name );
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
return fs_fns->opendir( outname );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
vfs_dir *r = fs_fns->opendir( outname );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int32_t vfs_stat( const char *name, struct vfs_stat *buf )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normname = normalize_path( name );
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
return fs_fns->stat( outname, buf );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
int32_t r = fs_fns->stat( outname, buf );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
int32_t vfs_remove( const char *name )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normname = normalize_path( name );
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
return fs_fns->remove( outname );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
int32_t r = fs_fns->remove( outname );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
int32_t vfs_rename( const char *oldname, const char *newname )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normoldname = normalize_path( oldname );
|
|
const char *normnewname = normalize_path( newname );
|
|
char *oldoutname, *newoutname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (myspiffs_realm( normoldname, &oldoutname, FALSE )) {
|
|
if (fs_fns = myspiffs_realm( normnewname, &newoutname, FALSE )) {
|
|
return fs_fns->rename( oldoutname, newoutname );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (myfatfs_realm( normoldname, &oldoutname, FALSE )) {
|
|
if (fs_fns = myfatfs_realm( normnewname, &newoutname, FALSE )) {
|
|
int32_t r = fs_fns->rename( oldoutname, newoutname );
|
|
free( oldoutname );
|
|
free( newoutname );
|
|
return r;
|
|
}
|
|
free( oldoutname );
|
|
}
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
int32_t vfs_mkdir( const char *name )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normname = normalize_path( name );
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
// not supported
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
int32_t r = fs_fns->mkdir( outname );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
int32_t vfs_fsinfo( const char *name, uint32_t *total, uint32_t *used )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
char *outname;
|
|
|
|
if (!name) name = ""; // current drive
|
|
|
|
const char *normname = normalize_path( name );
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
return fs_fns->fsinfo( total, used );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
free( outname );
|
|
return fs_fns->fsinfo( total, used );
|
|
}
|
|
#endif
|
|
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
int32_t vfs_fscfg( const char *name, uint32_t *phys_addr, uint32_t *phys_size)
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( "/FLASH", &outname, FALSE )) {
|
|
return fs_fns->fscfg( phys_addr, phys_size );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
// not supported
|
|
#endif
|
|
|
|
// Error
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
int32_t vfs_format( void )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( "/FLASH", &outname, FALSE )) {
|
|
return fs_fns->format();
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
// not supported
|
|
#endif
|
|
|
|
// Error
|
|
return 0;
|
|
}
|
|
|
|
int32_t vfs_chdir( const char *path )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
const char *normpath = normalize_path( path );
|
|
const char *level;
|
|
char *outname;
|
|
int ok = VFS_RES_ERR;
|
|
|
|
#if LDRV_TRAVERSAL
|
|
// track dir level
|
|
if (normpath[0] == '/') {
|
|
dir_level = 0;
|
|
level = &(normpath[1]);
|
|
} else {
|
|
level = normpath;
|
|
}
|
|
while (strlen( level ) > 0) {
|
|
dir_level++;
|
|
if (level = strchr( level, '/' )) {
|
|
level++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normpath, &outname, TRUE )) {
|
|
// our SPIFFS integration doesn't support directories
|
|
if (strlen( outname ) == 0) {
|
|
ok = VFS_RES_OK;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normpath, &outname, TRUE )) {
|
|
if (strchr( outname, ':' )) {
|
|
// need to set FatFS' default drive
|
|
fs_fns->chdrive( outname );
|
|
// and force chdir to root in case path points only to root
|
|
fs_fns->chdir( "/" );
|
|
}
|
|
if (fs_fns->chdir( outname ) == VFS_RES_OK) {
|
|
ok = VFS_RES_OK;
|
|
}
|
|
free( outname );
|
|
}
|
|
#endif
|
|
|
|
return ok == VFS_RES_OK ? VFS_RES_OK : VFS_RES_ERR;
|
|
}
|
|
|
|
int32_t vfs_errno( const char *name )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
char *outname;
|
|
|
|
if (!name) name = ""; // current drive
|
|
|
|
const char *normname = normalize_path( name );
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
return fs_fns->ferrno( );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
int32_t r = fs_fns->ferrno( );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return VFS_RES_ERR;
|
|
}
|
|
|
|
int32_t vfs_ferrno( int fd )
|
|
{
|
|
vfs_file *f = (vfs_file *)fd;
|
|
|
|
if (f) {
|
|
return f->fns->ferrno ? f->fns->ferrno( f ) : 0;
|
|
} else {
|
|
vfs_fs_fns *fs_fns;
|
|
const char *name = ""; // current drive
|
|
char *outname;
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( name, &outname, FALSE )) {
|
|
return fs_fns->ferrno( );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( name, &outname, FALSE )) {
|
|
int32_t r = fs_fns->ferrno( );
|
|
free( outname );
|
|
return r;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void vfs_clearerr( const char *name )
|
|
{
|
|
vfs_fs_fns *fs_fns;
|
|
char *outname;
|
|
|
|
if (!name) name = ""; // current drive
|
|
|
|
const char *normname = normalize_path( name );
|
|
|
|
#ifdef BUILD_SPIFFS
|
|
if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) {
|
|
fs_fns->clearerr ( );
|
|
}
|
|
#endif
|
|
|
|
#ifdef BUILD_FATFS
|
|
if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) {
|
|
fs_fns->clearerr ( );
|
|
free( outname );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const char *vfs_basename( const char *path )
|
|
{
|
|
const char *basename;
|
|
|
|
// deduce basename (incl. extension) for length check
|
|
if (basename = strrchr( path, '/' )) {
|
|
basename++;
|
|
} else if (basename = strrchr( path, ':' )) {
|
|
basename++;
|
|
} else {
|
|
basename = path;
|
|
}
|
|
|
|
return basename;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// supplementary functions
|
|
//
|
|
int vfs_getc( int fd )
|
|
{
|
|
unsigned char c = 0xFF;
|
|
int32_t res;
|
|
|
|
if(!vfs_eof( fd )) {
|
|
if (1 != vfs_read( fd, &c, 1 )) {
|
|
NODE_DBG("getc errno %i\n", vfs_ferrno( fd ));
|
|
return VFS_EOF;
|
|
} else {
|
|
return (int)c;
|
|
}
|
|
}
|
|
return VFS_EOF;
|
|
}
|
|
|
|
int vfs_ungetc( int c, int fd )
|
|
{
|
|
return vfs_lseek( fd, -1, VFS_SEEK_CUR );
|
|
}
|