Commit a463d764 authored by Johny Mattsson's avatar Johny Mattsson
Browse files

WIP ESP32 IDF port.

Currently the UART driver break boot (or at least output).
parent b4f06819
#include "spiffs.h"
#include "spiffs_nucleus.h"
static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) {
s32_t res = SPIFFS_OK;
if (pix == (spiffs_page_ix)-1) {
// referring to page 0xffff...., bad object index
return SPIFFS_ERR_INDEX_REF_FREE;
}
if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
// referring to an object lookup page, bad object index
return SPIFFS_ERR_INDEX_REF_LU;
}
if (pix > SPIFFS_MAX_PAGES(fs)) {
// referring to a bad page
return SPIFFS_ERR_INDEX_REF_INVALID;
}
#if SPIFFS_PAGE_CHECK
spiffs_page_header ph;
res = _spiffs_rd(
fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
fd->file_nbr,
SPIFFS_PAGE_TO_PADDR(fs, pix),
sizeof(spiffs_page_header),
(u8_t *)&ph);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix);
#endif
return res;
}
static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) {
s32_t res = SPIFFS_OK;
if (pix == (spiffs_page_ix)-1) {
// referring to page 0xffff...., bad object index
return SPIFFS_ERR_INDEX_FREE;
}
if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
// referring to an object lookup page, bad object index
return SPIFFS_ERR_INDEX_LU;
}
if (pix > SPIFFS_MAX_PAGES(fs)) {
// referring to a bad page
return SPIFFS_ERR_INDEX_INVALID;
}
#if SPIFFS_PAGE_CHECK
spiffs_page_header ph;
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_header),
(u8_t *)&ph);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix);
#endif
return res;
}
#if !SPIFFS_CACHE
s32_t spiffs_phys_rd(
spiffs *fs,
u32_t addr,
u32_t len,
u8_t *dst) {
return fs->cfg.hal_read_f(addr, len, dst);
}
s32_t spiffs_phys_wr(
spiffs *fs,
u32_t addr,
u32_t len,
u8_t *src) {
return fs->cfg.hal_write_f(addr, len, src);
}
#endif
s32_t spiffs_phys_cpy(
spiffs *fs,
spiffs_file fh,
u32_t dst,
u32_t src,
u32_t len) {
s32_t res;
u8_t b[SPIFFS_COPY_BUFFER_STACK];
while (len > 0) {
u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b);
SPIFFS_CHECK_RES(res);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b);
SPIFFS_CHECK_RES(res);
len -= chunk_size;
src += chunk_size;
dst += chunk_size;
}
return SPIFFS_OK;
}
// Find object lookup entry containing given id with visitor.
// Iterate over object lookup pages in each block until a given object id entry is found.
// When found, the visitor function is called with block index, entry index and user_data.
// If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be
// ended and visitor's return code is returned to caller.
// If no visitor is given (0) the search returns on first entry with matching object id.
// If no match is found in all look up, SPIFFS_VIS_END is returned.
// @param fs the file system
// @param starting_block the starting block to start search in
// @param starting_lu_entry the look up index entry to start search in
// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH,
// SPIFFS_VIS_NO_WRAP
// @param obj_id argument object id
// @param v visitor callback function
// @param user_data any data, passed to the callback visitor function
// @param user_p any pointer, passed to the callback visitor function
// @param block_ix reported block index where match was found
// @param lu_entry reported look up index where match was found
s32_t spiffs_obj_lu_find_entry_visitor(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
u8_t flags,
spiffs_obj_id obj_id,
spiffs_visitor_f v,
u32_t user_data,
void *user_p,
spiffs_block_ix *block_ix,
int *lu_entry) {
s32_t res = SPIFFS_OK;
s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs);
spiffs_block_ix cur_block = starting_block;
u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs);
spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work;
int cur_entry = starting_lu_entry;
int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id));
// wrap initial
if (cur_entry >= (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) {
cur_entry = 0;
cur_block++;
cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs);
if (cur_block >= fs->block_count) {
if (flags & SPIFFS_VIS_NO_WRAP) {
return SPIFFS_VIS_END;
} else {
// block wrap
cur_block = 0;
cur_block_addr = 0;
}
}
}
// check each block
while (res == SPIFFS_OK && entry_count > 0) {
int obj_lookup_page = cur_entry / entries_per_page;
// check each object lookup page
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
int entry_offset = obj_lookup_page * entries_per_page;
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
// check each entry
while (res == SPIFFS_OK &&
cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages
cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) // for last obj lookup page
{
if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry-entry_offset] == obj_id) {
if (block_ix) *block_ix = cur_block;
if (lu_entry) *lu_entry = cur_entry;
if (v) {
res = v(
fs,
(flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry-entry_offset],
cur_block,
cur_entry,
user_data,
user_p);
if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) {
if (res == SPIFFS_VIS_COUNTINUE_RELOAD) {
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work);
SPIFFS_CHECK_RES(res);
}
res = SPIFFS_OK;
cur_entry++;
entry_count--;
continue;
} else {
return res;
}
} else {
return SPIFFS_OK;
}
}
entry_count--;
cur_entry++;
} // per entry
obj_lookup_page++;
} // per object lookup page
cur_entry = 0;
cur_block++;
cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs);
if (cur_block >= fs->block_count) {
if (flags & SPIFFS_VIS_NO_WRAP) {
return SPIFFS_VIS_END;
} else {
// block wrap
cur_block = 0;
cur_block_addr = 0;
}
}
} // per block
SPIFFS_CHECK_RES(res);
return SPIFFS_VIS_END;
}
s32_t spiffs_erase_block(
spiffs *fs,
spiffs_block_ix bix) {
s32_t res;
u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix);
s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs);
// here we ignore res, just try erasing the block
while (size > 0) {
SPIFFS_DBG("erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
(void)fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs);
size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs);
}
fs->free_blocks++;
// register erase count for this block
res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
SPIFFS_ERASE_COUNT_PADDR(fs, bix),
sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count);
SPIFFS_CHECK_RES(res);
#if SPIFFS_USE_MAGIC
// finally, write magic
spiffs_obj_id magic = SPIFFS_MAGIC(fs);
res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
SPIFFS_MAGIC_PADDR(fs, bix),
sizeof(spiffs_obj_id), (u8_t *)&magic);
SPIFFS_CHECK_RES(res);
#endif
fs->max_erase_count++;
if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) {
fs->max_erase_count = 0;
}
return res;
}
static s32_t spiffs_obj_lu_scan_v(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_block_ix bix,
int ix_entry,
u32_t user_data,
void *user_p) {
(void)bix;
(void)user_data;
(void)user_p;
if (obj_id == SPIFFS_OBJ_ID_FREE) {
if (ix_entry == 0) {
fs->free_blocks++;
// todo optimize further, return SPIFFS_NEXT_BLOCK
}
} else if (obj_id == SPIFFS_OBJ_ID_DELETED) {
fs->stats_p_deleted++;
} else {
fs->stats_p_allocated++;
}
return SPIFFS_VIS_COUNTINUE;
}
// Scans thru all obj lu and counts free, deleted and used pages
// Find the maximum block erase count
// Checks magic if enabled
s32_t spiffs_obj_lu_scan(
spiffs *fs) {
s32_t res;
spiffs_block_ix bix;
int entry;
#if SPIFFS_USE_MAGIC
spiffs_block_ix unerased_bix = (spiffs_block_ix)-1;
#endif
// find out erase count
// if enabled, check magic
bix = 0;
spiffs_obj_id erase_count_final;
spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE;
spiffs_obj_id erase_count_max = 0;
while (bix < fs->block_count) {
#if SPIFFS_USE_MAGIC
spiffs_obj_id magic;
res = _spiffs_rd(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_MAGIC_PADDR(fs, bix) ,
sizeof(spiffs_obj_id), (u8_t *)&magic);
SPIFFS_CHECK_RES(res);
if (magic != SPIFFS_MAGIC(fs)) {
if (unerased_bix == (spiffs_block_ix)-1) {
// allow one unerased block as it might be powered down during an erase
unerased_bix = bix;
} else {
// more than one unerased block, bail out
SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS);
}
}
#endif
spiffs_obj_id erase_count;
res = _spiffs_rd(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_ERASE_COUNT_PADDR(fs, bix) ,
sizeof(spiffs_obj_id), (u8_t *)&erase_count);
SPIFFS_CHECK_RES(res);
if (erase_count != SPIFFS_OBJ_ID_FREE) {
erase_count_min = MIN(erase_count_min, erase_count);
erase_count_max = MAX(erase_count_max, erase_count);
}
bix++;
}
if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) {
// clean system, set counter to zero
erase_count_final = 0;
} else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE)/2) {
// wrap, take min
erase_count_final = erase_count_min+1;
} else {
erase_count_final = erase_count_max+1;
}
fs->max_erase_count = erase_count_final;
#if SPIFFS_USE_MAGIC
if (unerased_bix != (spiffs_block_ix)-1) {
// found one unerased block, remedy
SPIFFS_DBG("mount: erase block %i\n", bix);
res = spiffs_erase_block(fs, unerased_bix);
SPIFFS_CHECK_RES(res);
}
#endif
// count blocks
fs->free_blocks = 0;
fs->stats_p_allocated = 0;
fs->stats_p_deleted = 0;
res = spiffs_obj_lu_find_entry_visitor(fs,
0,
0,
0,
0,
spiffs_obj_lu_scan_v,
0,
0,
&bix,
&entry);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_OK;
}
SPIFFS_CHECK_RES(res);
return res;
}
// Find free object lookup entry
// Iterate over object lookup pages in each block until a free object id entry is found
s32_t spiffs_obj_lu_find_free(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
spiffs_block_ix *block_ix,
int *lu_entry) {
s32_t res;
if (!fs->cleaning && fs->free_blocks < 2) {
res = spiffs_gc_quick(fs, 0);
if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) {
res = SPIFFS_OK;
}
SPIFFS_CHECK_RES(res);
if (fs->free_blocks < 2) {
return SPIFFS_ERR_FULL;
}
}
res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry,
SPIFFS_OBJ_ID_FREE, block_ix, lu_entry);
if (res == SPIFFS_OK) {
fs->free_cursor_block_ix = *block_ix;
fs->free_cursor_obj_lu_entry = *lu_entry;
if (*lu_entry == 0) {
fs->free_blocks--;
}
}
if (res == SPIFFS_VIS_END) {
SPIFFS_DBG("fs full\n");
}
return res == SPIFFS_VIS_END ? SPIFFS_ERR_FULL : res;
}
// Find object lookup entry containing given id
// Iterate over object lookup pages in each block until a given object id entry is found
s32_t spiffs_obj_lu_find_id(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
spiffs_obj_id obj_id,
spiffs_block_ix *block_ix,
int *lu_entry) {
s32_t res = spiffs_obj_lu_find_entry_visitor(
fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_ERR_NOT_FOUND;
}
return res;
}
static s32_t spiffs_obj_lu_find_id_and_span_v(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_block_ix bix,
int ix_entry,
u32_t user_data,
void *user_p) {
s32_t res;
spiffs_page_header ph;
spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry);
res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph);
SPIFFS_CHECK_RES(res);
if (ph.obj_id == obj_id &&
ph.span_ix == (spiffs_span_ix)user_data &&
(ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET &&
!((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) &&
(user_p == 0 || *((spiffs_page_ix *)user_p) != pix)) {
return SPIFFS_OK;
} else {
return SPIFFS_VIS_COUNTINUE;
}
}
// Find object lookup entry containing given id and span index
// Iterate over object lookup pages in each block until a given object id entry is found
s32_t spiffs_obj_lu_find_id_and_span(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix exclusion_pix,
spiffs_page_ix *pix) {
s32_t res;
spiffs_block_ix bix;
int entry;
res = spiffs_obj_lu_find_entry_visitor(fs,
fs->cursor_block_ix,
fs->cursor_obj_lu_entry,
SPIFFS_VIS_CHECK_ID,
obj_id,
spiffs_obj_lu_find_id_and_span_v,
(u32_t)spix,
exclusion_pix ? &exclusion_pix : 0,
&bix,
&entry);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_ERR_NOT_FOUND;
}
SPIFFS_CHECK_RES(res);
if (pix) {
*pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
}
fs->cursor_block_ix = bix;
fs->cursor_obj_lu_entry = entry;
return res;
}
// Find object lookup entry containing given id and span index in page headers only
// Iterate over object lookup pages in each block until a given object id entry is found
s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix exclusion_pix,
spiffs_page_ix *pix) {
s32_t res;
spiffs_block_ix bix;
int entry;
res = spiffs_obj_lu_find_entry_visitor(fs,
fs->cursor_block_ix,
fs->cursor_obj_lu_entry,
SPIFFS_VIS_CHECK_PH,
obj_id,
spiffs_obj_lu_find_id_and_span_v,
(u32_t)spix,
exclusion_pix ? &exclusion_pix : 0,
&bix,
&entry);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_ERR_NOT_FOUND;
}
SPIFFS_CHECK_RES(res);
if (pix) {
*pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
}
fs->cursor_block_ix = bix;
fs->cursor_obj_lu_entry = entry;
return res;
}
// Allocates a free defined page with given obj_id
// Occupies object lookup entry and page
// data may be NULL; where only page header is stored, len and page_offs is ignored
s32_t spiffs_page_allocate_data(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_page_header *ph,
u8_t *data,
u32_t len,
u32_t page_offs,
u8_t finalize,
spiffs_page_ix *pix) {
s32_t res = SPIFFS_OK;
spiffs_block_ix bix;
int entry;
// find free entry
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
SPIFFS_CHECK_RES(res);
// occupy page in object lookup
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id);
SPIFFS_CHECK_RES(res);
fs->stats_p_allocated++;
// write page header
ph->flags &= ~SPIFFS_PH_FLAG_USED;
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_header), (u8_t*)ph);
SPIFFS_CHECK_RES(res);
// write page data
if (data) {
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_header) + page_offs, len, data);
SPIFFS_CHECK_RES(res);
}
// finalize header if necessary
if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) {
ph->flags &= ~SPIFFS_PH_FLAG_FINAL;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&ph->flags);
SPIFFS_CHECK_RES(res);
}
// return written page
if (pix) {
*pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
}
return res;
}
// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page.
// If page data is null, provided header is used for metainfo and page data is physically copied.
s32_t spiffs_page_move(
spiffs *fs,
spiffs_file fh,
u8_t *page_data,
spiffs_obj_id obj_id,
spiffs_page_header *page_hdr,
spiffs_page_ix src_pix,
spiffs_page_ix *dst_pix) {
s32_t res;
u8_t was_final = 0;
spiffs_page_header *p_hdr;
spiffs_block_ix bix;
int entry;
spiffs_page_ix free_pix;
// find free entry
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
SPIFFS_CHECK_RES(res);
free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
if (dst_pix) *dst_pix = free_pix;
p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr;
if (page_data) {
// got page data
was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0;
// write unfinalized page
p_hdr->flags |= SPIFFS_PH_FLAG_FINAL;
p_hdr->flags &= ~SPIFFS_PH_FLAG_USED;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data);
} else {
// copy page data
res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs));
}
SPIFFS_CHECK_RES(res);
// mark entry in destination object lookup
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix),
sizeof(spiffs_obj_id),
(u8_t *)&obj_id);
SPIFFS_CHECK_RES(res);
fs->stats_p_allocated++;
if (was_final) {
// mark finalized in destination page
p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fh,
SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&p_hdr->flags);
SPIFFS_CHECK_RES(res);
}
// mark source deleted
res = spiffs_page_delete(fs, src_pix);
return res;
}
// Deletes a page and removes it from object lookup.
s32_t spiffs_page_delete(
spiffs *fs,
spiffs_page_ix pix) {
s32_t res;
spiffs_page_header hdr;
hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED);
// mark deleted entry in source object lookup
spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE,
0,
SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix),
sizeof(spiffs_obj_id),
(u8_t *)&d_obj_id);
SPIFFS_CHECK_RES(res);
fs->stats_p_deleted++;
fs->stats_p_allocated--;
// mark deleted in source page
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE,
0,
SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&hdr.flags);
return res;
}
// Create an object index header page with empty index and undefined length
s32_t spiffs_object_create(
spiffs *fs,
spiffs_obj_id obj_id,
u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_obj_type type,
spiffs_page_ix *objix_hdr_pix) {
s32_t res = SPIFFS_OK;
spiffs_block_ix bix;
spiffs_page_object_ix_header oix_hdr;
int entry;
res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res);
obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
// find free entry
res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry);
SPIFFS_CHECK_RES(res);
SPIFFS_DBG("create: found free page @ %04x bix:%i entry:%i\n", SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry);
// occupy page in object lookup
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t*)&obj_id);
SPIFFS_CHECK_RES(res);
fs->stats_p_allocated++;
// write empty object index page
oix_hdr.p_hdr.obj_id = obj_id;
oix_hdr.p_hdr.span_ix = 0;
oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED);
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, (char *)name, SPIFFS_OBJ_NAME_LEN);
// 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);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, 0, SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN);
if (objix_hdr_pix) {
*objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
}
return res;
}
// update object index header with any combination of name/size/index
// new_objix_hdr_data may be null, if so the object index header page is loaded
// name may be null, if so name is not changed
// size may be null, if so size is not changed
s32_t spiffs_object_update_index_hdr(
spiffs *fs,
spiffs_fd *fd,
spiffs_obj_id obj_id,
spiffs_page_ix objix_hdr_pix,
u8_t *new_objix_hdr_data,
u8_t name[SPIFFS_OBJ_NAME_LEN],
u32_t size,
spiffs_page_ix *new_pix) {
s32_t res = SPIFFS_OK;
spiffs_page_object_ix_header *objix_hdr;
spiffs_page_ix new_objix_hdr_pix;
obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
if (new_objix_hdr_data) {
// object index header page already given to us, no need to load it
objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data;
} else {
// read object index header page
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
objix_hdr = (spiffs_page_object_ix_header *)fs->work;
}
SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0);
// change name
if (name) {
strncpy((char *)objix_hdr->name, (char *)name, SPIFFS_OBJ_NAME_LEN);
}
if (size) {
objix_hdr->size = size;
}
// move and update page
res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t*)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix);
if (res == SPIFFS_OK) {
if (new_pix) {
*new_pix = new_objix_hdr_pix;
}
// callback on object index update
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size);
if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster
}
return res;
}
void spiffs_cb_object_event(
spiffs *fs,
spiffs_fd *fd,
int ev,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix new_pix,
u32_t new_size) {
(void)fd;
// update index caches in all file descriptors
obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG;
u32_t i;
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
for (i = 0; i < fs->fd_count; i++) {
spiffs_fd *cur_fd = &fds[i];
if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue;
if (spix == 0) {
if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) {
SPIFFS_DBG(" callback: setting fd %i:%04x objix_hdr_pix to %04x, size:%i\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size);
cur_fd->objix_hdr_pix = new_pix;
if (new_size != 0) {
cur_fd->size = new_size;
}
} else if (ev == SPIFFS_EV_IX_DEL) {
cur_fd->file_nbr = 0;
cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED;
}
}
if (cur_fd->cursor_objix_spix == spix) {
if (ev == SPIFFS_EV_IX_NEW || ev == SPIFFS_EV_IX_UPD) {
SPIFFS_DBG(" callback: setting fd %i:%04x span:%04x objix_pix to %04x\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix);
cur_fd->cursor_objix_pix = new_pix;
} else {
cur_fd->cursor_objix_pix = 0;
}
}
}
}
// Open object by id
s32_t spiffs_object_open_by_id(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_fd *fd,
spiffs_flags flags,
spiffs_mode mode) {
s32_t res = SPIFFS_OK;
spiffs_page_ix pix;
res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix);
SPIFFS_CHECK_RES(res);
res = spiffs_object_open_by_page(fs, pix, fd, flags, mode);
return res;
}
// Open object by page index
s32_t spiffs_object_open_by_page(
spiffs *fs,
spiffs_page_ix pix,
spiffs_fd *fd,
spiffs_flags flags,
spiffs_mode mode) {
(void)mode;
s32_t res = SPIFFS_OK;
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);
SPIFFS_CHECK_RES(res);
spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix);
int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id);
fd->fs = fs;
fd->objix_hdr_pix = pix;
fd->size = oix_hdr.size;
fd->offset = 0;
fd->cursor_objix_pix = pix;
fd->cursor_objix_spix = 0;
fd->obj_id = obj_id;
fd->flags = flags;
SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0);
SPIFFS_DBG("open: fd %i is obj id %04x\n", fd->file_nbr, fd->obj_id);
return res;
}
// Append to object
// keep current object index (header) page in fs->work buffer
s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
spiffs *fs = fd->fs;
s32_t res = SPIFFS_OK;
u32_t written = 0;
SPIFFS_DBG("append: %i bytes @ offs %i of size %i\n", len, offset, fd->size);
if (offset > fd->size) {
SPIFFS_DBG("append: offset reversed to size\n");
offset = fd->size;
}
res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta
if (res != SPIFFS_OK) {
SPIFFS_DBG("append: gc check fail %i\n", res);
}
SPIFFS_CHECK_RES(res);
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
spiffs_page_header p_hdr;
spiffs_span_ix cur_objix_spix = 0;
spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix;
spiffs_page_ix new_objix_hdr_page;
spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
spiffs_page_ix data_page;
u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs);
// write all data
while (res == SPIFFS_OK && written < len) {
// calculate object index page span index
cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
// handle storing and loading of object indices
if (cur_objix_spix != prev_objix_spix) {
// new object index page
// within this clause we return directly if something fails, object index mess-up
if (written > 0) {
// store previous object index page, unless first pass
SPIFFS_DBG("append: %04x store objix %04x:%04x, written %i\n", fd->obj_id,
cur_objix_pix, prev_objix_spix, written);
if (prev_objix_spix == 0) {
// this is an update to object index header page
objix_hdr->size = offset+written;
if (offset == 0) {
// was an empty object, update same page (size was 0xffffffff)
res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0);
SPIFFS_CHECK_RES(res);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
} else {
// was a nonempty object, update to new page
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page);
SPIFFS_CHECK_RES(res);
SPIFFS_DBG("append: %04x store new objix_hdr, %04x:%04x, written %i\n", fd->obj_id,
new_objix_hdr_page, 0, written);
}
} else {
// this is an update to an object index page
res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix);
SPIFFS_CHECK_RES(res);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD,fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0);
// update length in object index header page
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page);
SPIFFS_CHECK_RES(res);
SPIFFS_DBG("append: %04x store new size I %i in objix_hdr, %04x:%04x, written %i\n", fd->obj_id,
offset+written, new_objix_hdr_page, 0, written);
}
fd->size = offset+written;
fd->offset = offset+written;
}
// create or load new object index page
if (cur_objix_spix == 0) {
// load object index header page, must always exist
SPIFFS_DBG("append: %04x load objixhdr page %04x:%04x\n", fd->obj_id, cur_objix_pix, cur_objix_spix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
} else {
spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size-1)/SPIFFS_DATA_PAGE_SIZE(fs));
// on subsequent passes, create a new object index page
if (written > 0 || cur_objix_spix > len_objix_spix) {
p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG;
p_hdr.span_ix = cur_objix_spix;
p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG,
&p_hdr, 0, 0, 0, 1, &cur_objix_pix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0);
// quick "load" of new object index page
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header));
SPIFFS_DBG("append: %04x create objix page, %04x:%04x, written %i\n", fd->obj_id
, cur_objix_pix, cur_objix_spix, written);
} else {
// on first pass, we load existing object index page
spiffs_page_ix pix;
SPIFFS_DBG("append: %04x find objix span_ix:%04x\n", fd->obj_id, cur_objix_spix);
if (fd->cursor_objix_spix == cur_objix_spix) {
pix = fd->cursor_objix_pix;
} else {
res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix);
SPIFFS_CHECK_RES(res);
}
SPIFFS_DBG("append: %04x found object index at page %04x [fd size %i]\n", fd->obj_id, pix, fd->size);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
cur_objix_pix = pix;
}
fd->cursor_objix_pix = cur_objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
fd->offset = offset+written;
fd->size = offset+written;
}
prev_objix_spix = cur_objix_spix;
}
// write data
u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
if (page_offs == 0) {
// at beginning of a page, allocate and write a new page of data
p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
p_hdr.span_ix = data_spix;
p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately
res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
&p_hdr, &data[written], to_write, page_offs, 1, &data_page);
SPIFFS_DBG("append: %04x store new data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id,
data_page, data_spix, page_offs, to_write, written);
} else {
// 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];
} 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)];
}
res = spiffs_page_data_check(fs, fd, data_page, data_spix);
SPIFFS_CHECK_RES(res);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]);
SPIFFS_DBG("append: %04x store to existing data page, %04x:%04x offset:%i, len %i, written %i\n", fd->obj_id
, data_page, data_spix, page_offs, to_write, written);
}
if (res != SPIFFS_OK) break;
// 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_DBG("append: %04x wrote page %04x to objix_hdr entry %02x in mem\n", fd->obj_id
, data_page, data_spix);
objix_hdr->size = offset+written;
} else {
// update object index page
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page;
SPIFFS_DBG("append: %04x wrote page %04x to objix entry %02x in mem\n", fd->obj_id
, data_page, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
}
// update internals
page_offs = 0;
data_spix++;
written += to_write;
} // while all data
fd->size = offset+written;
fd->offset = offset+written;
fd->cursor_objix_pix = cur_objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
// finalize updated object indices
s32_t res2 = SPIFFS_OK;
if (cur_objix_spix != 0) {
// wrote beyond object index header page
// write last modified object index page, unless object header index page
SPIFFS_DBG("append: %04x store objix page, %04x:%04x, written %i\n", fd->obj_id,
cur_objix_pix, cur_objix_spix, written);
res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix);
SPIFFS_CHECK_RES(res2);
res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res2);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0);
// update size in object header index page
res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page);
SPIFFS_DBG("append: %04x store new size II %i in objix_hdr, %04x:%04x, written %i, res %i\n", fd->obj_id
, offset+written, new_objix_hdr_page, 0, written, res2);
SPIFFS_CHECK_RES(res2);
} else {
// wrote within object index header page
if (offset == 0) {
// wrote to empty object - simply update size and write whole page
objix_hdr->size = offset+written;
SPIFFS_DBG("append: %04x store fresh objix_hdr page, %04x:%04x, written %i\n", fd->obj_id
, cur_objix_pix, cur_objix_spix, written);
res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix);
SPIFFS_CHECK_RES(res2);
res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res2);
// callback on object index update
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size);
} else {
// modifying object index header page, update size and make new copy
res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, fs->work, 0, offset+written, &new_objix_hdr_page);
SPIFFS_DBG("append: %04x store modified objix_hdr page, %04x:%04x, written %i\n", fd->obj_id
, new_objix_hdr_page, 0, written);
SPIFFS_CHECK_RES(res2);
}
}
return res;
}
// Modify object
// keep current object index (header) page in fs->work buffer
s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
spiffs *fs = fd->fs;
s32_t res = SPIFFS_OK;
u32_t written = 0;
res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res);
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
spiffs_page_header p_hdr;
spiffs_span_ix cur_objix_spix = 0;
spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix;
spiffs_page_ix new_objix_hdr_pix;
spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
spiffs_page_ix data_pix;
u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs);
// write all data
while (res == SPIFFS_OK && written < len) {
// calculate object index page span index
cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
// handle storing and loading of object indices
if (cur_objix_spix != prev_objix_spix) {
// new object index page
// within this clause we return directly if something fails, object index mess-up
if (written > 0) {
// store previous object index (header) page, unless first pass
if (prev_objix_spix == 0) {
// store previous object index header page
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix);
SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written);
SPIFFS_CHECK_RES(res);
} else {
// store new version of previous object index page
spiffs_page_ix new_objix_pix;
res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix);
SPIFFS_CHECK_RES(res);
res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix);
SPIFFS_DBG("modify: store previous modified objix page, %04x:%04x, written %i\n", new_objix_pix, objix->p_hdr.span_ix, written);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
}
}
// load next object index page
if (cur_objix_spix == 0) {
// load object index header page, must exist
SPIFFS_DBG("modify: load objixhdr page %04x:%04x\n", cur_objix_pix, cur_objix_spix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
} else {
// load existing object index page on first pass
spiffs_page_ix pix;
SPIFFS_DBG("modify: find objix span_ix:%04x\n", cur_objix_spix);
if (fd->cursor_objix_spix == cur_objix_spix) {
pix = fd->cursor_objix_pix;
} else {
res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix);
SPIFFS_CHECK_RES(res);
}
SPIFFS_DBG("modify: found object index at page %04x\n", pix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
cur_objix_pix = pix;
}
fd->cursor_objix_pix = cur_objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
fd->offset = offset+written;
prev_objix_spix = cur_objix_spix;
}
// write partial data
u32_t to_write = MIN(len-written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
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];
} 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)];
}
p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
p_hdr.span_ix = data_spix;
p_hdr.flags = 0xff;
if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) {
// a full page, allocate and write a new page of data
res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
&p_hdr, &data[written], to_write, page_offs, 1, &data_pix);
SPIFFS_DBG("modify: store new data page, %04x:%04x offset:%i, len %i, written %i\n", data_pix, data_spix, page_offs, to_write, written);
} else {
// write to existing page, allocate new and copy unmodified data
res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix);
SPIFFS_CHECK_RES(res);
res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
&p_hdr, 0, 0, 0, 0, &data_pix);
if (res != SPIFFS_OK) break;
// copy unmodified data
if (page_offs > 0) {
// before modification
res = spiffs_phys_cpy(fs, fd->file_nbr,
SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header),
SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header),
page_offs);
if (res != SPIFFS_OK) break;
}
if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) {
// after modification
res = spiffs_phys_cpy(fs, fd->file_nbr,
SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write,
SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write,
SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write));
if (res != SPIFFS_OK) break;
}
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fd->file_nbr,
SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]);
if (res != SPIFFS_OK) break;
p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fd->file_nbr,
SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&p_hdr.flags);
if (res != SPIFFS_OK) break;
SPIFFS_DBG("modify: store to existing data page, src:%04x, dst:%04x:%04x offset:%i, len %i, written %i\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written);
}
// delete original data page
res = spiffs_page_delete(fs, orig_data_pix);
if (res != SPIFFS_OK) break;
// 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_DBG("modify: wrote page %04x to objix_hdr entry %02x in mem\n", data_pix, data_spix);
} else {
// update object index page
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix;
SPIFFS_DBG("modify: wrote page %04x to objix entry %02x in mem\n", data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
}
// update internals
page_offs = 0;
data_spix++;
written += to_write;
} // while all data
fd->offset = offset+written;
fd->cursor_objix_pix = cur_objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
// finalize updated object indices
s32_t res2 = SPIFFS_OK;
if (cur_objix_spix != 0) {
// wrote beyond object index header page
// write last modified object index page
// move and update page
spiffs_page_ix new_objix_pix;
res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix);
SPIFFS_CHECK_RES(res2);
res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix);
SPIFFS_DBG("modify: store modified objix page, %04x:%04x, written %i\n", new_objix_pix, cur_objix_spix, written);
fd->cursor_objix_pix = new_objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
SPIFFS_CHECK_RES(res2);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
} else {
// wrote within object index header page
res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, fs->work, 0, 0, &new_objix_hdr_pix);
SPIFFS_DBG("modify: store modified objix_hdr page, %04x:%04x, written %i\n", new_objix_hdr_pix, 0, written);
SPIFFS_CHECK_RES(res2);
}
return res;
}
static s32_t spiffs_object_find_object_index_header_by_name_v(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_block_ix bix,
int ix_entry,
u32_t user_data,
void *user_p) {
(void)user_data;
s32_t res;
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);
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)) ==
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
if (strncmp((char *)user_p, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN) == 0) {
return SPIFFS_OK;
}
}
return SPIFFS_VIS_COUNTINUE;
}
// Finds object index header page by name
s32_t spiffs_object_find_object_index_header_by_name(
spiffs *fs,
u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_page_ix *pix) {
s32_t res;
spiffs_block_ix bix;
int entry;
res = spiffs_obj_lu_find_entry_visitor(fs,
fs->cursor_block_ix,
fs->cursor_obj_lu_entry,
0,
0,
spiffs_object_find_object_index_header_by_name_v,
0,
name,
&bix,
&entry);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_ERR_NOT_FOUND;
}
SPIFFS_CHECK_RES(res);
if (pix) {
*pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry);
}
fs->cursor_block_ix = bix;
fs->cursor_obj_lu_entry = entry;
return res;
}
// Truncates object to new size. If new size is null, object may be removed totally
s32_t spiffs_object_truncate(
spiffs_fd *fd,
u32_t new_size,
u8_t remove) {
s32_t res = SPIFFS_OK;
spiffs *fs = fd->fs;
res = spiffs_gc_check(fs, remove ? 0 : SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res);
spiffs_page_ix objix_pix = fd->objix_hdr_pix;
spiffs_span_ix data_spix = (fd->size > 0 ? fd->size-1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs);
u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ;
spiffs_span_ix cur_objix_spix = 0;
spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
spiffs_page_ix data_pix;
spiffs_page_ix new_objix_hdr_pix;
// before truncating, check if object is to be fully removed and mark this
if (remove && new_size == 0) {
u8_t flags = ~( SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&flags);
SPIFFS_CHECK_RES(res);
}
// delete from end of object until desired len is reached
while (cur_size > new_size) {
cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
// put object index for current data span index in work buffer
if (prev_objix_spix != cur_objix_spix) {
if (prev_objix_spix != (spiffs_span_ix)-1) {
// remove previous object index page
SPIFFS_DBG("truncate: delete objix page %04x:%04x\n", objix_pix, prev_objix_spix);
res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix);
SPIFFS_CHECK_RES(res);
res = spiffs_page_delete(fs, objix_pix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0);
if (prev_objix_spix > 0) {
// update object index header page
SPIFFS_DBG("truncate: update objix hdr page %04x:%04x to size %i\n", fd->objix_hdr_pix, prev_objix_spix, cur_size);
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix);
SPIFFS_CHECK_RES(res);
fd->size = cur_size;
}
}
// load current object index (header) page
if (cur_objix_spix == 0) {
objix_pix = fd->objix_hdr_pix;
} else {
res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix);
SPIFFS_CHECK_RES(res);
}
SPIFFS_DBG("truncate: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix);
fd->cursor_objix_pix = objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
fd->offset = cur_size;
prev_objix_spix = cur_objix_spix;
}
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;
} 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)];
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE;
}
SPIFFS_DBG("truncate: got data pix %04x\n", data_pix);
if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) {
// delete full data page
res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) {
SPIFFS_DBG("truncate: err validating data pix %i\n", res);
break;
}
if (res == SPIFFS_OK) {
res = spiffs_page_delete(fs, data_pix);
if (res != SPIFFS_OK) {
SPIFFS_DBG("truncate: err deleting data pix %i\n", res);
break;
}
} else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) {
res = SPIFFS_OK;
}
// update current size
if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) {
cur_size -= SPIFFS_DATA_PAGE_SIZE(fs);
} else {
cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs);
}
fd->size = cur_size;
fd->offset = cur_size;
SPIFFS_DBG("truncate: delete data page %04x for data spix:%04x, cur_size:%i\n", data_pix, data_spix, cur_size);
} else {
// delete last page, partially
spiffs_page_header p_hdr;
spiffs_page_ix new_data_pix;
u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_DBG("truncate: delete %i bytes from data page %04x for data spix:%04x, cur_size:%i\n", bytes_to_remove, data_pix, data_spix, cur_size);
res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
if (res != SPIFFS_OK) break;
p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG;
p_hdr.span_ix = data_spix;
p_hdr.flags = 0xff;
// allocate new page and copy unmodified data
res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG,
&p_hdr, 0, 0, 0, 0, &new_data_pix);
if (res != SPIFFS_OK) break;
res = spiffs_phys_cpy(fs, 0,
SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header),
SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header),
SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove);
if (res != SPIFFS_OK) break;
// delete original data page
res = spiffs_page_delete(fs, data_pix);
if (res != SPIFFS_OK) break;
p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fd->file_nbr,
SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&p_hdr.flags);
if (res != SPIFFS_OK) break;
// 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_DBG("truncate: wrote page %04x to objix_hdr entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
} else {
// update object index page
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix;
SPIFFS_DBG("truncate: wrote page %04x to objix entry %02x in mem\n", new_data_pix, SPIFFS_OBJ_IX_ENTRY(fs, data_spix));
}
cur_size = new_size;
fd->size = new_size;
fd->offset = cur_size;
break;
}
data_spix--;
} // while all data
// update object indices
if (cur_objix_spix == 0) {
// update object index header page
if (cur_size == 0) {
if (remove) {
// remove object altogether
SPIFFS_DBG("truncate: remove object index header page %04x\n", objix_pix);
res = spiffs_page_index_check(fs, fd, objix_pix, 0);
SPIFFS_CHECK_RES(res);
res = spiffs_page_delete(fs, objix_pix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0);
} else {
// make uninitialized object
SPIFFS_DBG("truncate: reset objix_hdr page %04x\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));
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
objix_pix, fs->work, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix);
SPIFFS_CHECK_RES(res);
}
} else {
// update object index header page
SPIFFS_DBG("truncate: update object index header page with indices and size\n");
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
objix_pix, fs->work, 0, cur_size, &new_objix_hdr_pix);
SPIFFS_CHECK_RES(res);
}
} else {
// update both current object index page and object index header page
spiffs_page_ix new_objix_pix;
res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix);
SPIFFS_CHECK_RES(res);
// move and update object index page
res = spiffs_page_move(fs, fd->file_nbr, (u8_t*)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix);
SPIFFS_CHECK_RES(res);
spiffs_cb_object_event(fs, fd, SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0);
SPIFFS_DBG("truncate: store modified objix page, %04x:%04x\n", new_objix_pix, cur_objix_spix);
fd->cursor_objix_pix = new_objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
fd->offset = cur_size;
// update object index header page with new size
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, 0, 0, cur_size, &new_objix_hdr_pix);
SPIFFS_CHECK_RES(res);
}
fd->size = cur_size;
return res;
}
s32_t spiffs_object_read(
spiffs_fd *fd,
u32_t offset,
u32_t len,
u8_t *dst) {
s32_t res = SPIFFS_OK;
spiffs *fs = fd->fs;
spiffs_page_ix objix_pix;
spiffs_page_ix data_pix;
spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs);
u32_t cur_offset = offset;
spiffs_span_ix cur_objix_spix;
spiffs_span_ix prev_objix_spix = (spiffs_span_ix)-1;
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work;
while (cur_offset < offset + len) {
cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
if (prev_objix_spix != cur_objix_spix) {
// load current object index (header) page
if (cur_objix_spix == 0) {
objix_pix = fd->objix_hdr_pix;
} else {
SPIFFS_DBG("read: find objix %04x:%04x\n", fd->obj_id, cur_objix_spix);
res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix);
SPIFFS_CHECK_RES(res);
}
SPIFFS_DBG("read: load objix page %04x:%04x for data spix:%04x\n", objix_pix, cur_objix_spix, data_spix);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res);
SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix);
fd->offset = cur_offset;
fd->cursor_objix_pix = objix_pix;
fd->cursor_objix_spix = cur_objix_spix;
prev_objix_spix = cur_objix_spix;
}
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];
} 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)];
}
// all remaining data
u32_t len_to_read = offset + len - cur_offset;
// remaining data in page
len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)));
// remaining data in file
len_to_read = MIN(len_to_read, fd->size);
SPIFFS_DBG("read: offset:%i rd:%i data spix:%04x is data_pix:%04x addr:%08x\n", cur_offset, len_to_read, data_spix, data_pix,
SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)));
if (len_to_read <= 0) {
res = SPIFFS_ERR_END_OF_OBJECT;
break;
}
res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
SPIFFS_CHECK_RES(res);
res = _spiffs_rd(
fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
fd->file_nbr,
SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)),
len_to_read,
dst);
SPIFFS_CHECK_RES(res);
dst += len_to_read;
cur_offset += len_to_read;
fd->offset = cur_offset;
data_spix++;
}
return res;
}
typedef struct {
spiffs_obj_id min_obj_id;
spiffs_obj_id max_obj_id;
u32_t compaction;
const u8_t *conflicting_name;
} spiffs_free_obj_id_state;
static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
u32_t user_data, void *user_p) {
if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) {
spiffs_obj_id min_obj_id = user_data;
u8_t *conflicting_name = (u8_t *)user_p;
// if conflicting name parameter is given, also check if this name is found in object index hdrs
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;
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);
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)) ==
(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) {
if (strncmp((char *)user_p, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN) == 0) {
return SPIFFS_ERR_CONFLICTING_NAME;
}
}
}
id &= ~SPIFFS_OBJ_ID_IX_FLAG;
u32_t bit_ix = (id-min_obj_id) & 7;
int byte_ix = (id-min_obj_id) >> 3;
if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) {
fs->work[byte_ix] |= (1<<bit_ix);
}
}
return SPIFFS_VIS_COUNTINUE;
}
static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry,
u32_t user_data, void *user_p) {
(void)user_data;
if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) {
s32_t res;
spiffs_free_obj_id_state *state = (spiffs_free_obj_id_state *)user_p;
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);
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))) {
// ok object look up entry
if (state->conflicting_name && strncmp((const char *)state->conflicting_name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN) == 0) {
return SPIFFS_ERR_CONFLICTING_NAME;
}
id &= ~SPIFFS_OBJ_ID_IX_FLAG;
if (id >= state->min_obj_id && id <= state->max_obj_id) {
u8_t *map = (u8_t *)fs->work;
int ix = (id - state->min_obj_id) / state->compaction;
//SPIFFS_DBG("free_obj_id: add ix %i for id %04x min:%04x max%04x comp:%i\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction);
map[ix]++;
}
}
}
return SPIFFS_VIS_COUNTINUE;
}
// Scans thru all object lookup for object index header pages. If total possible number of
// object ids cannot fit into a work buffer, these are grouped. When a group containing free
// object ids is found, the object lu is again scanned for object ids within group and bitmasked.
// Finally, the bitmasked is searched for a free id
s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, u8_t *conflicting_name) {
s32_t res = SPIFFS_OK;
u32_t max_objects = (SPIFFS_CFG_PHYS_SZ(fs) / (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) / 2;
spiffs_free_obj_id_state state;
spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE;
state.min_obj_id = 1;
state.max_obj_id = max_objects + 1;
if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) {
state.max_obj_id = ((spiffs_obj_id)-1) & ~SPIFFS_OBJ_ID_IX_FLAG;
}
state.compaction = 0;
state.conflicting_name = conflicting_name;
while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) {
if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8) {
// possible to represent in bitmap
u32_t i, j;
SPIFFS_DBG("free_obj_id: BITM min:%04x max:%04x\n", state.min_obj_id, state.max_obj_id);
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, state.min_obj_id,
conflicting_name, 0, 0);
if (res == SPIFFS_VIS_END) res = SPIFFS_OK;
SPIFFS_CHECK_RES(res);
// traverse bitmask until found free obj_id
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) {
u8_t mask = fs->work[i];
if (mask == 0xff) {
continue;
}
for (j = 0; j < 8; j++) {
if ((mask & (1<<j)) == 0) {
*obj_id = (i<<3)+j+state.min_obj_id;
return SPIFFS_OK;
}
}
}
return SPIFFS_ERR_FULL;
} else {
// not possible to represent all ids in range in a bitmap, compact and count
if (state.compaction != 0) {
// select element in compacted table, decrease range and recompact
u32_t i, min_i = 0;
u8_t *map = (u8_t *)fs->work;
u8_t min_count = 0xff;
for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(u8_t); i++) {
if (map[i] < min_count) {
min_count = map[i];
min_i = i;
if (min_count == 0) {
break;
}
}
}
if (min_count == state.compaction) {
// there are no free objids!
SPIFFS_DBG("free_obj_id: compacted table is full\n");
return SPIFFS_ERR_FULL;
}
SPIFFS_DBG("free_obj_id: COMP select index:%i min_count:%i min:%04x max:%04x compact:%i\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction);
if (min_count == 0) {
// no id in this range, skip compacting and use directly
*obj_id = min_i * state.compaction + state.min_obj_id;
return SPIFFS_OK;
} else {
SPIFFS_DBG("free_obj_id: COMP SEL chunk:%04x min:%04x -> %04x\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction);
state.min_obj_id += min_i * state.compaction;
state.max_obj_id = state.min_obj_id + state.compaction;
// decrease compaction
}
if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs)*8)) {
// no need for compacting, use bitmap
continue;
}
}
// in a work memory of log_page_size bytes, we may fit in log_page_size ids
// todo what if compaction is > 255 - then we cannot fit it in a byte
state.compaction = (state.max_obj_id-state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t)));
SPIFFS_DBG("free_obj_id: COMP min:%04x max:%04x compact:%i\n", state.min_obj_id, state.max_obj_id, state.compaction);
memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs));
res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, 0, &state, 0, 0);
if (res == SPIFFS_VIS_END) res = SPIFFS_OK;
SPIFFS_CHECK_RES(res);
state.conflicting_name = 0; // searched for conflicting name once, no need to do it again
}
}
return res;
}
s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd) {
u32_t i;
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
for (i = 0; i < fs->fd_count; i++) {
spiffs_fd *cur_fd = &fds[i];
if (cur_fd->file_nbr == 0) {
cur_fd->file_nbr = i+1;
*fd = cur_fd;
return SPIFFS_OK;
}
}
return SPIFFS_ERR_OUT_OF_FILE_DESCS;
}
s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) {
if (f <= 0 || f > (s16_t)fs->fd_count) {
return SPIFFS_ERR_BAD_DESCRIPTOR;
}
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
spiffs_fd *fd = &fds[f-1];
if (fd->file_nbr == 0) {
return SPIFFS_ERR_FILE_CLOSED;
}
fd->file_nbr = 0;
return SPIFFS_OK;
}
s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) {
if (f <= 0 || f > (s16_t)fs->fd_count) {
return SPIFFS_ERR_BAD_DESCRIPTOR;
}
spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
*fd = &fds[f-1];
if ((*fd)->file_nbr == 0) {
return SPIFFS_ERR_FILE_CLOSED;
}
return SPIFFS_OK;
}
/*
* spiffs_nucleus.h
*
* Created on: Jun 15, 2013
* Author: petera
*/
/* SPIFFS layout
*
* spiffs is designed for following spi flash characteristics:
* - only big areas of data (blocks) can be erased
* - erasing resets all bits in a block to ones
* - writing pulls ones to zeroes
* - zeroes cannot be pulled to ones, without erase
* - wear leveling
*
* spiffs is also meant to be run on embedded, memory constraint devices.
*
* Entire area is divided in blocks. Entire area is also divided in pages.
* Each block contains same number of pages. A page cannot be erased, but a
* block can be erased.
*
* Entire area must be block_size * x
* page_size must be block_size / (2^y) where y > 2
*
* ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes
*
* BLOCK 0 PAGE 0 object lookup 1
* PAGE 1 object lookup 2
* ...
* PAGE n-1 object lookup n
* PAGE n object data 1
* PAGE n+1 object data 2
* ...
* PAGE n+m-1 object data m
*
* BLOCK 1 PAGE n+m object lookup 1
* PAGE n+m+1 object lookup 2
* ...
* PAGE 2n+m-1 object lookup n
* PAGE 2n+m object data 1
* PAGE 2n+m object data 2
* ...
* PAGE 2n+2m-1 object data m
* ...
*
* n is number of object lookup pages, which is number of pages needed to index all pages
* in a block by object id
* : block_size / page_size * sizeof(obj_id) / page_size
* m is number data pages, which is number of pages in block minus number of lookup pages
* : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size
* thus, n+m is total number of pages in a block
* : block_size / page_size
*
* ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256
*
* Object lookup pages contain object id entries. Each entry represent the corresponding
* data page.
* Assuming a 16 bit object id, an object id being 0xffff represents a free page.
* An object id being 0x0000 represents a deleted page.
*
* ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff ..
* page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff ..
* page 2 : data : data for object id 0008
* page 3 : data : data for object id 0001
* page 4 : data : data for object id 0aaa
* ...
*
*
* Object data pages can be either object index pages or object content.
* All object data pages contains a data page header, containing object id and span index.
* The span index denotes the object page ordering amongst data pages with same object id.
* This applies to both object index pages (when index spans more than one page of entries),
* and object data pages.
* An object index page contains page entries pointing to object content page. The entry index
* in a object index page correlates to the span index in the actual object data page.
* The first object index page (span index 0) is called object index header page, and also
* contains object flags (directory/file), size, object name etc.
*
* ex:
* BLOCK 1
* PAGE 256: objectl lookup page 1
* [*123] [ 123] [ 123] [ 123]
* [ 123] [*123] [ 123] [ 123]
* [free] [free] [free] [free] ...
* PAGE 257: objectl lookup page 2
* [free] [free] [free] [free] ...
* PAGE 258: object index page (header)
* obj.id:0123 span.ix:0000 flags:INDEX
* size:1600 name:ex.txt type:file
* [259] [260] [261] [262]
* PAGE 259: object data page
* obj.id:0123 span.ix:0000 flags:DATA
* PAGE 260: object data page
* obj.id:0123 span.ix:0001 flags:DATA
* PAGE 261: object data page
* obj.id:0123 span.ix:0002 flags:DATA
* PAGE 262: object data page
* obj.id:0123 span.ix:0003 flags:DATA
* PAGE 263: object index page
* obj.id:0123 span.ix:0001 flags:INDEX
* [264] [265] [fre] [fre]
* [fre] [fre] [fre] [fre]
* PAGE 264: object data page
* obj.id:0123 span.ix:0004 flags:DATA
* PAGE 265: object data page
* obj.id:0123 span.ix:0005 flags:DATA
*
*/
#ifndef SPIFFS_NUCLEUS_H_
#define SPIFFS_NUCLEUS_H_
#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1)
#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1)
#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2)
#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3)
#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4)
#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20)
#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21)
#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22)
#define SPIFFS_EV_IX_UPD 0
#define SPIFFS_EV_IX_NEW 1
#define SPIFFS_EV_IX_DEL 2
#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1)))
#define SPIFFS_UNDEFINED_LEN (u32_t)(-1)
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
#define SPIFFS_MAGIC(fs) ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
#define SPIFFS_CONFIG_MAGIC (0x20090315)
#if SPIFFS_SINGLETON == 0
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \
((fs)->cfg.log_page_size)
#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \
((fs)->cfg.log_block_size)
#define SPIFFS_CFG_PHYS_SZ(fs) \
((fs)->cfg.phys_size)
#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \
((fs)->cfg.phys_erase_block)
#define SPIFFS_CFG_PHYS_ADDR(fs) \
((fs)->cfg.phys_addr)
#endif
// total number of pages
#define SPIFFS_MAX_PAGES(fs) \
( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// total number of pages per block, including object lookup pages
#define SPIFFS_PAGES_PER_BLOCK(fs) \
( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// number of object lookup pages per block
#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \
(MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) )
// checks if page index belongs to object lookup
#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \
(((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs))
// number of object lookup entries in all object lookup pages
#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \
(SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs))
// converts a block to physical address
#define SPIFFS_BLOCK_TO_PADDR(fs, block) \
( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) )
// converts a object lookup entry to page index
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \
((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry))
// converts a object lookup entry to physical address of corresponding page
#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \
(SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// converts a page to physical address
#define SPIFFS_PAGE_TO_PADDR(fs, page) \
( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// converts a physical address to page
#define SPIFFS_PADDR_TO_PAGE(fs, addr) \
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// gives index in page for a physical address
#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \
( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) )
// returns containing block for given page
#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \
( (page) / SPIFFS_PAGES_PER_BLOCK(fs) )
// returns starting page for block
#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \
( (block) * SPIFFS_PAGES_PER_BLOCK(fs) )
// converts page to entry in object lookup page
#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \
( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) )
// returns data size in a data page
#define SPIFFS_DATA_PAGE_SIZE(fs) \
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) )
// returns physical address for block's erase count,
// always in the physical last entry of the last object lookup page
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) )
// returns physical address for block's magic,
// always in the physical second last entry of the last object lookup page
#define SPIFFS_MAGIC_PADDR(fs, bix) \
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 )
// checks if there is any room for magic in the object luts
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) )
// define helpers object
// 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))
// 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))
// object index entry for given data span index
#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs)))
// object index span index number for given data span index or entry
#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \
((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs)))
#define SPIFFS_OP_T_OBJ_LU (0<<0)
#define SPIFFS_OP_T_OBJ_LU2 (1<<0)
#define SPIFFS_OP_T_OBJ_IX (2<<0)
#define SPIFFS_OP_T_OBJ_DA (3<<0)
#define SPIFFS_OP_C_DELE (0<<2)
#define SPIFFS_OP_C_UPDT (1<<2)
#define SPIFFS_OP_C_MOVS (2<<2)
#define SPIFFS_OP_C_MOVD (3<<2)
#define SPIFFS_OP_C_FLSH (4<<2)
#define SPIFFS_OP_C_READ (5<<2)
#define SPIFFS_OP_C_WRTHRU (6<<2)
#define SPIFFS_OP_TYPE_MASK (3<<0)
#define SPIFFS_OP_COM_MASK (7<<2)
// if 0, this page is written to, else clean
#define SPIFFS_PH_FLAG_USED (1<<0)
// if 0, writing is finalized, else under modification
#define SPIFFS_PH_FLAG_FINAL (1<<1)
// if 0, this is an index page, else a data page
#define SPIFFS_PH_FLAG_INDEX (1<<2)
// if 0, page is deleted, else valid
#define SPIFFS_PH_FLAG_DELET (1<<7)
// if 0, this index header is being deleted
#define SPIFFS_PH_FLAG_IXDELE (1<<6)
#define SPIFFS_CHECK_MOUNT(fs) \
((fs)->mounted != 0)
#define SPIFFS_CHECK_CFG(fs) \
((fs)->config_magic == SPIFFS_CONFIG_MAGIC)
#define SPIFFS_CHECK_RES(res) \
do { \
if ((res) < SPIFFS_OK) return (res); \
} while (0);
#define SPIFFS_API_CHECK_MOUNT(fs) \
if (!SPIFFS_CHECK_MOUNT((fs))) { \
(fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
return -1; \
}
#define SPIFFS_API_CHECK_CFG(fs) \
if (!SPIFFS_CHECK_CFG((fs))) { \
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
return -1; \
}
#define SPIFFS_API_CHECK_RES(fs, res) \
if ((res) < SPIFFS_OK) { \
(fs)->err_code = (res); \
return -1; \
}
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
if ((res) < SPIFFS_OK) { \
(fs)->err_code = (res); \
SPIFFS_UNLOCK(fs); \
return -1; \
}
#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \
if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \
if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH;
//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED;
#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \
if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \
if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \
if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \
if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \
if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \
if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH;
// check id
#define SPIFFS_VIS_CHECK_ID (1<<0)
// report argument object id to visitor - else object lookup id is reported
#define SPIFFS_VIS_CHECK_PH (1<<1)
// stop searching at end of all look up pages
#define SPIFFS_VIS_NO_WRAP (1<<2)
#if SPIFFS_CACHE
#define SPIFFS_CACHE_FLAG_DIRTY (1<<0)
#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1)
#define SPIFFS_CACHE_FLAG_OBJLU (1<<2)
#define SPIFFS_CACHE_FLAG_OBJIX (1<<3)
#define SPIFFS_CACHE_FLAG_DATA (1<<4)
#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7)
#define SPIFFS_CACHE_PAGE_SIZE(fs) \
(sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs))
#define spiffs_get_cache(fs) \
((spiffs_cache *)((fs)->cache))
#define spiffs_get_cache_page_hdr(fs, c, ix) \
((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])))
#define spiffs_get_cache_page(fs, c, ix) \
((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page))
// cache page struct
typedef struct {
// cache flags
u8_t flags;
// cache page index
u8_t ix;
// last access of this cache page
u32_t last_access;
union {
// type read cache
struct {
// read cache page index
spiffs_page_ix pix;
};
#if SPIFFS_CACHE_WR
// type write cache
struct {
// write cache
spiffs_obj_id obj_id;
// offset in cache page
u32_t offset;
// size of cache page
u16_t size;
};
#endif
};
} spiffs_cache_page;
// cache struct
typedef struct {
u8_t cpage_count;
u32_t last_access;
u32_t cpage_use_map;
u32_t cpage_use_mask;
u8_t *cpages;
} spiffs_cache;
#endif
// spiffs nucleus file descriptor
typedef struct {
// the filesystem of this descriptor
spiffs *fs;
// number of file descriptor - if 0, the file descriptor is closed
spiffs_file file_nbr;
// object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted
spiffs_obj_id obj_id;
// size of the file
u32_t size;
// cached object index header page index
spiffs_page_ix objix_hdr_pix;
// cached offset object index page index
spiffs_page_ix cursor_objix_pix;
// cached offset object index span index
spiffs_span_ix cursor_objix_spix;
// current absolute offset
u32_t offset;
// current file descriptor offset
u32_t fdoffset;
// fd flags
spiffs_flags flags;
#if SPIFFS_CACHE_WR
spiffs_cache_page *cache_page;
#endif
} spiffs_fd;
// object structs
// page header, part of each page except object lookup pages
// NB: this is always aligned when the data page is an object index,
// as in this case struct spiffs_page_object_ix is used
typedef struct __attribute(( packed )) {
// object id
spiffs_obj_id obj_id;
// object span index
spiffs_span_ix span_ix;
// flags
u8_t flags;
} spiffs_page_header;
// object index header page header
typedef struct __attribute(( packed ))
{
// common page header
spiffs_page_header p_hdr;
// size of object
u32_t size;
// type of object
spiffs_obj_type type;
// name of object
u8_t name[SPIFFS_OBJ_NAME_LEN];
} spiffs_page_object_ix_header;
// object index page header
typedef struct __attribute(( packed )) {
spiffs_page_header p_hdr;
} spiffs_page_object_ix;
// 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,
u32_t user_data, void *user_p);
#if SPIFFS_CACHE
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst))
#define _spiffs_wr(fs, op, fh, addr, len, src) \
spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src))
#else
#define _spiffs_rd(fs, op, fh, addr, len, dst) \
spiffs_phys_rd((fs), (addr), (len), (dst))
#define _spiffs_wr(fs, op, fh, addr, len, src) \
spiffs_phys_wr((fs), (addr), (len), (src))
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
// ---------------
s32_t spiffs_phys_rd(
spiffs *fs,
#if SPIFFS_CACHE
u8_t op,
spiffs_file fh,
#endif
u32_t addr,
u32_t len,
u8_t *dst);
s32_t spiffs_phys_wr(
spiffs *fs,
#if SPIFFS_CACHE
u8_t op,
spiffs_file fh,
#endif
u32_t addr,
u32_t len,
u8_t *src);
s32_t spiffs_phys_cpy(
spiffs *fs,
spiffs_file fh,
u32_t dst,
u32_t src,
u32_t len);
s32_t spiffs_phys_count_free_blocks(
spiffs *fs);
s32_t spiffs_obj_lu_find_entry_visitor(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
u8_t flags,
spiffs_obj_id obj_id,
spiffs_visitor_f v,
u32_t user_data,
void *user_p,
spiffs_block_ix *block_ix,
int *lu_entry);
s32_t spiffs_erase_block(
spiffs *fs,
spiffs_block_ix bix);
// ---------------
s32_t spiffs_obj_lu_scan(
spiffs *fs);
s32_t spiffs_obj_lu_find_free_obj_id(
spiffs *fs,
spiffs_obj_id *obj_id,
u8_t *conflicting_name);
s32_t spiffs_obj_lu_find_free(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
spiffs_block_ix *block_ix,
int *lu_entry);
s32_t spiffs_obj_lu_find_id(
spiffs *fs,
spiffs_block_ix starting_block,
int starting_lu_entry,
spiffs_obj_id obj_id,
spiffs_block_ix *block_ix,
int *lu_entry);
s32_t spiffs_obj_lu_find_id_and_span(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix exclusion_pix,
spiffs_page_ix *pix);
s32_t spiffs_obj_lu_find_id_and_span_by_phdr(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix exclusion_pix,
spiffs_page_ix *pix);
// ---------------
s32_t spiffs_page_allocate_data(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_page_header *ph,
u8_t *data,
u32_t len,
u32_t page_offs,
u8_t finalize,
spiffs_page_ix *pix);
s32_t spiffs_page_move(
spiffs *fs,
spiffs_file fh,
u8_t *page_data,
spiffs_obj_id obj_id,
spiffs_page_header *page_hdr,
spiffs_page_ix src_pix,
spiffs_page_ix *dst_pix);
s32_t spiffs_page_delete(
spiffs *fs,
spiffs_page_ix pix);
// ---------------
s32_t spiffs_object_create(
spiffs *fs,
spiffs_obj_id obj_id,
u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_obj_type type,
spiffs_page_ix *objix_hdr_pix);
s32_t spiffs_object_update_index_hdr(
spiffs *fs,
spiffs_fd *fd,
spiffs_obj_id obj_id,
spiffs_page_ix objix_hdr_pix,
u8_t *new_objix_hdr_data,
u8_t name[SPIFFS_OBJ_NAME_LEN],
u32_t size,
spiffs_page_ix *new_pix);
void spiffs_cb_object_event(
spiffs *fs,
spiffs_fd *fd,
int ev,
spiffs_obj_id obj_id,
spiffs_span_ix spix,
spiffs_page_ix new_pix,
u32_t new_size);
s32_t spiffs_object_open_by_id(
spiffs *fs,
spiffs_obj_id obj_id,
spiffs_fd *f,
spiffs_flags flags,
spiffs_mode mode);
s32_t spiffs_object_open_by_page(
spiffs *fs,
spiffs_page_ix pix,
spiffs_fd *f,
spiffs_flags flags,
spiffs_mode mode);
s32_t spiffs_object_append(
spiffs_fd *fd,
u32_t offset,
u8_t *data,
u32_t len);
s32_t spiffs_object_modify(
spiffs_fd *fd,
u32_t offset,
u8_t *data,
u32_t len);
s32_t spiffs_object_read(
spiffs_fd *fd,
u32_t offset,
u32_t len,
u8_t *dst);
s32_t spiffs_object_truncate(
spiffs_fd *fd,
u32_t new_len,
u8_t remove_object);
s32_t spiffs_object_find_object_index_header_by_name(
spiffs *fs,
u8_t name[SPIFFS_OBJ_NAME_LEN],
spiffs_page_ix *pix);
// ---------------
s32_t spiffs_gc_check(
spiffs *fs,
u32_t len);
s32_t spiffs_gc_erase_page_stats(
spiffs *fs,
spiffs_block_ix bix);
s32_t spiffs_gc_find_candidate(
spiffs *fs,
spiffs_block_ix **block_candidate,
int *candidate_count,
char fs_crammed);
s32_t spiffs_gc_clean(
spiffs *fs,
spiffs_block_ix bix);
s32_t spiffs_gc_quick(
spiffs *fs, u16_t max_free_pages);
// ---------------
s32_t spiffs_fd_find_new(
spiffs *fs,
spiffs_fd **fd);
s32_t spiffs_fd_return(
spiffs *fs,
spiffs_file f);
s32_t spiffs_fd_get(
spiffs *fs,
spiffs_file f,
spiffs_fd **fd);
#if SPIFFS_CACHE
void spiffs_cache_init(
spiffs *fs);
void spiffs_cache_drop_page(
spiffs *fs,
spiffs_page_ix pix);
#if SPIFFS_CACHE_WR
spiffs_cache_page *spiffs_cache_page_allocate_by_fd(
spiffs *fs,
spiffs_fd *fd);
void spiffs_cache_fd_release(
spiffs *fs,
spiffs_cache_page *cp);
spiffs_cache_page *spiffs_cache_page_get_by_fd(
spiffs *fs,
spiffs_fd *fd);
#endif
#endif
s32_t spiffs_lookup_consistency_check(
spiffs *fs,
u8_t check_all_objects);
s32_t spiffs_page_consistency_check(
spiffs *fs);
s32_t spiffs_object_index_consistency_check(
spiffs *fs);
#endif /* SPIFFS_NUCLEUS_H_ */
#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)
}
COMPONENT_ADD_INCLUDEDIRS:=include
include $(IDF_PATH)/make/component_common.mk
#ifndef _TASK_H_
#define _TASK_H_
#include <stdint.h>
#include <stdbool.h>
#include <c_types.h>
/* use LOW / MEDIUM / HIGH since it isn't clear from the docs which is higher */
typedef enum {
TASK_PRIORITY_LOW,
TASK_PRIORITY_MEDIUM,
TASK_PRIORITY_HIGH,
TASK_PRIORITY_COUNT
} task_prio_t;
typedef uint32_t task_handle_t;
typedef intptr_t task_param_t;
/*
* Signals are a 32-bit number of the form header:14; count:18. The header
* is just a fixed fingerprint and the count is allocated serially by the
* task_get_id() function.
*/
bool task_post(task_prio_t priority, task_handle_t handle, task_param_t param);
#define task_post_low(handle,param) task_post(TASK_PRIORITY_LOW, handle, param)
#define task_post_medium(handle,param) task_post(TASK_PRIORITY_MEDIUM, handle, param)
#define task_post_high(handle,param) task_post(TASK_PRIORITY_HIGH, handle, param)
typedef void (*task_callback_t)(task_param_t param, task_prio_t prio);
bool task_init_handler(task_prio_t priority, uint8 qlen);
task_handle_t task_get_id(task_callback_t t);
/* RTOS loop to pump task messages until infinity */
void task_pump_messages (void);
#endif
/**
This file encapsulates the SDK-based task handling for the NodeMCU Lua firmware.
*/
#include "task/task.h"
#include "mem.h"
#include <stdlib.h>
#include <string.h>
#include "freertos/queue.h"
#include "freertos/semphr.h"
#define TASK_HANDLE_MONIKER 0x68680000
#define TASK_HANDLE_MASK 0xFFF80000
#define TASK_HANDLE_UNMASK (~TASK_HANDLE_MASK)
#define TASK_HANDLE_ALLOCATION_BRICK 4 // must be a power of 2
#define TASK_DEFAULT_QUEUE_LEN 8
#define CHECK(p,v,msg) if (!(p)) { NODE_DBG ( msg ); return (v); }
#ifndef NODE_DBG
# define NODE_DBG(...) do{}while(0)
#endif
typedef struct
{
task_handle_t sig;
task_param_t par;
} task_event_t;
/*
* Private arrays to hold the 3 event task queues and the dispatch callbacks
*/
static xQueueHandle task_Q[TASK_PRIORITY_COUNT];
/* Rather than using a QueueSet (which requires queues to be empty when created)
* we use a binary semaphore to unblock the pump whenever something is posted */
static xSemaphoreHandle pending;
static task_callback_t *task_func;
static int task_count;
/*
* Initialise the task handle callback for a given priority. This doesn't need
* to be called explicitly as the get_id function will call this lazily.
*/
bool task_init_handler(task_prio_t priority, uint8 qlen) {
if (priority >= TASK_PRIORITY_COUNT)
return false;
if (task_Q[priority] == NULL)
{
task_Q[priority] = xQueueCreate (qlen, sizeof (task_event_t));
return task_Q[priority] != NULL;
}
else
return false;
}
task_handle_t task_get_id(task_callback_t t) {
/* Initialise any uninitialised Qs with the default Q len */
for (task_prio_t p = TASK_PRIORITY_LOW; p != TASK_PRIORITY_COUNT; ++p)
{
if (!task_Q[p]) {
CHECK(task_init_handler( p, TASK_DEFAULT_QUEUE_LEN ), 0, "Task initialisation failed");
}
}
if ( (task_count & (TASK_HANDLE_ALLOCATION_BRICK - 1)) == 0 ) {
/* With a brick size of 4 this branch is taken at 0, 4, 8 ... and the new size is +4 */
task_func =(task_callback_t *)realloc(
task_func,
sizeof(task_callback_t)*(task_count+TASK_HANDLE_ALLOCATION_BRICK));
CHECK(task_func, 0 , "Malloc failure in task_get_id");
memset (task_func+task_count, 0, sizeof(task_callback_t)*TASK_HANDLE_ALLOCATION_BRICK);
}
task_func[task_count] = t;
return TASK_HANDLE_MONIKER | task_count++;
}
bool task_post (task_prio_t priority, task_handle_t handle, task_param_t param)
{
if (priority >= TASK_PRIORITY_COUNT ||
!task_Q[priority] ||
(handle & TASK_HANDLE_MASK) != TASK_HANDLE_MONIKER)
return false;
task_event_t ev = { handle, param };
bool res = pdPASS == xQueueSendToBackFromISR (task_Q[priority], &ev, NULL);
if (pending) /* only need to raise semaphore if it's been initialised */
xSemaphoreGiveFromISR (pending, NULL);
return res;
}
static bool next_event (task_event_t *ev, task_prio_t *prio)
{
for (task_prio_t pr = TASK_PRIORITY_COUNT; pr != TASK_PRIORITY_LOW; --pr)
{
task_prio_t p = pr -1;
if (task_Q[p] && xQueueReceive (task_Q[p], ev, 0) == pdTRUE)
{
*prio = p;
return true;
}
}
return false; // no events queued
}
static void dispatch (task_event_t *e, uint8_t prio) {
task_handle_t handle = e->sig;
if ( (handle & TASK_HANDLE_MASK) == TASK_HANDLE_MONIKER) {
uint16_t entry = (handle & TASK_HANDLE_UNMASK);
if ( task_func && entry < task_count ){
/* call the registered task handler with the specified parameter and priority */
task_func[entry](e->par, prio);
return;
}
}
/* Invalid signals are ignored */
NODE_DBG ( "Invalid signal issued: %08x", handle);
}
void task_pump_messages (void)
{
vSemaphoreCreateBinary (pending);
for (;;)
{
task_event_t ev;
task_prio_t prio;
if (next_event (&ev, &prio))
dispatch (&ev, prio);
else
xSemaphoreTake (pending, portMAX_DELAY);
}
}
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void pingTask(void *pvParameters)
{
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
printf("ping\n");
}
}
void app_main()
{
xTaskCreatePinnedToCore(&pingTask, "pingTask", 2048, NULL, 5, NULL, 0);
}
include $(IDF_PATH)/make/component_common.mk
//#include "user_interface.h"
#include "user_config.h"
#ifdef LUA_CROSS_COMPILER
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define true 1
#define false 0
#else
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
const char *lua_init_value = "@init.lua";
const char *c_getenv(const char *__string)
{
if (strcmp(__string, "LUA_INIT") == 0)
{
return lua_init_value;
}
return NULL;
}
#endif
const double powersOf10[] = /* Table giving binary powers of 10. Entry */
{
10., /* is 10^2^i. Used to convert decimal */
100., /* exponents into floating-point numbers. */
1.0e4,
1.0e8,
1.0e16,
1.0e32,
1.0e64,
1.0e128,
1.0e256
};
double c_strtod(const char *string, char **endPtr)
{
int maxExponent = 511; /* Largest possible base 10 exponent. Any
* exponent larger than this will already
* produce underflow or overflow, so there's
* no need to worry about additional digits.
*/
int sign, expSign = false;
double fraction, dblExp;
const double *d;
register const char *p;
register int c;
int exp = 0; /* Exponent read from "EX" field. */
int fracExp = 0; /* Exponent that derives from the fractional
* part. Under normal circumstatnces, it is
* the negative of the number of digits in F.
* However, if I is very long, the last digits
* of I get dropped (otherwise a long I with a
* large negative exponent could cause an
* unnecessary overflow on I alone). In this
* case, fracExp is incremented one for each
* dropped digit. */
int mantSize; /* Number of digits in mantissa. */
int decPt; /* Number of mantissa digits BEFORE decimal
* point. */
const char *pExp; /* Temporarily holds location of exponent
* in string. */
/*
* Strip off leading blanks and check for a sign.
*/
p = string;
while (isspace((unsigned char)(*p)))
{
p += 1;
}
if (*p == '-')
{
sign = true;
p += 1;
}
else
{
if (*p == '+')
{
p += 1;
}
sign = false;
}
/*
* Count the number of digits in the mantissa (including the decimal
* point), and also locate the decimal point.
*/
decPt = -1;
for (mantSize = 0; ; mantSize += 1)
{
c = *p;
if (!isdigit(c))
{
if ((c != '.') || (decPt >= 0))
{
break;
}
decPt = mantSize;
}
p += 1;
}
/*
* Now suck up the digits in the mantissa. Use two integers to
* collect 9 digits each (this is faster than using floating-point).
* If the mantissa has more than 18 digits, ignore the extras, since
* they can't affect the value anyway.
*/
pExp = p;
p -= mantSize;
if (decPt < 0)
{
decPt = mantSize;
}
else
{
mantSize -= 1; /* One of the digits was the point. */
}
if (mantSize > 18)
{
fracExp = decPt - 18;
mantSize = 18;
}
else
{
fracExp = decPt - mantSize;
}
if (mantSize == 0)
{
fraction = 0.0;
p = string;
goto done;
}
else
{
int frac1, frac2;
frac1 = 0;
for ( ; mantSize > 9; mantSize -= 1)
{
c = *p;
p += 1;
if (c == '.')
{
c = *p;
p += 1;
}
frac1 = 10 * frac1 + (c - '0');
}
frac2 = 0;
for (; mantSize > 0; mantSize -= 1)
{
c = *p;
p += 1;
if (c == '.')
{
c = *p;
p += 1;
}
frac2 = 10 * frac2 + (c - '0');
}
fraction = (1.0e9 * frac1) + frac2;
}
/*
* Skim off the exponent.
*/
p = pExp;
if ((*p == 'E') || (*p == 'e'))
{
p += 1;
if (*p == '-')
{
expSign = true;
p += 1;
}
else
{
if (*p == '+')
{
p += 1;
}
expSign = false;
}
if (!isdigit((unsigned char)(*p)))
{
p = pExp;
goto done;
}
while (isdigit((unsigned char)(*p)))
{
exp = exp * 10 + (*p - '0');
p += 1;
}
}
if (expSign)
{
exp = fracExp - exp;
}
else
{
exp = fracExp + exp;
}
/*
* Generate a floating-point number that represents the exponent.
* Do this by processing the exponent one bit at a time to combine
* many powers of 2 of 10. Then combine the exponent with the
* fraction.
*/
if (exp < 0)
{
expSign = true;
exp = -exp;
}
else
{
expSign = false;
}
if (exp > maxExponent)
{
exp = maxExponent;
// errno = ERANGE;
}
dblExp = 1.0;
for (d = powersOf10; exp != 0; exp >>= 1, d += 1)
{
if (exp & 01)
{
dblExp *= *d;
}
}
if (expSign)
{
fraction /= dblExp;
}
else
{
fraction *= dblExp;
}
done:
if (endPtr != NULL)
{
*endPtr = (char *) p;
}
if (sign)
{
return -fraction;
}
return fraction;
}
include $(IDF_PATH)/make/component_common.mk
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