diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index fa841c64fa..31d1809b21 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -205,7 +205,7 @@ File Dir::openFile(const char* mode) { return File(); } - File f(_impl->openFile(om, am), _baseFS); + auto f = File(_impl->openFile(om, am), _baseFS); f.setTimeCallback(timeCallback); return f; } @@ -350,7 +350,7 @@ File FS::open(const char* path, const char* mode) { DEBUGV("FS::open: invalid mode `%s`\r\n", mode); return File(); } - File f(_impl->open(path, om, am), this); + auto f = File(_impl->open(path, om, am), this); f.setTimeCallback(timeCallback); return f; } @@ -371,7 +371,7 @@ Dir FS::openDir(const char* path) { return Dir(); } DirImplPtr p = _impl->openDir(path); - Dir d(p, this); + auto d = Dir(p, this); d.setTimeCallback(timeCallback); return d; } diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index 23d05bad83..c737516cfa 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -189,10 +189,19 @@ class SPIFFSConfig : public FSConfig { public: static constexpr uint32_t FSId = 0x53504946; - SPIFFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { } + SPIFFSConfig(bool autoFormat = true, bool enableTime = true) { + _type = SPIFFSConfig::FSId; + _autoFormat = autoFormat; + _enableTime = enableTime; + } + + SPIFFSConfig setEnableTime(bool val = true) { + _enableTime = val; + return *this; + } // Inherit _type and _autoFormat - // nothing yet, enableTime TBD when SPIFFS has metadate + bool _enableTime; }; class FS diff --git a/cores/esp8266/spiffs/spiffs.h b/cores/esp8266/spiffs/spiffs.h index e659bf2f37..e2e61f854e 100644 --- a/cores/esp8266/spiffs/spiffs.h +++ b/cores/esp8266/spiffs/spiffs.h @@ -220,12 +220,13 @@ typedef struct { // logical size of a page, must be at least // log_block_size / 8 u32_t log_page_size; - #endif #if SPIFFS_FILEHDL_OFFSET // an integer offset added to each file handle u16_t fh_ix_offset; #endif + // active metadata length + u32_t obj_meta_len; } spiffs_config; typedef struct spiffs_t { @@ -300,9 +301,7 @@ typedef struct { spiffs_obj_type type; spiffs_page_ix pix; u8_t name[SPIFFS_OBJ_NAME_LEN]; -#if SPIFFS_OBJ_META_LEN - u8_t meta[SPIFFS_OBJ_META_LEN]; -#endif + u8_t meta[SPIFFS_MAX_META]; // Set to the largest supported metadata len } spiffs_stat; struct spiffs_dirent { @@ -311,9 +310,7 @@ struct spiffs_dirent { spiffs_obj_type type; u32_t size; spiffs_page_ix pix; -#if SPIFFS_OBJ_META_LEN - u8_t meta[SPIFFS_OBJ_META_LEN]; -#endif + u8_t meta[SPIFFS_MAX_META]; // Set to the largest supported metadata len }; typedef struct { @@ -534,7 +531,7 @@ s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); */ s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newPath); -#if SPIFFS_OBJ_META_LEN + /** * Updates file's metadata * @param fs the file system struct @@ -550,7 +547,7 @@ s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. */ s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); -#endif + /** * Returns last error of last file operation. diff --git a/cores/esp8266/spiffs/spiffs_check.cpp b/cores/esp8266/spiffs/spiffs_check.cpp index 258efb30c7..a23dc1bcb5 100644 --- a/cores/esp8266/spiffs/spiffs_check.cpp +++ b/cores/esp8266/spiffs/spiffs_check.cpp @@ -64,7 +64,7 @@ static s32_t spiffs_object_get_data_page_index_reference( u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); if (objix_spix == 0) { // get referenced page from object index header - addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); + addr += sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN + data_spix * sizeof(spiffs_page_ix); } else { // get referenced page from object index addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); @@ -136,7 +136,7 @@ static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ // rewrite in mem if (objix_spix == 0) { - ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix] = new_data_pix; } else { ((spiffs_page_ix*)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; } @@ -574,7 +574,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) { // object header page index entries = SPIFFS_OBJ_HDR_IX_LEN(fs); data_spix_offset = 0; - object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN); } else { // object page index entries = SPIFFS_OBJ_IX_LEN(fs); diff --git a/cores/esp8266/spiffs/spiffs_config.h b/cores/esp8266/spiffs/spiffs_config.h index c3343fe2fe..41c035d9a9 100644 --- a/cores/esp8266/spiffs/spiffs_config.h +++ b/cores/esp8266/spiffs/spiffs_config.h @@ -25,6 +25,21 @@ typedef uint16_t u16_t; typedef int8_t s8_t; typedef uint8_t u8_t; + +// Enable supporting both timestamped and non-timestamped FS images by making +// the meta-len a global variable here and by changing the logic of SPIFFS.begin. +#define SPIFFS_OBJ_META_LEN (fs->cfg.obj_meta_len) +#define SPIFFS_MAX_META (4) // Maximum metadata size allowed at runtime +// Because SPIFFS reads binary images of blocks, we need to be sure to actually ensure there is space to +// keep the metadata (since 0 bytes are allocated in the struct itself). Use an anonymous union to ensure +// that there is at least MAX_META bytes after the end of the struct. +#define alloc_spiffs_page_object_ix_header(name) \ + union { \ + spiffs_page_object_ix_header name; \ + char xxx[sizeof(spiffs_page_object_ix_header) + SPIFFS_MAX_META]; \ + }; + + #ifndef SEEK_SET #define SEEK_SET 0 /* set file offset to offset */ #endif diff --git a/cores/esp8266/spiffs/spiffs_gc.cpp b/cores/esp8266/spiffs/spiffs_gc.cpp index fe556d1fc7..0afa257423 100644 --- a/cores/esp8266/spiffs/spiffs_gc.cpp +++ b/cores/esp8266/spiffs/spiffs_gc.cpp @@ -467,7 +467,7 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { // update memory representation of object index page with new data page if (gc.cur_objix_spix == 0) { // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[p_hdr.span_ix] = new_data_pix; SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page " _SPIPRIpg" to objix_hdr entry " _SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); } else { // update object index page diff --git a/cores/esp8266/spiffs/spiffs_hydrogen.cpp b/cores/esp8266/spiffs/spiffs_hydrogen.cpp index fffc5a83a8..9310474dff 100644 --- a/cores/esp8266/spiffs/spiffs_hydrogen.cpp +++ b/cores/esp8266/spiffs/spiffs_hydrogen.cpp @@ -729,10 +729,10 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { (void)fh; - spiffs_page_object_ix_header objix_hdr; + alloc_spiffs_page_object_ix_header(objix_hdr); spiffs_obj_id obj_id; s32_t res =_spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, - SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, (u8_t *)&objix_hdr); SPIFFS_API_CHECK_RES(fs, res); u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs , pix)) + @@ -746,9 +746,8 @@ static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spi s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; s->pix = pix; strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN); -#if SPIFFS_OBJ_META_LEN - _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); -#endif + if (SPIFFS_OBJ_META_LEN) + _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); return res; } @@ -928,7 +927,7 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { #endif // SPIFFS_READ_ONLY } -#if SPIFFS_OBJ_META_LEN + s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { #if SPIFFS_READ_ONLY (void)fs; (void)name; (void)meta; @@ -953,8 +952,7 @@ s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { } SPIFFS_API_CHECK_RES_UNLOCK(fs, res); - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, - 0, &pix_dummy); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, (const u8_t*)meta, 0, &pix_dummy); spiffs_fd_return(fs, fd->file_nbr); @@ -988,8 +986,7 @@ s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { SPIFFS_API_CHECK_RES_UNLOCK(fs, res); } - res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, - 0, &pix_dummy); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, (const u8_t*)meta, 0, &pix_dummy); SPIFFS_API_CHECK_RES_UNLOCK(fs, res); @@ -998,7 +995,7 @@ s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { return res; #endif // SPIFFS_READ_ONLY } -#endif // SPIFFS_OBJ_META_LEN + spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { SPIFFS_API_DBG("%s\n", __func__); @@ -1029,7 +1026,7 @@ static s32_t spiffs_read_dir_v( void *user_var_p) { (void)user_const_p; s32_t res; - spiffs_page_object_ix_header objix_hdr; + alloc_spiffs_page_object_ix_header(objix_hdr); if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { return SPIFFS_VIS_COUNTINUE; @@ -1037,7 +1034,7 @@ static s32_t spiffs_read_dir_v( spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, (u8_t *)&objix_hdr); if (res != SPIFFS_OK) return res; if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && objix_hdr.p_hdr.span_ix == 0 && @@ -1049,9 +1046,8 @@ static s32_t spiffs_read_dir_v( e->type = objix_hdr.type; e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; e->pix = pix; -#if SPIFFS_OBJ_META_LEN - _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); -#endif + if (SPIFFS_OBJ_META_LEN) + _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); return SPIFFS_OK; } return SPIFFS_VIS_COUNTINUE; diff --git a/cores/esp8266/spiffs/spiffs_nucleus.cpp b/cores/esp8266/spiffs/spiffs_nucleus.cpp index 35238f351b..afaae9ac15 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.cpp +++ b/cores/esp8266/spiffs/spiffs_nucleus.cpp @@ -319,6 +319,7 @@ s32_t spiffs_probe( #endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + static s32_t spiffs_obj_lu_scan_v( spiffs *fs, spiffs_obj_id obj_id, @@ -635,7 +636,7 @@ static void spiffs_update_ix_map(spiffs *fs, spiffs_page_ix objix_data_pix; if (objix_spix == 0) { // get data page from object index header page - objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[map_spix]; } else { // get data page from object index page objix_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; @@ -915,7 +916,7 @@ s32_t spiffs_object_create( spiffs_page_ix *objix_hdr_pix) { s32_t res = SPIFFS_OK; spiffs_block_ix bix; - spiffs_page_object_ix_header oix_hdr; + alloc_spiffs_page_object_ix_header(oix_hdr); int entry; res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); @@ -942,19 +943,19 @@ s32_t spiffs_object_create( oix_hdr.type = type; oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); -#if SPIFFS_OBJ_META_LEN - if (meta) { - _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + if (SPIFFS_OBJ_META_LEN) { + if (meta) { + _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } else { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } } else { - memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + (void) meta; } -#else - (void) meta; -#endif // update page res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&oix_hdr); + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, (u8_t*)&oix_hdr); SPIFFS_CHECK_RES(res); spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, @@ -1006,13 +1007,13 @@ s32_t spiffs_object_update_index_hdr( if (name) { strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); } -#if SPIFFS_OBJ_META_LEN - if (meta) { - _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + if (SPIFFS_OBJ_META_LEN) { + if (meta) { + _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } + } else { + (void) meta; } -#else - (void) meta; -#endif if (size) { objix_hdr->size = size; } @@ -1174,11 +1175,11 @@ s32_t spiffs_object_open_by_page( spiffs_mode mode) { (void)mode; s32_t res = SPIFFS_OK; - spiffs_page_object_ix_header oix_hdr; + alloc_spiffs_page_object_ix_header(oix_hdr); spiffs_obj_id obj_id; res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, - fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, (u8_t *)&oix_hdr); SPIFFS_CHECK_RES(res); spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); @@ -1354,7 +1355,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // append to existing page, fill out free data in existing page if (cur_objix_spix == 0) { // get data page from object index header page - data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + data_page = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix]; } else { // get data page from object index page data_page = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; @@ -1374,7 +1375,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // update memory representation of object index page with new data page if (cur_objix_spix == 0) { // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix] = data_page; SPIFFS_DBG("append: " _SPIPRIid " wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", fd->obj_id , data_page, data_spix); objix_hdr->size = offset+written; @@ -1543,7 +1544,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { spiffs_page_ix orig_data_pix; if (cur_objix_spix == 0) { // get data page from object index header page - orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix]; } else { // get data page from object index page orig_data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; @@ -1606,7 +1607,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { // update memory representation of object index page with new data page if (cur_objix_spix == 0) { // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix] = data_pix; SPIFFS_DBG("modify: wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", data_pix, data_spix); } else { // update object index page @@ -1664,14 +1665,14 @@ static s32_t spiffs_object_find_object_index_header_by_name_v( void *user_var_p) { (void)user_var_p; s32_t res; - spiffs_page_object_ix_header objix_hdr; + alloc_spiffs_page_object_ix_header(objix_hdr); spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { return SPIFFS_VIS_COUNTINUE; } res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, (u8_t *)&objix_hdr); SPIFFS_CHECK_RES(res); if (objix_hdr.p_hdr.span_ix == 0 && (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == @@ -1814,8 +1815,8 @@ s32_t spiffs_object_truncate( if (cur_objix_spix == 0) { // get data page from object index header page - data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix]; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix] = SPIFFS_OBJ_ID_FREE; } else { // get data page from object index page data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; @@ -1887,7 +1888,7 @@ s32_t spiffs_object_truncate( // update memory representation of object index page with new data page if (cur_objix_spix == 0) { // update object index header page - ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix] = new_data_pix; SPIFFS_DBG("truncate: wrote page " _SPIPRIpg " to objix_hdr entry " _SPIPRIsp " in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); } else { // update object index page @@ -1920,8 +1921,8 @@ s32_t spiffs_object_truncate( } else { // make uninitialized object SPIFFS_DBG("truncate: reset objix_hdr page " _SPIPRIpg "\n", objix_pix); - memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, - SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + memset(fs->work + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)-SPIFFS_OBJ_META_LEN); res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); SPIFFS_CHECK_RES(res); @@ -2014,7 +2015,7 @@ s32_t spiffs_object_read( if (cur_objix_spix == 0) { // get data page from object index header page - data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + data_pix = ((spiffs_page_ix*)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN))[data_spix]; } else { // get data page from object index page data_pix = ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; @@ -2070,9 +2071,9 @@ static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id i if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); int res; - spiffs_page_object_ix_header objix_hdr; + alloc_spiffs_page_object_ix_header(objix_hdr); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, (u8_t *)&objix_hdr); SPIFFS_CHECK_RES(res); if (objix_hdr.p_hdr.span_ix == 0 && (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == @@ -2099,10 +2100,10 @@ static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) { s32_t res; const spiffs_free_obj_id_state *state = (const spiffs_free_obj_id_state*)user_const_p; - spiffs_page_object_ix_header objix_hdr; + alloc_spiffs_page_object_ix_header(objix_hdr); res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, - 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, ix_entry), sizeof(spiffs_page_object_ix_header), (u8_t*)&objix_hdr); + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, ix_entry), sizeof(spiffs_page_object_ix_header)+SPIFFS_OBJ_META_LEN, (u8_t*)&objix_hdr); if (res == SPIFFS_OK && objix_hdr.p_hdr.span_ix == 0 && ((objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) == (SPIFFS_PH_FLAG_DELET))) { diff --git a/cores/esp8266/spiffs/spiffs_nucleus.h b/cores/esp8266/spiffs/spiffs_nucleus.h index cc195c7eca..f39a5def50 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.h +++ b/cores/esp8266/spiffs/spiffs_nucleus.h @@ -248,7 +248,7 @@ extern "C" { // entries in an object header page index #define SPIFFS_OBJ_HDR_IX_LEN(fs) \ - ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)-SPIFFS_OBJ_META_LEN)/sizeof(spiffs_page_ix)) // entries in an object page index #define SPIFFS_OBJ_IX_LEN(fs) \ ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) @@ -509,10 +509,8 @@ typedef struct SPIFFS_PACKED spiffs_obj_type type; // name of object u8_t name[SPIFFS_OBJ_NAME_LEN]; -#if SPIFFS_OBJ_META_LEN // metadata. not interpreted by SPIFFS in any way. - u8_t meta[SPIFFS_OBJ_META_LEN]; -#endif + u8_t meta[]; // Leave array size undefined, since it is variable at runtime } spiffs_page_object_ix_header; // object index page header diff --git a/cores/esp8266/spiffs_api.cpp b/cores/esp8266/spiffs_api.cpp index 1f0278bfc5..53545c3607 100644 --- a/cores/esp8266/spiffs_api.cpp +++ b/cores/esp8266/spiffs_api.cpp @@ -42,6 +42,7 @@ int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) { namespace spiffs_impl { + FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode) { if (!isSpiffsFilenameValid(path)) { @@ -66,6 +67,14 @@ FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode acc fd, path, openMode, accessMode, _fs.err_code); return FileImplPtr(); } + if (!(mode & SPIFFS_O_RDONLY) && _fs.cfg.obj_meta_len) { + time_t t = time(NULL); + struct tm tmr; + localtime_r(&t, &tmr); + time_t meta = mktime(&tmr); + DEBUGV("SPIFFSImpl::open updating file write time to %ld\r\n", meta); + SPIFFS_fupdate_meta(&_fs, fd, &meta); + } return std::make_shared(this, fd); } diff --git a/cores/esp8266/spiffs_api.h b/cores/esp8266/spiffs_api.h index a40d59b0a8..caaf538998 100644 --- a/cores/esp8266/spiffs_api.h +++ b/cores/esp8266/spiffs_api.h @@ -171,7 +171,7 @@ class SPIFFSImpl : public FSImpl return false; } _cfg = *static_cast(&cfg); - return true; + return true; } bool begin() override @@ -187,9 +187,7 @@ class SPIFFSImpl : public FSImpl return true; } if (_cfg._autoFormat) { - auto rc = SPIFFS_format(&_fs); - if (rc != SPIFFS_OK) { - DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code); + if (!_formatOldOrNew()) { return false; } return _tryMount(); @@ -221,9 +219,8 @@ class SPIFFSImpl : public FSImpl if (_tryMount()) { SPIFFS_unmount(&_fs); } - auto rc = SPIFFS_format(&_fs); - if (rc != SPIFFS_OK) { - DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code); + + if (!_formatOldOrNew()) { return false; } @@ -253,7 +250,7 @@ class SPIFFSImpl : public FSImpl return &_fs; } - bool _tryMount() + spiffs_config _setupSpiffsConfig(bool metadata) { spiffs_config config; memset(&config, 0, sizeof(config)); @@ -261,12 +258,12 @@ class SPIFFSImpl : public FSImpl config.hal_read_f = &spiffs_hal_read; config.hal_write_f = &spiffs_hal_write; config.hal_erase_f = &spiffs_hal_erase; - config.phys_size = _size; - config.phys_addr = _start; + config.phys_size = metadata ? _size - _blockSize : _size; + config.phys_addr = metadata ? _start + _blockSize :_start; config.phys_erase_block = FLASH_SECTOR_SIZE; config.log_block_size = _blockSize; config.log_page_size = _pageSize; - + config.obj_meta_len = metadata ? 4 : 0; if (((uint32_t) std::numeric_limits::max()) < (_size / _blockSize)) { DEBUGV("spiffs_block_ix type too small"); @@ -288,6 +285,48 @@ class SPIFFSImpl : public FSImpl abort(); } + return config; + } + + bool _formatOldOrNew() { + // Make sure we format with requested metadata len + if (_cfg._enableTime) { + if (!_mark_metadata_fs(_start, _blockSize, _pageSize)) { + DEBUGV("SPIFFSImpl::_formatOldOrNew error, can't mark as metadata filesystem\n"); + return false; + } + } + // Make sure we format with requested metadata len + spiffs_config config = _setupSpiffsConfig(_cfg._enableTime); + DEBUGV("SPIFFSImpl::_formatOldOrNew formatting with metadata==%d\r\n", config.obj_meta_len); + + // We need to try a mount on SPIFFS, even though it will probably fail, to make the opaque + // SPIFFS _fs struct take the new sizes specified in the configuration. + size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&_fs, _maxOpenFds); + size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&_fs, _maxOpenFds); + auto err = SPIFFS_mount(&_fs, &config, _workBuf.get(), + _fdsBuf.get(), fdsBufSize, _cacheBuf.get(), cacheBufSize, + &SPIFFSImpl::_check_cb); + if (err == SPIFFS_OK) { + SPIFFS_unmount(&_fs); + } + auto rc = SPIFFS_format(&_fs); + if (rc != SPIFFS_OK) { + DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code); + return false; + } + if (_cfg._enableTime && !_is_metadata_fs(_start, _blockSize, _pageSize)) { + DEBUGV("SPIFFSImpl::_formatOldOrNew lost metadata flag somehow\n"); + return false; + } + return true; + } + + + bool _tryMount() + { + spiffs_config config = _setupSpiffsConfig(false); + // hack: even though fs is not initialized at this point, // SPIFFS_buffer_bytes_for_cache uses only fs->config.log_page_size // suggestion: change SPIFFS_buffer_bytes_for_cache to take @@ -307,18 +346,74 @@ class SPIFFSImpl : public FSImpl _cacheBuf.reset(new uint8_t[cacheBufSize]); } - DEBUGV("SPIFFSImpl: mounting fs @%x, size=%x, block=%x, page=%x\r\n", - _start, _size, _blockSize, _pageSize); - - auto err = SPIFFS_mount(&_fs, &config, _workBuf.get(), + // First, can we mount w/o metadata (preserve backwards) + int err; + if ( !_is_metadata_fs(_start, _blockSize, _pageSize) ) { + DEBUGV("SPIFFSImpl: trying fs @%x, size=%x, block=%x, page=%x, metadata=%d\r\n", + _start, _size, _blockSize, _pageSize, _fs.cfg.obj_meta_len); + err = SPIFFS_mount(&_fs, &config, _workBuf.get(), + _fdsBuf.get(), fdsBufSize, _cacheBuf.get(), cacheBufSize, + &SPIFFSImpl::_check_cb); + } else { + // Flag matched, it's a metadata FS + config = _setupSpiffsConfig(true); + DEBUGV("SPIFFSImpl: doesn't look like old metadata==0, so trying fs @%x, size=%x, block=%x, page=%x, metadata=%d\r\n", + _start, _size, _blockSize, _pageSize, _fs.cfg.obj_meta_len); + err = SPIFFS_mount(&_fs, &config, _workBuf.get(), _fdsBuf.get(), fdsBufSize, _cacheBuf.get(), cacheBufSize, &SPIFFSImpl::_check_cb); + } DEBUGV("SPIFFSImpl: mount rc=%d\r\n", err); return err == SPIFFS_OK; } + // The value that's repeated through the entire first blockSize bytes of a timestamp FS + enum { __SPIFFS_MD_FS__ = 0xc0de0004 }; + + // Check the first blockSize bytes, and if they match the flag it's a timestamp FS + static bool _is_metadata_fs(uint32_t _start, uint32_t _blockSize, uint32_t _pageSize) + { + char buff[_pageSize]; + for (uint32_t page = 0; page < _blockSize / _pageSize; page++) { + uint32_t addr = _start + page * _pageSize; + auto ret = spiffs_hal_read(addr, _pageSize, (uint8_t*)buff); + if (ret != SPIFFS_OK) { + return false; + } + for (uint32_t *ptr=reinterpret_cast(buff), cnt=0; cnt < sizeof(buff)/sizeof(uint32_t); cnt++, ptr++) { + if (*ptr != __SPIFFS_MD_FS__) { + return false; + } + } + } + DEBUGV("SPIFFSImpl: metadata FS\n"); + return true; + } + + // Fills the first block with the timestamp flag + static bool _mark_metadata_fs(uint32_t _start, uint32_t _blockSize, uint32_t _pageSize) + { + char buff[_pageSize]; + for (uint32_t *ptr=reinterpret_cast(buff), cnt=0; cnt < sizeof(buff)/sizeof(uint32_t); cnt++, ptr++) { + *ptr = __SPIFFS_MD_FS__; + } + auto ret = spiffs_hal_erase(_start, _blockSize); + if (ret != SPIFFS_OK) { + return false; + } + for (uint32_t page = 0; page < _blockSize / _pageSize; page++) { + uint32_t addr = _start + page * _pageSize; + ret = spiffs_hal_write(addr, _pageSize, (uint8_t *)buff); + if (ret != SPIFFS_OK) { + return false; + } + } + DEBUGV("SPIFFSImpl: set metadata FS flag\n"); + return true; + } + static void _check_cb(spiffs_check_type type, spiffs_check_report report, uint32_t arg1, uint32_t arg2) { @@ -495,6 +590,18 @@ class SPIFFSFileImpl : public FileImpl return name(); // No dirs, they're the same on SPIFFS } + time_t getLastWrite() override + { + CHECKFD(); + time_t t = 0; + if (_fs->getFs()->cfg.obj_meta_len) { + _getStat() ; + memcpy(&t, _stat.meta, sizeof(time_t)); + } + return t; + } + + protected: void _getStat() const { @@ -564,6 +671,14 @@ class SPIFFSDirImpl : public DirImpl return _dirent.size; } + time_t fileTime() override { + if (!_valid) { + return 0; + } + // TODO - When SPIFFS supports time metadata, return it + return 0; + } + bool isFile() const override { // No such thing as directories on SPIFFS diff --git a/doc/filesystem.rst b/doc/filesystem.rst index fecd840742..f4417218a3 100644 --- a/doc/filesystem.rst +++ b/doc/filesystem.rst @@ -181,6 +181,31 @@ in the specific subdirectory. This mimics the POSIX behavior for directory traversal most C programmers are used to. +SPIFFS on-flash versions +------------------------ + +There are two on-flash formats for SPIFFS. The original one has no +metadata support (SPIFFS_OBJ_META_LEN==0), while the current one supports +a timestamp (SPIFFS_OBJ_META_LEN==4). + +Because the on-flash SPIFFS layout changes, depending on whether or not +metadata is present, the two formats are incompatible. + +To ensure backwards compatibility, a unique layout on-flash is defined for +the new version, with the first page of flash set to 0xc0de0004. + +When SPIFFS is attempted to be mounted the core first checks the first +page of the flash filesystem. If it is not 0xc0de0004 filled, then +it immediately attempts to mount using the non-timestamped version. +Only if the first page is all 0xc0de0004 will it attempt to mount with +timestamps. + +When formatting a new filesystem, by default it builds a timestamp +filesystem, but this can be overridden using +``SPIFFS.setConfig(SPIFFSConfig().setEnableTime(true));`` before any +``SPIFFS.begin()`` or ``SPIFFS.format()``. + + Uploading files to file system ------------------------------ @@ -478,8 +503,8 @@ setTimeCallback(time_t (*cb)(void)) } -The SD, SDFS, and LittleFS filesystems support a file timestamp, updated when the file is -opened for writing. By default, the ESP8266 will use the internal time returned from +The SD, SDFS, SPIFFS, and LittleFS filesystems support a file timestamp, updated when the +file is opened for writing. By default, the ESP8266 will use the internal time returned from ``time(NULL)`` (i.e. local time, not UTC, to conform to the existing FAT filesystem), but this can be overridden to GMT or any other standard you'd like by using ``setTimeCallback()``. If your app sets the system time using NTP before file operations, then diff --git a/libraries/esp8266/examples/SPIFFS_Time/SPIFFS_Time.ino b/libraries/esp8266/examples/SPIFFS_Time/SPIFFS_Time.ino new file mode 100644 index 0000000000..ed093ae10c --- /dev/null +++ b/libraries/esp8266/examples/SPIFFS_Time/SPIFFS_Time.ino @@ -0,0 +1,214 @@ +#include +#include +#include + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char *ssid = STASSID; +const char *pass = STAPSK; + +long timezone = 2; +byte daysavetime = 1; + + +bool getLocalTime(struct tm * info, uint32_t ms) { + uint32_t count = ms / 10; + time_t now; + + time(&now); + localtime_r(&now, info); + + if (info->tm_year > (2016 - 1900)) { + return true; + } + + while (count--) { + delay(10); + time(&now); + localtime_r(&now, info); + if (info->tm_year > (2016 - 1900)) { + return true; + } + } + return false; +} + + +void listDir(const char * dirname) { + Serial.printf("Listing directory: %s\n", dirname); + + Dir root = SPIFFS.openDir(dirname); + + while (root.next()) { + File file = root.openFile("r"); + Serial.print(" FILE: "); + Serial.print(root.fileName()); + Serial.print(" SIZE: "); + Serial.print(file.size()); + time_t t = file.getLastWrite(); + struct tm * tmstruct = localtime(&t); + file.close(); + Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec); + } +} + + +void readFile(const char * path) { + Serial.printf("Reading file: %s\n", path); + + File file = SPIFFS.open(path, "r"); + if (!file) { + Serial.println("Failed to open file for reading"); + return; + } + + Serial.print("Read from file: "); + while (file.available()) { + Serial.write(file.read()); + } + file.close(); +} + +void writeFile(const char * path, const char * message) { + Serial.printf("Writing file: %s\n", path); + + File file = SPIFFS.open(path, "w"); + if (!file) { + Serial.println("Failed to open file for writing"); + return; + } + if (file.print(message)) { + Serial.println("File written"); + } else { + Serial.println("Write failed"); + } + file.close(); +} + +void appendFile(const char * path, const char * message) { + Serial.printf("Appending to file: %s\n", path); + + File file = SPIFFS.open(path, "a"); + if (!file) { + Serial.println("Failed to open file for appending"); + return; + } + if (file.print(message)) { + Serial.println("Message appended"); + } else { + Serial.println("Append failed"); + } + file.close(); +} + +void renameFile(const char * path1, const char * path2) { + Serial.printf("Renaming file %s to %s\n", path1, path2); + if (SPIFFS.rename(path1, path2)) { + Serial.println("File renamed"); + } else { + Serial.println("Rename failed"); + } +} + +void deleteFile(const char * path) { + Serial.printf("Deleting file: %s\n", path); + if (SPIFFS.remove(path)) { + Serial.println("File deleted"); + } else { + Serial.println("Delete failed"); + } +} + +void setup() { + Serial.begin(115200); + // We start by connecting to a WiFi network + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, pass); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println("Contacting Time Server"); + configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); + struct tm tmstruct ; + delay(2000); + tmstruct.tm_year = 0; + getLocalTime(&tmstruct, 5000); + Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec); + Serial.println(""); + + Serial.printf("First, format an old-style filesystem without metadata\n"); + SPIFFS.setConfig(SPIFFSConfig(false, false)); + SPIFFS.format(); + SPIFFS.end(); + Serial.printf("Now mount it\n"); + if (!SPIFFS.begin()) { + Serial.println("SPIFFS mount failed"); + return; + } + + listDir("/"); + deleteFile("/hello.txt"); + writeFile("/hello.txt", "Hello "); + appendFile("/hello.txt", "World!\n"); + listDir("/"); + + Serial.printf("The timestamp should be invalid above since the filesystem doesn't have metadata\n"); + + Serial.printf("Now unmount and remount and perform the same operation.\n"); + Serial.printf("Timestamp should be invalid, but data should be good.\n"); + SPIFFS.end(); + Serial.printf("Now mount it\n"); + if (!SPIFFS.begin()) { + Serial.println("SPIFFS mount failed"); + return; + } + readFile("/hello.txt"); + listDir("/"); + + Serial.printf("Now format a new SPIFFS FS with timestamp support\n"); + + SPIFFS.end(); + SPIFFS.setConfig(SPIFFSConfig().setEnableTime(true)); + SPIFFS.format(); + SPIFFS.end(); + Serial.printf("Now mount it\n"); + if (!SPIFFS.begin()) { + Serial.println("SPIFFS mount failed"); + return; + } + + listDir("/"); + deleteFile("/hello.txt"); + writeFile("/hello.txt", "Hello "); + appendFile("/hello.txt", "World!\n"); + listDir("/"); + + Serial.printf("The timestamp should be valid above\n"); + + Serial.printf("Now unmount and remount and perform the same operation.\n"); + Serial.printf("Timestamp should be valid, data should be good.\n"); + SPIFFS.end(); + Serial.printf("Now mount it\n"); + if (!SPIFFS.begin()) { + Serial.println("SPIFFS mount failed"); + return; + } + readFile("/hello.txt"); + listDir("/"); + + +} + +void loop() { }