Commit af39a0bc authored by Philip Gladstone's avatar Philip Gladstone Committed by Marcel Stör
Browse files

Upgrade to latest SPIFFS and add building of file systems (#1226)

Fixes #1164 and thus also #1150, #1149, #1147 and #898.

* Move to latest version of SPIFFS
* Add SPIFFS porting layer for NodeMCU
* Add option to delete output if it doesn't fit
* Change FLASHSIZE to be in bits by default: default 4mb 32mb
* Add SPIFFS_MAX_FILESYSTEM_SIZE override
* Add notes on SPIFFS_FIXED_LOCATION
* Add 1M boundary
* Include the current version of the LICENSE
parent e3f7e632
...@@ -131,7 +131,15 @@ ...@@ -131,7 +131,15 @@
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) #define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) #define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
#define SPIFFS_MAGIC(fs) ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) #if SPIFFS_USE_MAGIC
#if !SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
#else // SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \
((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix))))
#endif // SPIFFS_USE_MAGIC_LENGTH
#endif // SPIFFS_USE_MAGIC
#define SPIFFS_CONFIG_MAGIC (0x20090315) #define SPIFFS_CONFIG_MAGIC (0x20090315)
...@@ -264,26 +272,26 @@ ...@@ -264,26 +272,26 @@
#define SPIFFS_API_CHECK_MOUNT(fs) \ #define SPIFFS_API_CHECK_MOUNT(fs) \
if (!SPIFFS_CHECK_MOUNT((fs))) { \ if (!SPIFFS_CHECK_MOUNT((fs))) { \
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
return -1; \ return SPIFFS_ERR_NOT_MOUNTED; \
} }
#define SPIFFS_API_CHECK_CFG(fs) \ #define SPIFFS_API_CHECK_CFG(fs) \
if (!SPIFFS_CHECK_CFG((fs))) { \ if (!SPIFFS_CHECK_CFG((fs))) { \
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
return -1; \ return SPIFFS_ERR_NOT_CONFIGURED; \
} }
#define SPIFFS_API_CHECK_RES(fs, res) \ #define SPIFFS_API_CHECK_RES(fs, res) \
if ((res) < SPIFFS_OK) { \ if ((res) < SPIFFS_OK) { \
(fs)->err_code = (res); \ (fs)->err_code = (res); \
return -1; \ return (res); \
} }
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
if ((res) < SPIFFS_OK) { \ if ((res) < SPIFFS_OK) { \
(fs)->err_code = (res); \ (fs)->err_code = (res); \
SPIFFS_UNLOCK(fs); \ SPIFFS_UNLOCK(fs); \
return -1; \ return (res); \
} }
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ #define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
...@@ -311,6 +319,26 @@ ...@@ -311,6 +319,26 @@
// stop searching at end of all look up pages // stop searching at end of all look up pages
#define SPIFFS_VIS_NO_WRAP (1<<2) #define SPIFFS_VIS_NO_WRAP (1<<2)
#if SPIFFS_HAL_CALLBACK_EXTRA
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
(_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src))
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
(_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst))
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
(_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len))
#else // SPIFFS_HAL_CALLBACK_EXTRA
#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \
(_fs)->cfg.hal_write_f((_paddr), (_len), (_src))
#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \
(_fs)->cfg.hal_read_f((_paddr), (_len), (_dst))
#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \
(_fs)->cfg.hal_erase_f((_paddr), (_len))
#endif // SPIFFS_HAL_CALLBACK_EXTRA
#if SPIFFS_CACHE #if SPIFFS_CACHE
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) #define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
...@@ -416,9 +444,14 @@ typedef struct __attribute(( packed )) { ...@@ -416,9 +444,14 @@ typedef struct __attribute(( packed )) {
// object index header page header // object index header page header
typedef struct __attribute(( packed )) typedef struct __attribute(( packed ))
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
#endif
{ {
// common page header // common page header
spiffs_page_header p_hdr; spiffs_page_header p_hdr;
// alignment
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
// size of object // size of object
u32_t size; u32_t size;
// type of object // type of object
...@@ -430,11 +463,12 @@ typedef struct __attribute(( packed )) ...@@ -430,11 +463,12 @@ typedef struct __attribute(( packed ))
// object index page header // object index page header
typedef struct __attribute(( packed )) { typedef struct __attribute(( packed )) {
spiffs_page_header p_hdr; spiffs_page_header p_hdr;
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
} spiffs_page_object_ix; } spiffs_page_object_ix;
// callback func for object lookup visitor // callback func for object lookup visitor
typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
u32_t user_data, void *user_p); const void *user_const_p, void *user_var_p);
#if SPIFFS_CACHE #if SPIFFS_CACHE
...@@ -495,8 +529,8 @@ s32_t spiffs_obj_lu_find_entry_visitor( ...@@ -495,8 +529,8 @@ s32_t spiffs_obj_lu_find_entry_visitor(
u8_t flags, u8_t flags,
spiffs_obj_id obj_id, spiffs_obj_id obj_id,
spiffs_visitor_f v, spiffs_visitor_f v,
u32_t user_data, const void *user_const_p,
void *user_p, void *user_var_p,
spiffs_block_ix *block_ix, spiffs_block_ix *block_ix,
int *lu_entry); int *lu_entry);
...@@ -504,6 +538,11 @@ s32_t spiffs_erase_block( ...@@ -504,6 +538,11 @@ s32_t spiffs_erase_block(
spiffs *fs, spiffs *fs,
spiffs_block_ix bix); spiffs_block_ix bix);
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
s32_t spiffs_probe(
spiffs_config *cfg);
#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH
// --------------- // ---------------
s32_t spiffs_obj_lu_scan( s32_t spiffs_obj_lu_scan(
...@@ -512,7 +551,7 @@ s32_t spiffs_obj_lu_scan( ...@@ -512,7 +551,7 @@ s32_t spiffs_obj_lu_scan(
s32_t spiffs_obj_lu_find_free_obj_id( s32_t spiffs_obj_lu_find_free_obj_id(
spiffs *fs, spiffs *fs,
spiffs_obj_id *obj_id, spiffs_obj_id *obj_id,
u8_t *conflicting_name); const u8_t *conflicting_name);
s32_t spiffs_obj_lu_find_free( s32_t spiffs_obj_lu_find_free(
spiffs *fs, spiffs *fs,
...@@ -573,7 +612,7 @@ s32_t spiffs_page_delete( ...@@ -573,7 +612,7 @@ s32_t spiffs_page_delete(
s32_t spiffs_object_create( s32_t spiffs_object_create(
spiffs *fs, spiffs *fs,
spiffs_obj_id obj_id, spiffs_obj_id obj_id,
u8_t name[SPIFFS_OBJ_NAME_LEN], const u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_obj_type type, spiffs_obj_type type,
spiffs_page_ix *objix_hdr_pix); spiffs_page_ix *objix_hdr_pix);
...@@ -583,7 +622,7 @@ s32_t spiffs_object_update_index_hdr( ...@@ -583,7 +622,7 @@ s32_t spiffs_object_update_index_hdr(
spiffs_obj_id obj_id, spiffs_obj_id obj_id,
spiffs_page_ix objix_hdr_pix, spiffs_page_ix objix_hdr_pix,
u8_t *new_objix_hdr_data, u8_t *new_objix_hdr_data,
u8_t name[SPIFFS_OBJ_NAME_LEN], const u8_t name[SPIFFS_OBJ_NAME_LEN],
u32_t size, u32_t size,
spiffs_page_ix *new_pix); spiffs_page_ix *new_pix);
...@@ -635,7 +674,7 @@ s32_t spiffs_object_truncate( ...@@ -635,7 +674,7 @@ s32_t spiffs_object_truncate(
s32_t spiffs_object_find_object_index_header_by_name( s32_t spiffs_object_find_object_index_header_by_name(
spiffs *fs, spiffs *fs,
u8_t name[SPIFFS_OBJ_NAME_LEN], const u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_page_ix *pix); spiffs_page_ix *pix);
// --------------- // ---------------
......
#include "testrunner.h"
#include <stdlib.h>
int main(int argc, char **args) {
run_tests(argc, args);
exit(EXIT_SUCCESS);
}
/*
* params_test.h
*
* Created on: May 26, 2013
* Author: petera
*/
#ifndef PARAMS_TEST_H_
#define PARAMS_TEST_H_
// total emulated spi flash size
#define PHYS_FLASH_SIZE (16*1024*1024)
// spiffs file system size
#define SPIFFS_FLASH_SIZE (2*1024*1024)
// spiffs file system offset in emulated spi flash
#define SPIFFS_PHYS_ADDR (4*1024*1024)
#define SECTOR_SIZE 65536
#define LOG_BLOCK (SECTOR_SIZE*2)
#define LOG_PAGE (SECTOR_SIZE/256)
#define FD_BUF_SIZE 64*6
#define CACHE_BUF_SIZE (LOG_PAGE + 32)*8
#define ASSERT(c, m) real_assert((c),(m), __FILE__, __LINE__);
typedef signed int s32_t;
typedef unsigned int u32_t;
typedef signed short s16_t;
typedef unsigned short u16_t;
typedef signed char s8_t;
typedef unsigned char u8_t;
void real_assert(int c, const char *n, const char *file, int l);
#endif /* PARAMS_TEST_H_ */
/*
* test_bugreports.c
*
* Created on: Mar 8, 2015
* Author: petera
*/
#include "testrunner.h"
#include "test_spiffs.h"
#include "spiffs_nucleus.h"
#include "spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
SUITE(bug_tests)
void setup() {
_setup_test_only();
}
void teardown() {
_teardown();
}
TEST(nodemcu_full_fs_1) {
fs_reset_specific(0, 4096*20, 4096, 4096, 256);
int res;
spiffs_file fd;
printf(" fill up system by writing one byte a lot\n");
fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
int i;
spiffs_stat s;
res = SPIFFS_OK;
for (i = 0; i < 100*1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
int errno = SPIFFS_errno(FS);
int res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(errno == SPIFFS_ERR_FULL);
SPIFFS_close(FS, fd);
printf(" remove big file\n");
res = SPIFFS_remove(FS, "test1.txt");
printf("res:%i errno:%i\n",res, SPIFFS_errno(FS));
TEST_CHECK(res == SPIFFS_OK);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == -1);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res2 = SPIFFS_stat(FS, "test1.txt", &s);
TEST_CHECK(res2 == -1);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
printf(" create small file\n");
fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
res = SPIFFS_OK;
for (i = 0; res >= 0 && i < 1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
TEST_CHECK(res >= SPIFFS_OK);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(s.size == 1000);
SPIFFS_close(FS, fd);
return TEST_RES_OK;
} TEST_END(nodemcu_full_fs_1)
TEST(nodemcu_full_fs_2) {
fs_reset_specific(0, 4096*22, 4096, 4096, 256);
int res;
spiffs_file fd;
printf(" fill up system by writing one byte a lot\n");
fd = SPIFFS_open(FS, "test1.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
int i;
spiffs_stat s;
res = SPIFFS_OK;
for (i = 0; i < 100*1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
int errno = SPIFFS_errno(FS);
int res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(errno == SPIFFS_ERR_FULL);
SPIFFS_close(FS, fd);
res2 = SPIFFS_stat(FS, "test1.txt", &s);
TEST_CHECK(res2 == SPIFFS_OK);
SPIFFS_clearerr(FS);
printf(" create small file\n");
fd = SPIFFS_open(FS, "test2.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
TEST_CHECK(fd > 0);
for (i = 0; i < 1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FULL);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(s.size == 0);
SPIFFS_clearerr(FS);
printf(" remove files\n");
res = SPIFFS_remove(FS, "test1.txt");
TEST_CHECK(res == SPIFFS_OK);
res = SPIFFS_remove(FS, "test2.txt");
TEST_CHECK(res == SPIFFS_OK);
printf(" create medium file\n");
fd = SPIFFS_open(FS, "test3.txt", SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
TEST_CHECK(fd > 0);
for (i = 0; i < 20*1000; i++) {
u8_t buf = 'x';
res = SPIFFS_write(FS, fd, &buf, 1);
}
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_OK);
res2 = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res2 == SPIFFS_OK);
printf(" >>> file %s size: %i\n", s.name, s.size);
TEST_CHECK(s.size == 20*1000);
return TEST_RES_OK;
} TEST_END(nodemcu_full_fs_2)
SUITE_END(bug_tests)
/*
* test_dev.c
*
* Created on: Jul 14, 2013
* Author: petera
*/
#include "testrunner.h"
#include "test_spiffs.h"
#include "spiffs_nucleus.h"
#include "spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
SUITE(check_tests)
void setup() {
_setup();
}
void teardown() {
_teardown();
}
TEST(evil_write) {
fs_set_validate_flashing(0);
printf("writing corruption to block 1 data range (leaving lu intact)\n");
u32_t data_range = SPIFFS_CFG_LOG_BLOCK_SZ(FS) -
SPIFFS_CFG_LOG_PAGE_SZ(FS) * (SPIFFS_OBJ_LOOKUP_PAGES(FS));
u8_t *corruption = malloc(data_range);
memrand(corruption, data_range);
u32_t addr = 0 * SPIFFS_CFG_LOG_PAGE_SZ(FS) * SPIFFS_OBJ_LOOKUP_PAGES(FS);
area_write(addr, corruption, data_range);
free(corruption);
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
printf("CHECK1-----------------\n");
SPIFFS_check(FS);
printf("CHECK2-----------------\n");
SPIFFS_check(FS);
printf("CHECK3-----------------\n");
SPIFFS_check(FS);
res = test_create_and_write_file("file2", size, size);
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(evil_write)
TEST(lu_check1) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index 1
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
TEST_CHECK(res >= 0);
// reset lu entry to being erased, but keep page data
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry*sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
return TEST_RES_OK;
} TEST_END(lu_check1)
TEST(page_cons1) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// set object index entry 2 to a bad page
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 0 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = 0x55;
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
area_write(addr+2, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons1)
TEST(page_cons2) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// find data page span index 0
spiffs_page_ix dpix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &dpix);
TEST_CHECK(res >= 0);
// set object index entry 1+2 to a data page 0
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = dpix;
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons2)
TEST(page_cons3) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// set object index entry 1+2 lookup page
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 1 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = SPIFFS_PAGES_PER_BLOCK(FS) * (*FS.block_count - 2);
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
area_write(addr+sizeof(spiffs_page_ix), (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons3)
TEST(page_cons_final) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify page header, make unfinalized
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 1, 0, &pix);
TEST_CHECK(res >= 0);
// set page span ix 1 as unfinalized
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
u8_t flags;
area_read(addr, (u8_t*)&flags, 1);
flags |= SPIFFS_PH_FLAG_FINAL;
area_write(addr, (u8_t*)&flags, 1);
// delete all cache
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(page_cons_final)
TEST(index_cons1) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" deleting lu entry pix %04x\n", pix);
// reset lu entry to being erased, but keep page data
spiffs_obj_id obj_id = SPIFFS_OBJ_ID_DELETED;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(index_cons1)
TEST(index_cons2) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" writing lu entry for index page, ix %04x, as data page\n", pix);
spiffs_obj_id obj_id = 0x1234;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(index_cons2)
TEST(index_cons3) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" setting lu entry pix %04x to another index page\n", pix);
// reset lu entry to being erased, but keep page data
spiffs_obj_id obj_id = 1234 | SPIFFS_OBJ_ID_IX_FLAG;
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(FS, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(FS, pix);
u32_t addr = SPIFFS_BLOCK_TO_PADDR(FS, bix) + entry * sizeof(spiffs_obj_id);
area_write(addr, (u8_t*)&obj_id, sizeof(spiffs_obj_id));
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
} TEST_END(index_cons3)
TEST(index_cons4) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*SPIFFS_PAGES_PER_BLOCK(FS);
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify lu entry data page index header, flags
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
printf(" cue objix hdr deletion in page %04x\n", pix);
// set flags as deleting ix header
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + offsetof(spiffs_page_header, flags);
u8_t flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE);
area_write(addr, (u8_t*)&flags, 1);
#if SPIFFS_CACHE
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
SPIFFS_check(FS);
return TEST_RES_OK;
} TEST_END(index_cons4)
SUITE_END(check_tests)
/*
* test_dev.c
*
* Created on: Jul 14, 2013
* Author: petera
*/
#include "testrunner.h"
#include "test_spiffs.h"
#include "spiffs_nucleus.h"
#include "spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
SUITE(dev_tests)
void setup() {
_setup();
}
void teardown() {
_teardown();
}
TEST(interrupted_write) {
char *name = "interrupt";
char *name2 = "interrupt2";
int res;
spiffs_file fd;
const u32_t sz = SPIFFS_CFG_LOG_PAGE_SZ(FS)*8;
u8_t *buf = malloc(sz);
memrand(buf, sz);
printf(" create reference file\n");
fd = SPIFFS_open(FS, name, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
clear_flash_ops_log();
res = SPIFFS_write(FS, fd, buf, sz);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
u32_t written = get_flash_ops_log_write_bytes();
printf(" written bytes: %i\n", written);
printf(" create error file\n");
fd = SPIFFS_open(FS, name2, SPIFFS_RDWR | SPIFFS_CREAT | SPIFFS_TRUNC, 0);
TEST_CHECK(fd > 0);
clear_flash_ops_log();
invoke_error_after_write_bytes(written/2, 0);
res = SPIFFS_write(FS, fd, buf, sz);
SPIFFS_close(FS, fd);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_TEST);
clear_flash_ops_log();
#if SPIFFS_CACHE
// delete all cache
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
printf(" read error file\n");
fd = SPIFFS_open(FS, name2, SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
printf(" file size: %i\n", s.size);
if (s.size > 0) {
u8_t *buf2 = malloc(s.size);
res = SPIFFS_read(FS, fd, buf2, s.size);
TEST_CHECK(res >= 0);
u32_t ix = 0;
for (ix = 0; ix < s.size; ix += 16) {
int i;
printf(" ");
for (i = 0; i < 16; i++) {
printf("%02x", buf[ix+i]);
}
printf(" ");
for (i = 0; i < 16; i++) {
printf("%02x", buf2[ix+i]);
}
printf("\n");
}
free(buf2);
}
SPIFFS_close(FS, fd);
printf(" FS check\n");
SPIFFS_check(FS);
printf(" read error file again\n");
fd = SPIFFS_open(FS, name2, SPIFFS_APPEND | SPIFFS_RDWR, 0);
TEST_CHECK(fd > 0);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
printf(" file size: %i\n", s.size);
printf(" write file\n");
res = SPIFFS_write(FS, fd, buf, sz);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
free(buf);
return TEST_RES_OK;
} TEST_END(interrupted_write)
SUITE_END(dev_tests)
/*
* test_suites.c
*
* Created on: Jun 19, 2013
* Author: petera
*/
#include "testrunner.h"
#include "test_spiffs.h"
#include "spiffs_nucleus.h"
#include "spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
SUITE(hydrogen_tests)
void setup() {
_setup();
}
void teardown() {
_teardown();
}
TEST(info)
{
u32_t used, total;
int res = SPIFFS_info(FS, &total, &used);
TEST_CHECK(res == SPIFFS_OK);
TEST_CHECK(used == 0);
TEST_CHECK(total < __fs.cfg.phys_size);
return TEST_RES_OK;
}
TEST_END(info)
TEST(missing_file)
{
spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0);
TEST_CHECK(fd < 0);
return TEST_RES_OK;
}
TEST_END(missing_file)
TEST(bad_fd)
{
int res;
spiffs_stat s;
spiffs_file fd = SPIFFS_open(FS, "this_wont_exist", SPIFFS_RDONLY, 0);
TEST_CHECK(fd < 0);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
res = SPIFFS_read(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
res = SPIFFS_write(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_BAD_DESCRIPTOR);
return TEST_RES_OK;
}
TEST_END(bad_fd)
TEST(closed_fd)
{
int res;
spiffs_stat s;
res = test_create_file("file");
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd >= 0);
SPIFFS_close(FS, fd);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_read(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_write(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
return TEST_RES_OK;
}
TEST_END(closed_fd)
TEST(deleted_same_fd)
{
int res;
spiffs_stat s;
spiffs_file fd;
res = test_create_file("remove");
fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res >= 0);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_read(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_write(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
return TEST_RES_OK;
}
TEST_END(deleted_same_fd)
TEST(deleted_other_fd)
{
int res;
spiffs_stat s;
spiffs_file fd, fd_orig;
res = test_create_file("remove");
fd_orig = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
TEST_CHECK(fd_orig >= 0);
fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_fremove(FS, fd_orig);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd_orig);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_CUR);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_read(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
res = SPIFFS_write(FS, fd, 0, 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_FILE_CLOSED);
return TEST_RES_OK;
}
TEST_END(deleted_other_fd)
TEST(file_by_open)
{
int res;
spiffs_stat s;
spiffs_file fd = SPIFFS_open(FS, "filebopen", SPIFFS_CREAT, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0);
TEST_CHECK(s.size == 0);
SPIFFS_close(FS, fd);
fd = SPIFFS_open(FS, "filebopen", SPIFFS_RDONLY, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
TEST_CHECK(strcmp((char*)s.name, "filebopen") == 0);
TEST_CHECK(s.size == 0);
SPIFFS_close(FS, fd);
return TEST_RES_OK;
}
TEST_END(file_by_open)
TEST(file_by_creat)
{
int res;
res = test_create_file("filebcreat");
TEST_CHECK(res >= 0);
res = SPIFFS_creat(FS, "filebcreat", 0);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS)==SPIFFS_ERR_CONFLICTING_NAME);
return TEST_RES_OK;
}
TEST_END(file_by_creat)
TEST(list_dir)
{
int res;
char *files[4] = {
"file1",
"file2",
"file3",
"file4"
};
int file_cnt = sizeof(files)/sizeof(char *);
int i;
for (i = 0; i < file_cnt; i++) {
res = test_create_file(files[i]);
TEST_CHECK(res >= 0);
}
spiffs_DIR d;
struct spiffs_dirent e;
struct spiffs_dirent *pe = &e;
SPIFFS_opendir(FS, "/", &d);
int found = 0;
while ((pe = SPIFFS_readdir(&d, pe))) {
printf(" %s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size);
for (i = 0; i < file_cnt; i++) {
if (strcmp(files[i], pe->name) == 0) {
found++;
break;
}
}
}
SPIFFS_closedir(&d);
TEST_CHECK(found == file_cnt);
return TEST_RES_OK;
}
TEST_END(list_dir)
TEST(open_by_dirent) {
int res;
char *files[4] = {
"file1",
"file2",
"file3",
"file4"
};
int file_cnt = sizeof(files)/sizeof(char *);
int i;
int size = SPIFFS_DATA_PAGE_SIZE(FS);
for (i = 0; i < file_cnt; i++) {
res = test_create_and_write_file(files[i], size, size);
TEST_CHECK(res >= 0);
}
spiffs_DIR d;
struct spiffs_dirent e;
struct spiffs_dirent *pe = &e;
int found = 0;
SPIFFS_opendir(FS, "/", &d);
while ((pe = SPIFFS_readdir(&d, pe))) {
spiffs_file fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
res = read_and_verify_fd(fd, pe->name);
TEST_CHECK(res == SPIFFS_OK);
fd = SPIFFS_open_by_dirent(FS, pe, SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res == SPIFFS_OK);
SPIFFS_close(FS, fd);
found++;
}
SPIFFS_closedir(&d);
TEST_CHECK(found == file_cnt);
found = 0;
SPIFFS_opendir(FS, "/", &d);
while ((pe = SPIFFS_readdir(&d, pe))) {
found++;
}
SPIFFS_closedir(&d);
TEST_CHECK(found == 0);
return TEST_RES_OK;
} TEST_END(open_by_dirent)
TEST(rename) {
int res;
char *src_name = "baah";
char *dst_name = "booh";
char *dst_name2 = "beeh";
int size = SPIFFS_DATA_PAGE_SIZE(FS);
res = test_create_and_write_file(src_name, size, size);
TEST_CHECK(res >= 0);
res = SPIFFS_rename(FS, src_name, dst_name);
TEST_CHECK(res >= 0);
res = SPIFFS_rename(FS, dst_name, dst_name);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_CONFLICTING_NAME);
res = SPIFFS_rename(FS, src_name, dst_name2);
TEST_CHECK(res < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
return TEST_RES_OK;
} TEST_END(rename)
TEST(remove_single_by_path)
{
int res;
spiffs_file fd;
res = test_create_file("remove");
TEST_CHECK(res >= 0);
res = SPIFFS_remove(FS, "remove");
TEST_CHECK(res >= 0);
fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0);
TEST_CHECK(fd < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
return TEST_RES_OK;
}
TEST_END(remove_single_by_path)
TEST(remove_single_by_fd)
{
int res;
spiffs_file fd;
res = test_create_file("remove");
TEST_CHECK(res >= 0);
fd = SPIFFS_open(FS, "remove", SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
fd = SPIFFS_open(FS, "remove", SPIFFS_RDONLY, 0);
TEST_CHECK(fd < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
return TEST_RES_OK;
}
TEST_END(remove_single_by_fd)
TEST(write_big_file_chunks_page)
{
int size = ((50*(FS)->cfg.phys_size)/100);
printf(" filesize %i\n", size);
int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS));
TEST_CHECK(res >= 0);
res = read_and_verify("bigfile");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(write_big_file_chunks_page)
TEST(write_big_files_chunks_page)
{
char name[32];
int f;
int files = 10;
int res;
int size = ((50*(FS)->cfg.phys_size)/100)/files;
printf(" filesize %i\n", size);
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS));
TEST_CHECK(res >= 0);
}
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = read_and_verify(name);
TEST_CHECK(res >= 0);
}
return TEST_RES_OK;
}
TEST_END(write_big_files_chunks_page)
TEST(write_big_file_chunks_index)
{
int size = ((50*(FS)->cfg.phys_size)/100);
printf(" filesize %i\n", size);
int res = test_create_and_write_file("bigfile", size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS));
TEST_CHECK(res >= 0);
res = read_and_verify("bigfile");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(write_big_file_chunks_index)
TEST(write_big_files_chunks_index)
{
char name[32];
int f;
int files = 10;
int res;
int size = ((50*(FS)->cfg.phys_size)/100)/files;
printf(" filesize %i\n", size);
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = test_create_and_write_file(name, size, SPIFFS_DATA_PAGE_SIZE(FS) * SPIFFS_OBJ_HDR_IX_LEN(FS));
TEST_CHECK(res >= 0);
}
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = read_and_verify(name);
TEST_CHECK(res >= 0);
}
return TEST_RES_OK;
}
TEST_END(write_big_files_chunks_index)
TEST(write_big_file_chunks_huge)
{
int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS);
printf(" filesize %i\n", size);
int res = test_create_and_write_file("bigfile", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("bigfile");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(write_big_file_chunks_huge)
TEST(write_big_files_chunks_huge)
{
char name[32];
int f;
int files = 10;
int res;
int size = ((50*(FS)->cfg.phys_size)/100)/files;
printf(" filesize %i\n", size);
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = test_create_and_write_file(name, size, size);
TEST_CHECK(res >= 0);
}
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = read_and_verify(name);
TEST_CHECK(res >= 0);
}
return TEST_RES_OK;
}
TEST_END(write_big_files_chunks_huge)
TEST(truncate_big_file)
{
int size = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS);
printf(" filesize %i\n", size);
int res = test_create_and_write_file("bigfile", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("bigfile");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0);
TEST_CHECK(fd > 0);
res = SPIFFS_fremove(FS, fd);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
fd = SPIFFS_open(FS, "bigfile", SPIFFS_RDWR, 0);
TEST_CHECK(fd < 0);
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_NOT_FOUND);
return TEST_RES_OK;
}
TEST_END(truncate_big_file)
TEST(simultaneous_write) {
int res = SPIFFS_creat(FS, "simul1", 0);
TEST_CHECK(res >= 0);
spiffs_file fd1 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0);
TEST_CHECK(fd1 > 0);
spiffs_file fd2 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0);
TEST_CHECK(fd2 > 0);
spiffs_file fd3 = SPIFFS_open(FS, "simul1", SPIFFS_RDWR, 0);
TEST_CHECK(fd3 > 0);
u8_t data1 = 1;
u8_t data2 = 2;
u8_t data3 = 3;
res = SPIFFS_write(FS, fd1, &data1, 1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd1);
res = SPIFFS_write(FS, fd2, &data2, 1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd2);
res = SPIFFS_write(FS, fd3, &data3, 1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd3);
spiffs_stat s;
res = SPIFFS_stat(FS, "simul1", &s);
TEST_CHECK(res >= 0);
TEST_CHECK(s.size == 1);
u8_t rdata;
spiffs_file fd = SPIFFS_open(FS, "simul1", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
res = SPIFFS_read(FS, fd, &rdata, 1);
TEST_CHECK(res >= 0);
TEST_CHECK(rdata == data3);
return TEST_RES_OK;
}
TEST_END(simultaneous_write)
TEST(simultaneous_write_append) {
int res = SPIFFS_creat(FS, "simul2", 0);
TEST_CHECK(res >= 0);
spiffs_file fd1 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0);
TEST_CHECK(fd1 > 0);
spiffs_file fd2 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0);
TEST_CHECK(fd2 > 0);
spiffs_file fd3 = SPIFFS_open(FS, "simul2", SPIFFS_RDWR | SPIFFS_APPEND, 0);
TEST_CHECK(fd3 > 0);
u8_t data1 = 1;
u8_t data2 = 2;
u8_t data3 = 3;
res = SPIFFS_write(FS, fd1, &data1, 1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd1);
res = SPIFFS_write(FS, fd2, &data2, 1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd2);
res = SPIFFS_write(FS, fd3, &data3, 1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd3);
spiffs_stat s;
res = SPIFFS_stat(FS, "simul2", &s);
TEST_CHECK(res >= 0);
TEST_CHECK(s.size == 3);
u8_t rdata[3];
spiffs_file fd = SPIFFS_open(FS, "simul2", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
res = SPIFFS_read(FS, fd, &rdata, 3);
TEST_CHECK(res >= 0);
TEST_CHECK(rdata[0] == data1);
TEST_CHECK(rdata[1] == data2);
TEST_CHECK(rdata[2] == data3);
return TEST_RES_OK;
}
TEST_END(simultaneous_write_append)
TEST(file_uniqueness)
{
int res;
spiffs_file fd;
char fname[32];
int files = ((SPIFFS_CFG_PHYS_SZ(FS) * 75) / 100) / 2 / SPIFFS_CFG_LOG_PAGE_SZ(FS);
//(FS_PURE_DATA_PAGES(FS) / 2) - SPIFFS_PAGES_PER_BLOCK(FS)*8;
int i;
printf(" creating %i files\n", files);
for (i = 0; i < files; i++) {
char content[20];
sprintf(fname, "file%i", i);
sprintf(content, "%i", i);
res = test_create_file(fname);
TEST_CHECK(res >= 0);
fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_write(FS, fd, content, strlen(content)+1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
}
printf(" checking %i files\n", files);
for (i = 0; i < files; i++) {
char content[20];
char ref_content[20];
sprintf(fname, "file%i", i);
sprintf(content, "%i", i);
fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1);
TEST_CHECK(res >= 0);
TEST_CHECK(strcmp(ref_content, content) == 0);
SPIFFS_close(FS, fd);
}
printf(" removing %i files\n", files/2);
for (i = 0; i < files; i += 2) {
sprintf(fname, "file%i", i);
res = SPIFFS_remove(FS, fname);
TEST_CHECK(res >= 0);
}
printf(" creating %i files\n", files/2);
for (i = 0; i < files; i += 2) {
char content[20];
sprintf(fname, "file%i", i);
sprintf(content, "new%i", i);
res = test_create_file(fname);
TEST_CHECK(res >= 0);
fd = SPIFFS_open(FS, fname, SPIFFS_APPEND | SPIFFS_RDWR, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_write(FS, fd, content, strlen(content)+1);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
}
printf(" checking %i files\n", files);
for (i = 0; i < files; i++) {
char content[20];
char ref_content[20];
sprintf(fname, "file%i", i);
if ((i & 1) == 0) {
sprintf(content, "new%i", i);
} else {
sprintf(content, "%i", i);
}
fd = SPIFFS_open(FS, fname, SPIFFS_RDONLY, 0);
TEST_CHECK(fd >= 0);
res = SPIFFS_read(FS, fd, ref_content, strlen(content)+1);
TEST_CHECK(res >= 0);
TEST_CHECK(strcmp(ref_content, content) == 0);
SPIFFS_close(FS, fd);
}
return TEST_RES_OK;
}
TEST_END(file_uniqueness)
int create_and_read_back(int size, int chunk) {
char *name = "file";
spiffs_file fd;
s32_t res;
u8_t *buf = malloc(size);
memrand(buf, size);
res = test_create_file(name);
CHECK(res >= 0);
fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0);
CHECK(fd >= 0);
res = SPIFFS_write(FS, fd, buf, size);
CHECK(res >= 0);
spiffs_stat stat;
res = SPIFFS_fstat(FS, fd, &stat);
CHECK(res >= 0);
CHECK(stat.size == size);
SPIFFS_close(FS, fd);
fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
CHECK(fd >= 0);
u8_t *rbuf = malloc(size);
int offs = 0;
while (offs < size) {
int len = MIN(size - offs, chunk);
res = SPIFFS_read(FS, fd, &rbuf[offs], len);
CHECK(res >= 0);
CHECK(memcmp(&rbuf[offs], &buf[offs], len) == 0);
offs += chunk;
}
CHECK(memcmp(&rbuf[0], &buf[0], size) == 0);
SPIFFS_close(FS, fd);
free(rbuf);
free(buf);
return 0;
}
TEST(read_chunk_1)
{
TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*8, 1) == 0);
return TEST_RES_OK;
}
TEST_END(read_chunk_1)
TEST(read_chunk_page)
{
TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*2,
SPIFFS_DATA_PAGE_SIZE(FS)) == 0);
return TEST_RES_OK;
}
TEST_END(read_chunk_page)
TEST(read_chunk_index)
{
TEST_CHECK(create_and_read_back(SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))*4,
SPIFFS_DATA_PAGE_SIZE(FS)*(SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS))) == 0);
return TEST_RES_OK;
}
TEST_END(read_chunk_index)
TEST(read_chunk_huge)
{
int sz = (2*(FS)->cfg.phys_size)/3;
TEST_CHECK(create_and_read_back(sz, sz) == 0);
return TEST_RES_OK;
}
TEST_END(read_chunk_huge)
TEST(read_beyond)
{
char *name = "file";
spiffs_file fd;
s32_t res;
u32_t size = SPIFFS_DATA_PAGE_SIZE(FS)*2;
u8_t *buf = malloc(size);
memrand(buf, size);
res = test_create_file(name);
CHECK(res >= 0);
fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0);
CHECK(fd >= 0);
res = SPIFFS_write(FS, fd, buf, size);
CHECK(res >= 0);
spiffs_stat stat;
res = SPIFFS_fstat(FS, fd, &stat);
CHECK(res >= 0);
CHECK(stat.size == size);
SPIFFS_close(FS, fd);
fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
CHECK(fd >= 0);
u8_t *rbuf = malloc(size+10);
res = SPIFFS_read(FS, fd, rbuf, size+10);
SPIFFS_close(FS, fd);
free(rbuf);
free(buf);
TEST_CHECK(res == size);
return TEST_RES_OK;
}
TEST_END(read_beyond)
TEST(bad_index_1) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// set object index entry 2 to a bad page, free
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = (spiffs_page_ix)-1;
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
#if SPIFFS_CACHE
// delete all cache
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
res = read_and_verify("file");
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_FREE);
return TEST_RES_OK;
} TEST_END(bad_index_1)
TEST(bad_index_2) {
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
int res = test_create_and_write_file("file", size, size);
TEST_CHECK(res >= 0);
res = read_and_verify("file");
TEST_CHECK(res >= 0);
spiffs_file fd = SPIFFS_open(FS, "file", SPIFFS_RDONLY, 0);
TEST_CHECK(fd > 0);
spiffs_stat s;
res = SPIFFS_fstat(FS, fd, &s);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
// modify object index, find object index header
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(FS, s.obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
TEST_CHECK(res >= 0);
// set object index entry 2 to a bad page, lu
u32_t addr = SPIFFS_PAGE_TO_PADDR(FS, pix) + sizeof(spiffs_page_object_ix_header) + 2 * sizeof(spiffs_page_ix);
spiffs_page_ix bad_pix_ref = SPIFFS_OBJ_LOOKUP_PAGES(FS)-1;
area_write(addr, (u8_t*)&bad_pix_ref, sizeof(spiffs_page_ix));
#if SPIFFS_CACHE
// delete all cache
spiffs_cache *cache = spiffs_get_cache(FS);
cache->cpage_use_map = 0;
#endif
res = read_and_verify("file");
TEST_CHECK(SPIFFS_errno(FS) == SPIFFS_ERR_INDEX_REF_LU);
return TEST_RES_OK;
} TEST_END(bad_index_2)
TEST(lseek_simple_modification) {
int res;
spiffs_file fd;
char *fname = "seekfile";
int i;
int len = 4096;
fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
TEST_CHECK(fd > 0);
int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
u8_t *buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
TEST_CHECK(res >= 0);
write(pfd, buf, len);
free(buf);
res = read_and_verify(fname);
TEST_CHECK(res >= 0);
res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET);
TEST_CHECK(res >= 0);
lseek(pfd, len/2, SEEK_SET);
len = len/4;
buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
TEST_CHECK(res >= 0);
write(pfd, buf, len);
free(buf);
res = read_and_verify(fname);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
close(pfd);
return TEST_RES_OK;
}
TEST_END(lseek_simple_modification)
TEST(lseek_modification_append) {
int res;
spiffs_file fd;
char *fname = "seekfile";
int i;
int len = 4096;
fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
TEST_CHECK(fd > 0);
int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
u8_t *buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
TEST_CHECK(res >= 0);
write(pfd, buf, len);
free(buf);
res = read_and_verify(fname);
TEST_CHECK(res >= 0);
res = SPIFFS_lseek(FS, fd, len/2, SPIFFS_SEEK_SET);
TEST_CHECK(res >= 0);
lseek(pfd, len/2, SEEK_SET);
buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
TEST_CHECK(res >= 0);
write(pfd, buf, len);
free(buf);
res = read_and_verify(fname);
TEST_CHECK(res >= 0);
SPIFFS_close(FS, fd);
close(pfd);
return TEST_RES_OK;
}
TEST_END(lseek_modification_append)
TEST(lseek_modification_append_multi) {
int res;
spiffs_file fd;
char *fname = "seekfile";
int len = 1024;
int runs = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS) / (len/2);
fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
TEST_CHECK(fd > 0);
int pfd = open(make_test_fname(fname), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
u8_t *buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
TEST_CHECK(res >= 0);
write(pfd, buf, len);
free(buf);
res = read_and_verify(fname);
TEST_CHECK(res >= 0);
while (runs--) {
res = SPIFFS_lseek(FS, fd, -len/2, SPIFFS_SEEK_END);
TEST_CHECK(res >= 0);
lseek(pfd, -len/2, SEEK_END);
buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
TEST_CHECK(res >= 0);
write(pfd, buf, len);
free(buf);
res = read_and_verify(fname);
TEST_CHECK(res >= 0);
}
SPIFFS_close(FS, fd);
close(pfd);
return TEST_RES_OK;
}
TEST_END(lseek_modification_append_multi)
TEST(lseek_read) {
int res;
spiffs_file fd;
char *fname = "seekfile";
int len = (FS_PURE_DATA_PAGES(FS) / 2) * SPIFFS_DATA_PAGE_SIZE(FS);
int runs = 100000;
fd = SPIFFS_open(FS, fname, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
TEST_CHECK(fd > 0);
u8_t *refbuf = malloc(len);
memrand(refbuf, len);
res = SPIFFS_write(FS, fd, refbuf, len);
TEST_CHECK(res >= 0);
int offs = 0;
res = SPIFFS_lseek(FS, fd, 0, SPIFFS_SEEK_SET);
TEST_CHECK(res >= 0);
while (runs--) {
int i;
u8_t buf[64];
if (offs + 41 + sizeof(buf) >= len) {
offs = (offs + 41 + sizeof(buf)) % len;
res = SPIFFS_lseek(FS, fd, offs, SPIFFS_SEEK_SET);
TEST_CHECK(res >= 0);
}
res = SPIFFS_lseek(FS, fd, 41, SPIFFS_SEEK_CUR);
TEST_CHECK(res >= 0);
offs += 41;
res = SPIFFS_read(FS, fd, buf, sizeof(buf));
TEST_CHECK(res >= 0);
for (i = 0; i < sizeof(buf); i++) {
if (buf[i] != refbuf[offs+i]) {
printf(" mismatch at offs %i\n", offs);
}
TEST_CHECK(buf[i] == refbuf[offs+i]);
}
offs += sizeof(buf);
res = SPIFFS_lseek(FS, fd, -((u32_t)sizeof(buf)+11), SPIFFS_SEEK_CUR);
TEST_CHECK(res >= 0);
offs -= (sizeof(buf)+11);
res = SPIFFS_read(FS, fd, buf, sizeof(buf));
TEST_CHECK(res >= 0);
for (i = 0; i < sizeof(buf); i++) {
if (buf[i] != refbuf[offs+i]) {
printf(" mismatch at offs %i\n", offs);
}
TEST_CHECK(buf[i] == refbuf[offs+i]);
}
offs += sizeof(buf);
}
free(refbuf);
SPIFFS_close(FS, fd);
return TEST_RES_OK;
}
TEST_END(lseek_read)
TEST(write_small_file_chunks_1)
{
int res = test_create_and_write_file("smallfile", 256, 1);
TEST_CHECK(res >= 0);
res = read_and_verify("smallfile");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(write_small_file_chunks_1)
TEST(write_small_files_chunks_1)
{
char name[32];
int f;
int size = 512;
int files = ((20*(FS)->cfg.phys_size)/100)/size;
int res;
for (f = 0; f < files; f++) {
sprintf(name, "smallfile%i", f);
res = test_create_and_write_file(name, size, 1);
TEST_CHECK(res >= 0);
}
for (f = 0; f < files; f++) {
sprintf(name, "smallfile%i", f);
res = read_and_verify(name);
TEST_CHECK(res >= 0);
}
return TEST_RES_OK;
}
TEST_END(write_small_files_chunks_1)
TEST(write_big_file_chunks_1)
{
int size = ((50*(FS)->cfg.phys_size)/100);
printf(" filesize %i\n", size);
int res = test_create_and_write_file("bigfile", size, 1);
TEST_CHECK(res >= 0);
res = read_and_verify("bigfile");
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(write_big_file_chunks_1)
TEST(write_big_files_chunks_1)
{
char name[32];
int f;
int files = 10;
int res;
int size = ((50*(FS)->cfg.phys_size)/100)/files;
printf(" filesize %i\n", size);
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = test_create_and_write_file(name, size, 1);
TEST_CHECK(res >= 0);
}
for (f = 0; f < files; f++) {
sprintf(name, "bigfile%i", f);
res = read_and_verify(name);
TEST_CHECK(res >= 0);
}
return TEST_RES_OK;
}
TEST_END(write_big_files_chunks_1)
TEST(long_run_config_many_small_one_long)
{
tfile_conf cfgs[] = {
{ .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = LONG
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = LONG
},
};
int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 206, 5, 0);
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(long_run_config_many_small_one_long)
TEST(long_run_config_many_medium)
{
tfile_conf cfgs[] = {
{ .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = LARGE, .ttype = MODIFIED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = MEDIUM, .ttype = MODIFIED, .tlife = LONG
},
};
int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 305, 5, 0);
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(long_run_config_many_medium)
TEST(long_run_config_many_small)
{
tfile_conf cfgs[] = {
{ .tsize = SMALL, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = LONG
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = MODIFIED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = UNTAMPERED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = SHORT
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = MEDIUM
},
{ .tsize = EMPTY, .ttype = UNTAMPERED, .tlife = SHORT
},
};
int res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 115, 6, 0);
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(long_run_config_many_small)
TEST(long_run)
{
tfile_conf cfgs[] = {
{ .tsize = EMPTY, .ttype = APPENDED, .tlife = MEDIUM
},
{ .tsize = SMALL, .ttype = REWRITTEN, .tlife = SHORT
},
{ .tsize = MEDIUM, .ttype = MODIFIED, .tlife = SHORT
},
{ .tsize = MEDIUM, .ttype = APPENDED, .tlife = SHORT
},
};
int macro_runs = 500;
printf(" ");
u32_t clob_size = SPIFFS_CFG_PHYS_SZ(FS)/4;
int res = test_create_and_write_file("long_clobber", clob_size, clob_size);
TEST_CHECK(res >= 0);
res = read_and_verify("long_clobber");
TEST_CHECK(res >= 0);
while (macro_runs--) {
//printf(" ---- run %i ----\n", macro_runs);
if ((macro_runs % 20) == 0) {
printf(".");
fflush(stdout);
}
res = run_file_config(sizeof(cfgs)/sizeof(cfgs[0]), &cfgs[0], 11, 2, 0);
TEST_CHECK(res >= 0);
}
printf("\n");
res = read_and_verify("long_clobber");
TEST_CHECK(res >= 0);
res = SPIFFS_check(FS);
TEST_CHECK(res >= 0);
return TEST_RES_OK;
}
TEST_END(long_run)
SUITE_END(hydrogen_tests)
/*
* test_spiffs.c
*
* Created on: Jun 19, 2013
* Author: petera
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "params_test.h"
#include "spiffs.h"
#include "spiffs_nucleus.h"
#include "testrunner.h"
#include "test_spiffs.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
static unsigned char area[PHYS_FLASH_SIZE];
static int erases[256];
static char _path[256];
static u32_t bytes_rd = 0;
static u32_t bytes_wr = 0;
static u32_t reads = 0;
static u32_t writes = 0;
static u32_t error_after_bytes_written = 0;
static u32_t error_after_bytes_read = 0;
static char error_after_bytes_written_once_only = 0;
static char error_after_bytes_read_once_only = 0;
static char log_flash_ops = 1;
static u32_t fs_check_fixes = 0;
spiffs __fs;
static u8_t _work[LOG_PAGE*2];
static u8_t _fds[FD_BUF_SIZE];
static u8_t _cache[CACHE_BUF_SIZE];
static int check_valid_flash = 1;
#define TEST_PATH "test_data/"
char *make_test_fname(const char *name) {
sprintf(_path, "%s%s", TEST_PATH, name);
return _path;
}
void clear_test_path() {
DIR *dp;
struct dirent *ep;
dp = opendir(TEST_PATH);
if (dp != NULL) {
while ((ep = readdir(dp))) {
if (ep->d_name[0] != '.') {
sprintf(_path, "%s%s", TEST_PATH, ep->d_name);
remove(_path);
}
}
closedir(dp);
}
}
static s32_t _read(u32_t addr, u32_t size, u8_t *dst) {
if (log_flash_ops) {
bytes_rd += size;
reads++;
if (error_after_bytes_read > 0 && bytes_rd >= error_after_bytes_read) {
if (error_after_bytes_read_once_only) {
error_after_bytes_read = 0;
}
return SPIFFS_ERR_TEST;
}
}
if (addr < __fs.cfg.phys_addr) {
printf("FATAL read addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
exit(0);
}
if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) {
printf("FATAL read addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
exit(0);
}
memcpy(dst, &area[addr], size);
return 0;
}
static s32_t _write(u32_t addr, u32_t size, u8_t *src) {
int i;
//printf("wr %08x %i\n", addr, size);
if (log_flash_ops) {
bytes_wr += size;
writes++;
if (error_after_bytes_written > 0 && bytes_wr >= error_after_bytes_written) {
if (error_after_bytes_written_once_only) {
error_after_bytes_written = 0;
}
return SPIFFS_ERR_TEST;
}
}
if (addr < __fs.cfg.phys_addr) {
printf("FATAL write addr too low %08x < %08x\n", addr, SPIFFS_PHYS_ADDR);
exit(0);
}
if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) {
printf("FATAL write addr too high %08x + %08x > %08x\n", addr, size, SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE);
exit(0);
}
for (i = 0; i < size; i++) {
if (((addr + i) & (__fs.cfg.log_page_size-1)) != offsetof(spiffs_page_header, flags)) {
if (check_valid_flash && ((area[addr + i] ^ src[i]) & src[i])) {
printf("trying to write %02x to %02x at addr %08x\n", src[i], area[addr + i], addr+i);
spiffs_page_ix pix = (addr + i) / LOG_PAGE;
dump_page(&__fs, pix);
return -1;
}
}
area[addr + i] &= src[i];
}
return 0;
}
static s32_t _erase(u32_t addr, u32_t size) {
if (addr & (__fs.cfg.phys_erase_block-1)) {
printf("trying to erase at addr %08x, out of boundary\n", addr);
return -1;
}
if (size & (__fs.cfg.phys_erase_block-1)) {
printf("trying to erase at with size %08x, out of boundary\n", size);
return -1;
}
erases[(addr-__fs.cfg.phys_addr)/__fs.cfg.phys_erase_block]++;
memset(&area[addr], 0xff, size);
return 0;
}
void hexdump_mem(u8_t *b, u32_t len) {
while (len--) {
if ((((intptr_t)b)&0x1f) == 0) {
printf("\n");
}
printf("%02x", *b++);
}
printf("\n");
}
void hexdump(u32_t addr, u32_t len) {
int remainder = (addr % 32) == 0 ? 0 : 32 - (addr % 32);
u32_t a;
for (a = addr - remainder; a < addr+len; a++) {
if ((a & 0x1f) == 0) {
if (a != addr) {
printf(" ");
int j;
for (j = 0; j < 32; j++) {
if (a-32+j < addr)
printf(" ");
else {
printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]);
}
}
}
printf("%s %08x: ", a<=addr ? "":"\n", a);
}
if (a < addr) {
printf(" ");
} else {
printf("%02x", area[a]);
}
}
int j;
printf(" ");
for (j = 0; j < 32; j++) {
if (a-32+j < addr)
printf(" ");
else {
printf("%c", (area[a-32+j] < 32 || area[a-32+j] >= 0x7f) ? '.' : area[a-32+j]);
}
}
printf("\n");
}
void dump_page(spiffs *fs, spiffs_page_ix p) {
printf("page %04x ", p);
u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, p);
if (p % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
// obj lu page
printf("OBJ_LU");
} else {
u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , p)) +
SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, p) * sizeof(spiffs_obj_id);
spiffs_obj_id obj_id = *((spiffs_obj_id *)&area[obj_id_addr]);
// data page
spiffs_page_header *ph = (spiffs_page_header *)&area[addr];
printf("DATA %04x:%04x ", obj_id, ph->span_ix);
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_FINAL) == 0) ? "FIN " : "fin ");
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_DELET) == 0) ? "DEL " : "del ");
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_INDEX) == 0) ? "IDX " : "idx ");
printf("%s", ((ph->flags & SPIFFS_PH_FLAG_USED) == 0) ? "USD " : "usd ");
printf("%s ", ((ph->flags & SPIFFS_PH_FLAG_IXDELE) == 0) ? "IDL " : "idl ");
if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) {
// object index
printf("OBJ_IX");
if (ph->span_ix == 0) {
printf("_HDR ");
spiffs_page_object_ix_header *oix_hdr = (spiffs_page_object_ix_header *)&area[addr];
printf("'%s' %i bytes type:%02x", oix_hdr->name, oix_hdr->size, oix_hdr->type);
}
} else {
// data page
printf("CONTENT");
}
}
printf("\n");
u32_t len = fs->cfg.log_page_size;
hexdump(addr, len);
}
void area_write(u32_t addr, u8_t *buf, u32_t size) {
int i;
for (i = 0; i < size; i++) {
area[addr + i] = *buf++;
}
}
void area_read(u32_t addr, u8_t *buf, u32_t size) {
int i;
for (i = 0; i < size; i++) {
*buf++ = area[addr + i];
}
}
void dump_erase_counts(spiffs *fs) {
spiffs_block_ix bix;
printf(" BLOCK |\n");
printf(" AGE COUNT|\n");
for (bix = 0; bix < fs->block_count; bix++) {
printf("----%3i ----|", bix);
}
printf("\n");
for (bix = 0; bix < fs->block_count; bix++) {
spiffs_obj_id erase_mark;
_spiffs_rd(fs, 0, 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), sizeof(spiffs_obj_id), (u8_t *)&erase_mark);
if (erases[bix] == 0) {
printf(" |");
} else {
printf("%7i %4i|", (fs->max_erase_count - erase_mark), erases[bix]);
}
}
printf("\n");
}
void dump_flash_access_stats() {
printf(" RD: %10i reads %10i bytes %10i avg bytes/read\n", reads, bytes_rd, reads == 0 ? 0 : (bytes_rd / reads));
printf(" WR: %10i writes %10i bytes %10i avg bytes/write\n", writes, bytes_wr, writes == 0 ? 0 : (bytes_wr / writes));
}
static u32_t old_perc = 999;
static void spiffs_check_cb_f(spiffs_check_type type, spiffs_check_report report,
u32_t arg1, u32_t arg2) {
/* if (report == SPIFFS_CHECK_PROGRESS && old_perc != arg1) {
old_perc = arg1;
printf("CHECK REPORT: ");
switch(type) {
case SPIFFS_CHECK_LOOKUP:
printf("LU "); break;
case SPIFFS_CHECK_INDEX:
printf("IX "); break;
case SPIFFS_CHECK_PAGE:
printf("PA "); break;
}
printf("%i%%\n", arg1 * 100 / 256);
}*/
if (report != SPIFFS_CHECK_PROGRESS) {
if (report != SPIFFS_CHECK_ERROR) fs_check_fixes++;
printf(" check: ");
switch (type) {
case SPIFFS_CHECK_INDEX:
printf("INDEX "); break;
case SPIFFS_CHECK_LOOKUP:
printf("LOOKUP "); break;
case SPIFFS_CHECK_PAGE:
printf("PAGE "); break;
default:
printf("???? "); break;
}
if (report == SPIFFS_CHECK_ERROR) {
printf("ERROR %i", arg1);
} else if (report == SPIFFS_CHECK_DELETE_BAD_FILE) {
printf("DELETE BAD FILE %04x", arg1);
} else if (report == SPIFFS_CHECK_DELETE_ORPHANED_INDEX) {
printf("DELETE ORPHANED INDEX %04x", arg1);
} else if (report == SPIFFS_CHECK_DELETE_PAGE) {
printf("DELETE PAGE %04x", arg1);
} else if (report == SPIFFS_CHECK_FIX_INDEX) {
printf("FIX INDEX %04x:%04x", arg1, arg2);
} else if (report == SPIFFS_CHECK_FIX_LOOKUP) {
printf("FIX INDEX %04x:%04x", arg1, arg2);
} else {
printf("??");
}
printf("\n");
}
}
void fs_reset_specific(u32_t phys_addr, u32_t phys_size,
u32_t phys_sector_size,
u32_t log_block_size, u32_t log_page_size) {
memset(area, 0xcc, sizeof(area));
memset(&area[phys_addr], 0xff, phys_size);
spiffs_config c;
c.hal_erase_f = _erase;
c.hal_read_f = _read;
c.hal_write_f = _write;
c.log_block_size = log_block_size;
c.log_page_size = log_page_size;
c.phys_addr = phys_addr;
c.phys_erase_block = phys_sector_size;
c.phys_size = phys_size;
memset(erases,0,sizeof(erases));
memset(_cache,0,sizeof(_cache));
SPIFFS_mount(&__fs, &c, _work, _fds, sizeof(_fds), _cache, sizeof(_cache), spiffs_check_cb_f);
clear_flash_ops_log();
log_flash_ops = 1;
fs_check_fixes = 0;
}
void fs_reset() {
fs_reset_specific(SPIFFS_PHYS_ADDR, SPIFFS_FLASH_SIZE, SECTOR_SIZE, LOG_BLOCK, LOG_PAGE);
}
void set_flash_ops_log(int enable) {
log_flash_ops = enable;
}
void clear_flash_ops_log() {
bytes_rd = 0;
bytes_wr = 0;
reads = 0;
writes = 0;
error_after_bytes_read = 0;
error_after_bytes_written = 0;
}
u32_t get_flash_ops_log_read_bytes() {
return bytes_rd;
}
u32_t get_flash_ops_log_write_bytes() {
return bytes_wr;
}
void invoke_error_after_read_bytes(u32_t b, char once_only) {
error_after_bytes_read = b;
error_after_bytes_read_once_only = once_only;
}
void invoke_error_after_write_bytes(u32_t b, char once_only) {
error_after_bytes_written = b;
error_after_bytes_written_once_only = once_only;
}
void fs_set_validate_flashing(int i) {
check_valid_flash = i;
}
void real_assert(int c, const char *n, const char *file, int l) {
if (c == 0) {
printf("ASSERT: %s %s @ %i\n", (n ? n : ""), file, l);
printf("fs errno:%i\n", __fs.err_code);
exit(0);
}
}
int read_and_verify(char *name) {
s32_t res;
int fd = SPIFFS_open(&__fs, name, SPIFFS_RDONLY, 0);
if (fd < 0) {
printf(" read_and_verify: could not open file %s\n", name);
return fd;
}
return read_and_verify_fd(fd, name);
}
int read_and_verify_fd(spiffs_file fd, char *name) {
s32_t res;
int pfd = open(make_test_fname(name), O_RDONLY);
spiffs_stat s;
res = SPIFFS_fstat(&__fs, fd, &s);
if (res < 0) {
printf(" read_and_verify: could not stat file %s\n", name);
return res;
}
if (s.size == 0) {
SPIFFS_close(&__fs, fd);
close(pfd);
return 0;
}
//printf("verifying %s, len %i\n", name, s.size);
int offs = 0;
u8_t buf_d[256];
u8_t buf_v[256];
while (offs < s.size) {
int read_len = MIN(s.size - offs, sizeof(buf_d));
res = SPIFFS_read(&__fs, fd, buf_d, read_len);
if (res < 0) {
printf(" read_and_verify: could not read file %s offs:%i len:%i filelen:%i\n", name, offs, read_len, s.size);
return res;
}
int pres = read(pfd, buf_v, read_len);
(void)pres;
//printf("reading offs:%i len:%i spiffs_res:%i posix_res:%i\n", offs, read_len, res, pres);
int i;
int veri_ok = 1;
for (i = 0; veri_ok && i < read_len; i++) {
if (buf_d[i] != buf_v[i]) {
printf("file verification mismatch @ %i, %02x %c != %02x %c\n", offs+i, buf_d[i], buf_d[i], buf_v[i], buf_v[i]);
int j = MAX(0, i-16);
int k = MIN(sizeof(buf_d), i+16);
k = MIN(s.size-offs, k);
int l;
for (l = j; l < k; l++) {
printf("%c", buf_d[l] > 31 ? buf_d[l] : '.');
}
printf("\n");
for (l = j; l < k; l++) {
printf("%c", buf_v[l] > 31 ? buf_v[l] : '.');
}
printf("\n");
veri_ok = 0;
}
}
if (!veri_ok) {
SPIFFS_close(&__fs, fd);
close(pfd);
printf("data mismatch\n");
return -1;
}
offs += read_len;
}
SPIFFS_close(&__fs, fd);
close(pfd);
return 0;
}
static void test_on_stop(test *t) {
printf(" spiffs errno:%i\n", SPIFFS_errno(&__fs));
#if SPIFFS_TEST_VISUALISATION
SPIFFS_vis(FS);
#endif
}
void memrand(u8_t *b, int len) {
int i;
for (i = 0; i < len; i++) {
b[i] = rand();
}
}
int test_create_file(char *name) {
spiffs_stat s;
spiffs_file fd;
int res = SPIFFS_creat(FS, name, 0);
CHECK_RES(res);
fd = SPIFFS_open(FS, name, SPIFFS_RDONLY, 0);
CHECK(fd >= 0);
res = SPIFFS_fstat(FS, fd, &s);
CHECK_RES(res);
CHECK(strcmp((char*)s.name, name) == 0);
CHECK(s.size == 0);
SPIFFS_close(FS, fd);
return 0;
}
int test_create_and_write_file(char *name, int size, int chunk_size) {
int res;
spiffs_file fd;
printf(" create and write %s", name);
res = test_create_file(name);
if (res < 0) {
printf(" failed creation, %i\n",res);
}
CHECK(res >= 0);
fd = SPIFFS_open(FS, name, SPIFFS_APPEND | SPIFFS_RDWR, 0);
if (res < 0) {
printf(" failed open, %i\n",res);
}
CHECK(fd >= 0);
int pfd = open(make_test_fname(name), O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
int offset = 0;
int mark = 0;
while (offset < size) {
int len = MIN(size-offset, chunk_size);
if (offset > mark) {
mark += size/16;
printf(".");
fflush(stdout);
}
u8_t *buf = malloc(len);
memrand(buf, len);
res = SPIFFS_write(FS, fd, buf, len);
write(pfd, buf, len);
free(buf);
if (res < 0) {
printf("\n error @ offset %i, res %i\n", offset, res);
}
offset += len;
CHECK(res >= 0);
}
printf("\n");
close(pfd);
spiffs_stat stat;
res = SPIFFS_fstat(FS, fd, &stat);
if (res < 0) {
printf(" failed fstat, %i\n",res);
}
CHECK(res >= 0);
if (stat.size != size) {
printf(" failed size, %i != %i\n", stat.size, size);
}
CHECK(stat.size == size);
SPIFFS_close(FS, fd);
return 0;
}
#if SPIFFS_CACHE
#if SPIFFS_CACHE_STATS
static u32_t chits_tot = 0;
static u32_t cmiss_tot = 0;
#endif
#endif
void _setup_test_only() {
fs_set_validate_flashing(1);
test_init(test_on_stop);
}
void _setup() {
fs_reset();
_setup_test_only();
}
void _teardown() {
printf(" free blocks : %i of %i\n", (FS)->free_blocks, (FS)->block_count);
printf(" pages allocated : %i\n", (FS)->stats_p_allocated);
printf(" pages deleted : %i\n", (FS)->stats_p_deleted);
#if SPIFFS_GC_STATS
printf(" gc runs : %i\n", (FS)->stats_gc_runs);
#endif
#if SPIFFS_CACHE
#if SPIFFS_CACHE_STATS
chits_tot += (FS)->cache_hits;
cmiss_tot += (FS)->cache_misses;
printf(" cache hits : %i (sum %i)\n", (FS)->cache_hits, chits_tot);
printf(" cache misses : %i (sum %i)\n", (FS)->cache_misses, cmiss_tot);
printf(" cache utiliz : %f\n", ((float)chits_tot/(float)(chits_tot + cmiss_tot)));
#endif
#endif
dump_flash_access_stats();
clear_flash_ops_log();
#if SPIFFS_GC_STATS
if ((FS)->stats_gc_runs > 0)
#endif
dump_erase_counts(FS);
printf(" fs consistency check:\n");
SPIFFS_check(FS);
clear_test_path();
//hexdump_mem(&area[SPIFFS_PHYS_ADDR - 16], 32);
//hexdump_mem(&area[SPIFFS_PHYS_ADDR + SPIFFS_FLASH_SIZE - 16], 32);
}
u32_t tfile_get_size(tfile_size s) {
switch (s) {
case EMPTY:
return 0;
case SMALL:
return SPIFFS_DATA_PAGE_SIZE(FS)/2;
case MEDIUM:
return SPIFFS_DATA_PAGE_SIZE(FS) * (SPIFFS_PAGES_PER_BLOCK(FS) - SPIFFS_OBJ_LOOKUP_PAGES(FS));
case LARGE:
return (FS)->cfg.phys_size/3;
}
return 0;
}
int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg) {
int res;
tfile *tfiles = malloc(sizeof(tfile) * max_concurrent_files);
memset(tfiles, 0, sizeof(tfile) * max_concurrent_files);
int run = 0;
int cur_config_ix = 0;
char name[32];
while (run < max_runs) {
if (dbg) printf(" run %i/%i\n", run, max_runs);
int i;
for (i = 0; i < max_concurrent_files; i++) {
sprintf(name, "file%i_%i", (1+run), i);
tfile *tf = &tfiles[i];
if (tf->state == 0 && cur_config_ix < cfg_count) {
// create a new file
strcpy(tf->name, name);
tf->state = 1;
tf->cfg = cfgs[cur_config_ix];
int size = tfile_get_size(tf->cfg.tsize);
if (dbg) printf(" create new %s with cfg %i/%i, size %i\n", name, (1+cur_config_ix), cfg_count, size);
if (tf->cfg.tsize == EMPTY) {
res = SPIFFS_creat(FS, name, 0);
CHECK_RES(res);
int pfd = open(make_test_fname(name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
close(pfd);
int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_RDWR, 0);
CHECK(fd > 0);
tf->fd = fd;
} else {
int extra_flags = tf->cfg.ttype == APPENDED ? SPIFFS_APPEND : 0;
spiffs_file fd = SPIFFS_open(FS, name, extra_flags | SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
CHECK(fd > 0);
extra_flags = tf->cfg.ttype == APPENDED ? O_APPEND : 0;
int pfd = open(make_test_fname(name), extra_flags | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
tf->fd = fd;
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, fd, buf, size);
CHECK_RES(res);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(name);
CHECK_RES(res);
}
cur_config_ix++;
} else if (tf->state > 0) {
// hande file lifecycle
switch (tf->cfg.ttype) {
case UNTAMPERED: {
break;
}
case APPENDED: {
if (dbg) printf(" appending %s\n", tf->name);
int size = SPIFFS_DATA_PAGE_SIZE(FS)*3;
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, tf->fd, buf, size);
CHECK_RES(res);
int pfd = open(make_test_fname(tf->name), O_APPEND | O_RDWR);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(tf->name);
CHECK_RES(res);
break;
}
case MODIFIED: {
if (dbg) printf(" modify %s\n", tf->name);
spiffs_stat stat;
res = SPIFFS_fstat(FS, tf->fd, &stat);
CHECK_RES(res);
int size = stat.size / tf->cfg.tlife + SPIFFS_DATA_PAGE_SIZE(FS)/3;
int offs = (stat.size / tf->cfg.tlife) * tf->state;
res = SPIFFS_lseek(FS, tf->fd, offs, SPIFFS_SEEK_SET);
CHECK_RES(res);
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, tf->fd, buf, size);
CHECK_RES(res);
int pfd = open(make_test_fname(tf->name), O_RDWR);
lseek(pfd, offs, SEEK_SET);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(tf->name);
CHECK_RES(res);
break;
}
case REWRITTEN: {
if (tf->fd > 0) {
SPIFFS_close(FS, tf->fd);
}
if (dbg) printf(" rewriting %s\n", tf->name);
spiffs_file fd = SPIFFS_open(FS, tf->name, SPIFFS_TRUNC | SPIFFS_CREAT | SPIFFS_RDWR, 0);
CHECK(fd > 0);
int pfd = open(make_test_fname(tf->name), O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
tf->fd = fd;
int size = tfile_get_size(tf->cfg.tsize);
u8_t *buf = malloc(size);
memrand(buf, size);
res = SPIFFS_write(FS, fd, buf, size);
CHECK_RES(res);
write(pfd, buf, size);
close(pfd);
free(buf);
res = read_and_verify(tf->name);
CHECK_RES(res);
break;
}
}
tf->state++;
if (tf->state > tf->cfg.tlife) {
// file outlived its time, kill it
if (tf->fd > 0) {
SPIFFS_close(FS, tf->fd);
}
if (dbg) printf(" removing %s\n", tf->name);
res = read_and_verify(tf->name);
CHECK_RES(res);
res = SPIFFS_remove(FS, tf->name);
CHECK_RES(res);
remove(make_test_fname(tf->name));
memset(tf, 0, sizeof(tf));
}
}
}
run++;
}
free(tfiles);
return 0;
}
/*
* test_spiffs.h
*
* Created on: Jun 19, 2013
* Author: petera
*/
#ifndef TEST_SPIFFS_H_
#define TEST_SPIFFS_H_
#include "spiffs.h"
#define FS &__fs
extern spiffs __fs;
#define CHECK(r) if (!(r)) return -1;
#define CHECK_RES(r) if (r < 0) return -1;
#define FS_PURE_DATA_PAGES(fs) \
((fs)->cfg.phys_size / (fs)->cfg.log_page_size - (fs)->block_count * SPIFFS_OBJ_LOOKUP_PAGES(fs))
#define FS_PURE_DATA_SIZE(fs) \
FS_PURE_DATA_PAGES(fs) * SPIFFS_DATA_PAGE_SIZE(fs)
typedef enum {
EMPTY,
SMALL,
MEDIUM,
LARGE,
} tfile_size;
typedef enum {
UNTAMPERED,
APPENDED,
MODIFIED,
REWRITTEN,
} tfile_type;
typedef enum {
SHORT = 4,
NORMAL = 20,
LONG = 100,
} tfile_life;
typedef struct {
tfile_size tsize;
tfile_type ttype;
tfile_life tlife;
} tfile_conf;
typedef struct {
int state;
spiffs_file fd;
tfile_conf cfg;
char name[32];
} tfile;
void fs_reset();
void fs_reset_specific(u32_t phys_addr, u32_t phys_size,
u32_t phys_sector_size,
u32_t log_block_size, u32_t log_page_size);
int read_and_verify(char *name);
int read_and_verify_fd(spiffs_file fd, char *name);
void dump_page(spiffs *fs, spiffs_page_ix p);
void hexdump(u32_t addr, u32_t len);
char *make_test_fname(const char *name);
void clear_test_path();
void area_write(u32_t addr, u8_t *buf, u32_t size);
void area_read(u32_t addr, u8_t *buf, u32_t size);
void dump_erase_counts(spiffs *fs);
void dump_flash_access_stats();
void set_flash_ops_log(int enable);
void clear_flash_ops_log();
u32_t get_flash_ops_log_read_bytes();
u32_t get_flash_ops_log_write_bytes();
void invoke_error_after_read_bytes(u32_t b, char once_only);
void invoke_error_after_write_bytes(u32_t b, char once_only);
void memrand(u8_t *b, int len);
int test_create_file(char *name);
int test_create_and_write_file(char *name, int size, int chunk_size);
void _setup();
void _setup_test_only();
void _teardown();
u32_t tfile_get_size(tfile_size s);
int run_file_config(int cfg_count, tfile_conf* cfgs, int max_runs, int max_concurrent_files, int dbg);
#endif /* TEST_SPIFFS_H_ */
/*
* testrunner.c
*
* Created on: Jun 18, 2013
* Author: petera
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include "testrunner.h"
static struct {
test *tests;
test *_last_test;
int test_count;
void (*on_stop)(test *t);
test_res *failed;
test_res *failed_last;
test_res *stopped;
test_res *stopped_last;
FILE *spec;
} test_main;
void test_init(void (*on_stop)(test *t)) {
test_main.on_stop = on_stop;
}
static char check_spec(char *name) {
if (test_main.spec) {
fseek(test_main.spec, 0, SEEK_SET);
char *line = NULL;
size_t sz;
ssize_t read;
while ((read = getline(&line, &sz, test_main.spec)) != -1) {
if (strncmp(line, name, strlen(name)) == 0) {
free(line);
return 1;
}
}
free(line);
return 0;
} else {
return 1;
}
}
void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)) {
if (f == 0) return;
if (!check_spec(name)) return;
DBGT("adding test %s\n", name);
test *t = malloc(sizeof(test));
memset(t, 0, sizeof(test));
t->f = f;
strcpy(t->name, name);
t->setup = setup;
t->teardown = teardown;
if (test_main.tests == 0) {
test_main.tests = t;
} else {
test_main._last_test->_next = t;
}
test_main._last_test = t;
test_main.test_count++;
}
static void add_res(test *t, test_res **head, test_res **last) {
test_res *tr = malloc(sizeof(test_res));
memset(tr,0,sizeof(test_res));
strcpy(tr->name, t->name);
if (*head == 0) {
*head = tr;
} else {
(*last)->_next = tr;
}
*last = tr;
}
static void dump_res(test_res **head) {
test_res *tr = (*head);
while (tr) {
test_res *next_tr = tr->_next;
printf(" %s\n", tr->name);
free(tr);
tr = next_tr;
}
}
void run_tests(int argc, char **args) {
memset(&test_main, 0, sizeof(test_main));
if (argc > 1) {
printf("running tests from %s\n", args[1]);
FILE *fd = fopen(args[1], "r");
if (fd == NULL) {
printf("%s not found\n", args[1]);
exit(EXIT_FAILURE);
}
test_main.spec = fd;
}
DBGT("adding suites...\n");
add_suites();
DBGT("%i tests added\n", test_main.test_count);
if (test_main.spec) {
fclose(test_main.spec);
}
if (test_main.test_count == 0) {
printf("No tests to run\n");
return;
}
int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
DBGT("running tests...\n");
int ok = 0;
int failed = 0;
int stopped = 0;
test *cur_t = test_main.tests;
int i = 1;
while (cur_t) {
cur_t->setup(cur_t);
test *next_test = cur_t->_next;
DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name);
i++;
int res = cur_t->f(cur_t);
cur_t->teardown(cur_t);
int fd = res == TEST_RES_OK ? fd_success : fd_bad;
write(fd, cur_t->name, strlen(cur_t->name));
write(fd, "\n", 1);
switch (res) {
case TEST_RES_OK:
ok++;
printf(" .. ok\n");
break;
case TEST_RES_FAIL:
failed++;
printf(" .. FAILED\n");
if (test_main.on_stop) test_main.on_stop(cur_t);
add_res(cur_t, &test_main.failed, &test_main.failed_last);
break;
case TEST_RES_ASSERT:
stopped++;
printf(" .. ABORTED\n");
if (test_main.on_stop) test_main.on_stop(cur_t);
add_res(cur_t, &test_main.stopped, &test_main.stopped_last);
break;
}
free(cur_t);
cur_t = next_test;
}
close(fd_success);
close(fd_bad);
DBGT("ran %i tests\n", test_main.test_count);
printf("Test report, %i tests\n", test_main.test_count);
printf("%i succeeded\n", ok);
printf("%i failed\n", failed);
dump_res(&test_main.failed);
printf("%i stopped\n", stopped);
dump_res(&test_main.stopped);
if (ok < test_main.test_count) {
printf("\nFAILED\n");
} else {
printf("\nALL TESTS OK\n");
}
}
/*
* testrunner.h
*
* Created on: Jun 19, 2013
* Author: petera
*/
/*
SUITE(mysuite)
void setup(test *t) {}
void teardown(test *t) {}
TEST(mytest) {
printf("mytest runs now..\n");
return 0;
} TEST_END(mytest)
SUITE_END(mysuite)
SUITE(mysuite2)
void setup(test *t) {}
void teardown(test *t) {}
TEST(mytest2a) {
printf("mytest2a runs now..\n");
return 0;
} TEST_END(mytest2a)
TEST(mytest2b) {
printf("mytest2b runs now..\n");
return 0;
} TEST_END(mytest2b)
SUITE_END(mysuite2)
void add_suites() {
ADD_SUITE(mysuite);
ADD_SUITE(mysuite2);
}
*/
#ifndef TESTS_H_
#define TESTS_H_
#define TEST_RES_OK 0
#define TEST_RES_FAIL -1
#define TEST_RES_ASSERT -2
struct test_s;
typedef int (*test_f)(struct test_s *t);
typedef struct test_s {
test_f f;
char name[256];
void *data;
void (*setup)(struct test_s *t);
void (*teardown)(struct test_s *t);
struct test_s *_next;
} test;
typedef struct test_res_s {
char name[256];
struct test_res_s *_next;
} test_res;
#define TEST_CHECK(x) if (!(x)) { \
printf(" TEST FAIL %s:%i\n", __FILE__, __LINE__); \
goto __fail_stop; \
}
#define TEST_ASSERT(x) if (!(x)) { \
printf(" TEST ASSERT %s:%i\n", __FILE__, __LINE__); \
goto __fail_assert; \
}
#define DBGT(...) printf(__VA_ARGS__)
#define str(s) #s
#define SUITE(sui) \
extern void __suite_##sui() {
#define SUITE_END(sui) \
}
#define ADD_SUITE(sui) \
__suite_##sui();
#define TEST(tf) \
int tf(struct test_s *t) { do
#define TEST_END(tf) \
while(0); \
__fail_stop: return TEST_RES_FAIL; \
__fail_assert: return TEST_RES_ASSERT; \
} \
add_test(tf, str(tf), setup, teardown);
void add_suites();
void test_init(void (*on_stop)(test *t));
void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t));
void run_tests(int argc, char **args);
#endif /* TESTS_H_ */
/*
* testsuites.c
*
* Created on: Jun 19, 2013
* Author: petera
*/
#include "testrunner.h"
void add_suites() {
//ADD_SUITE(dev_tests);
ADD_SUITE(check_tests);
ADD_SUITE(hydrogen_tests)
ADD_SUITE(bug_tests)
}
...@@ -90,16 +90,6 @@ void nodemcu_init(void) ...@@ -90,16 +90,6 @@ void nodemcu_init(void)
// Fit hardware real flash size. // Fit hardware real flash size.
flash_rom_set_size_byte(flash_safe_get_size_byte()); flash_rom_set_size_byte(flash_safe_get_size_byte());
if( !fs_format() )
{
NODE_ERR( "\ni*** ERROR ***: unable to format. FS might be compromised.\n" );
NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
}
else{
NODE_ERR( "format done.\n" );
}
fs_unmount(); // mounted by format.
// Reboot to get SDK to use (or write) init data at new location // Reboot to get SDK to use (or write) init data at new location
system_restart (); system_restart ();
...@@ -109,7 +99,15 @@ void nodemcu_init(void) ...@@ -109,7 +99,15 @@ void nodemcu_init(void)
#endif // defined(FLASH_SAFE_API) #endif // defined(FLASH_SAFE_API)
#if defined ( BUILD_SPIFFS ) #if defined ( BUILD_SPIFFS )
fs_mount(); if (!fs_mount()) {
// Failed to mount -- try reformat
c_printf("Formatting file system.\n");
if (!fs_format()) {
NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" );
NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" );
}
// Note that fs_format leaves the file system mounted
}
// test_spiffs(); // test_spiffs();
#endif #endif
// endpoint_setup(); // endpoint_setup();
......
# SPIFFS File System
The NodeMCU project uses the [SPIFFS](https://github.com/pellepl/spiffs)
filesystem to store files in the flash chip. The technical details about how this is configured can be found below, along with various build time options.
# spiffsimg - Manipulate SPI Flash File System disk images
Ever wished you could prepare a SPIFFS image offline and flash the whole
thing onto your microprocessor's storage instead of painstakingly upload
file-by-file through your app on the micro? With spiffsimg you can!
NodeMCU uses a SPIFFS filesystem that knows how big it is -- i.e. when you build a file system
image, it must fit into the flash chip, and it cannot be expanded once flashed.
It is important to give the `spiffimg` tool the correct size. You can provide either the `-c` option or both the `-U` and `-S` options.
### Syntax
```
spiffsimg -f <filename>
[-o <offsetfile>]
[-c <size>]
[-S <flashsize>]
[-U <usedsize>]
[-d]
[-l | -i | -r <scriptname> ]
```
### Supported operations:
* `-f` specifies the filename for the disk image. '%x' will be replaced by the calculated offset of the file system.
* `-o` specifies the file which is to contain the calculated offset.
* `-S` specifies the size of the flash chip. `32m` is 32 mbits, `1MB` is 1 megabyte.
* `-U` specifies the amount of flash used by the firmware. Decimal or Hex bytes.
* `-c` Create a blank disk image of the given size.
* `-l` List the contents of the given disk image.
* `-i` Interactive commands.
* `-r` Scripted commands from filename.
* `-d` causes the disk image to be deleted on error. This makes it easier to script.
### Available commands:
* `ls` List contents. Output format is {type} {size} {name}.
* `cat <filename>` Dump file contents to stdout.
* `rm <filename>` Delete file.
* `info` Display SPIFFS usage estimates.
* `import <srcfile> <spiffsname>` Import a file into the disk image.
* `export <spiffsname> <dstfile>` Export a file from the disk image.
### Example:
```lua
# spiffsimg -f flash.img -S 32m -U 524288 -i
> import myapp/lua/init.lua init.lua
> import myapp/lua/httpd.lua httpd.lua
> import myapp/html/index.html http/index.html
> import myapp/html/favicon.ico http/favicon.ico
> ls
f 122 init.lua
f 5169 httpd.lua
f 2121 http/index.html
f 880 http/favicon.ico
>^D
#
```
### Known limitations:
* The block & page sizes are hard-coded to be compatible with nodemcu.
* Error handling is not entirely consistent, some errors result in an
early exit, others just print an error (both cause a non-zero exit though).
* Only flat SPIFFS is supported.
# Technical Details
The SPIFFS configuration is 4k sectors (the only size supported by the SDK) and 8k blocks. 256 byte pages. Magic is enabled and magic_len is also enabled. This allows the firmware to find the start of the filesystem (and also the size).
One of the goals is to make the filsystem more persistent across reflashing of the firmware.
There are two significant sizes of flash -- the 512K and 4M (or bigger).
The file system has to start on a 4k boundary, but since it ends on a much bigger boundary (a 16k boundary), it also starts on an 8k boundary. For the small flash chip, there is
not much spare space, so a newly formatted file system will start as low as possible (to get as much space as possible). For the large flash, the
file system will start on a 64k boundary. A newly formatted file system will start between 64k and 128k from the end of the firmware. This means that the file
system will survive lots of reflashing and at least 64k of firmware growth.
The spiffsimg tool can also be built (from source) in the nodemcu-firmware build tree. If there is any data in the `local/fs` directory tree, then it will
be copied into the flash disk image. Two images will normally be created -- one for the 512k flash part and the other for the 4M flash part. If the data doesn't
fit into the 512k part after the firmware is included, then the file will not be generated.
The disk image file is placed into the `bin` directory and it is named `0x<offset>-<size>.bin` where the offset is the location where it should be
flashed, and the size is the size of the flash part. It is quite valid (and quicker) to flash the 512k image into a 4M part. However, there will probably be
limited space in the file system for creating new files.
If no file system is found during platform boot, then a new file system will be formatted. This can take some time on the first boot.
Note that the last 16k of the flash chip is reserved for the SDK to store parameters (such as the client wifi settings).
In order to speed up the boot time, it is possible to define (at build time) the size of the SPIFFS Filesystem to be formatted. This can be as small as 32768 bytes which gives a filesystem with about 15k bytes of usable space.
Just place the following define in `user_config.h` or some other file that is included during the build.
```
#define SPIFFS_MAX_FILESYSTEM_SIZE 32768
```
This filesystem size limit only affects the formatting of a file system -- if the firm finds an existing valid filesystem (of any size) it will use that. However, if the
filesystem is reformatted from Lua (using file.format()) then the new file system will obey the size limit.
There is also an option to control the positioning of the SPIFFS file system:
```
#define SPIFFS_FIXED_LOCATION 0x100000
```
This specifies that the SPIFFS filesystem starts at 1Mb from the start of the flash. Unless otherwise specified, it will run to the end of the flash (excluding the 16k of space reserved by the SDK).
There is an option that limits the size of the file system to run up to the next 1MB boundary (minus the 16k for the parameter space). This may be useful when dealing with OTA upgrades.
```
#define SPIFFS_SIZE_1M_BOUNDARY
```
# Ignore everything
*
# But not this file itself.
!.gitignore
...@@ -26,6 +26,7 @@ pages: ...@@ -26,6 +26,7 @@ pages:
- Home: 'en/index.md' - Home: 'en/index.md'
- Building the firmware: 'en/build.md' - Building the firmware: 'en/build.md'
- Flashing the firmware: 'en/flash.md' - Flashing the firmware: 'en/flash.md'
- Filesystem notes: 'en/spiffs.md'
- Uploading code: 'en/upload.md' - Uploading code: 'en/upload.md'
- FAQs: - FAQs:
- Lua Developer FAQ: 'en/lua-developer-faq.md' - Lua Developer FAQ: 'en/lua-developer-faq.md'
......
#############################################################
# Options
#
FSSOURCE ?= ../local/fs/
FLASHSIZE ?= 4mb 32mb 8mb
SUBDIRS =
OBJDUMP = $(or $(shell which objdump),xtensa-lx106-elf-objdump)
#############################################################
# Get the files to pack into the spiffs image
#
SPIFFSFILES ?= $(patsubst $(FSSOURCE)%,%,$(shell find $(FSSOURCE) -name '*' '!' -name .gitignore ))
#################################################################
# Get the filesize of /bin/0x10000.bin
#
FLASH_USED_END = $$((0x`$(OBJDUMP) -t ../app/.output/eagle/debug/image/eagle.app.v6.out |grep _flash_used_end |cut -f1 -d" "` - 0x40200000))
#############################################################
# Rules base
#
#
all: spiffsscript
spiffsscript: remove-image
$(MAKE) -C spiffsimg CC=gcc
rm -f ./spiffsimg/spiffs.lst
echo "" >> ./spiffsimg/spiffs.lst
@$(foreach f, $(SPIFFSFILES), echo "import $(FSSOURCE)$(f) $(f)" >> ./spiffsimg/spiffs.lst ;)
@$(foreach sz, $(FLASHSIZE), spiffsimg/spiffsimg -U $(FLASH_USED_END) -o ../bin/spiffs-$(sz).dat -f ../bin/0x%x-$(sz).bin -S $(sz) -r ./spiffsimg/spiffs.lst -d; )
@$(foreach sz, $(FLASHSIZE), if [ -r ../bin/spiffs-$(sz).dat ]; then echo Built $$(cat ../bin/spiffs-$(sz).dat)-$(sz).bin; fi; )
remove-image:
$(foreach sz, $(FLASHSIZE), if [ -r ../bin/spiffs-$(sz).dat ]; then rm -f ../bin/$$(cat ../bin/spiffs-$(sz).dat)-$(sz).bin; fi; )
rm -f ../bin/spiffs*.dat
spiffsclean: remove-image
rm -f ./spiffsimg/spiffsimg
rm -f ./spiffsimg/spiffs.lst
spiffs.lst
spiffsimg
SRCS=\
main.c \
../../app/spiffs/spiffs_cache.c ../../app/spiffs/spiffs_check.c ../../app/spiffs/spiffs_gc.c ../../app/spiffs/spiffs_hydrogen.c ../../app/spiffs/spiffs_nucleus.c
CFLAGS=-g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -I. -I../../app/spiffs -DNODEMCU_SPIFFS_NO_INCLUDE --include spiffs_typedefs.h
spiffsimg: $(SRCS)
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
clean:
rm -f spiffsimg
# spiffsimg - Manipulate SPI Flash File System disk images
Ever wished you could prepare a SPIFFS image offline and flash the whole
thing onto your microprocessor's storage instead of painstakingly upload
file-by-file through your app on the micro? With spiffsimg you can!
For the full gory details see [spiffs.md](../../docs/en/spiffs.md)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment