in fs/spiffs/src/spiffs_core.c [1722:2283]
ssize_t spiffs_fobj_append(FAR struct spiffs_s *fs,
FAR struct spiffs_file_s *fobj, off_t offset,
FAR uint8_t *data, size_t len)
{
struct spiffs_page_header_s phdr;
FAR struct spiffs_pgobj_ndxheader_s *objhdr;
FAR struct spiffs_page_objndx_s *objndx;
ssize_t nwritten = 0;
uint32_t page_offs;
int16_t cur_objndx_spndx;
int16_t prev_objndx_spndx;
int16_t cur_objndx_pgndx;
int16_t new_objhdr_page;
int16_t data_spndx;
int16_t data_page;
int ret = OK;
int ret2;
finfo("Append %zu bytes @ offs=%" PRIdOFF " of size=%" PRIdOFF "\n",
len, offset, fobj->size);
if (offset > fobj->size)
{
finfo("Offset replaced with size\n");
offset = fobj->size < 0 ? 0 : fobj->size;
}
/* Add an extra page of data worth for meta */
ret = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
if (ret < 0)
{
ferr("ERROR: spiffs_gc_epage_stats() failed: %d\n", ret);
return ret;
}
objhdr = (FAR struct spiffs_pgobj_ndxheader_s *)fs->work;
objndx = (FAR struct spiffs_page_objndx_s *)fs->work;
cur_objndx_spndx = 0;
prev_objndx_spndx = (int16_t)-1;
cur_objndx_pgndx = fobj->objhdr_pgndx;
data_spndx = offset / SPIFFS_DATA_PAGE_SIZE(fs);
page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs);
/* Write all data */
while (ret >= 0 && nwritten < len)
{
size_t to_write;
/* Calculate object index page span index */
cur_objndx_spndx = SPIFFS_OBJNDX_ENTRY_SPNDX(fs, data_spndx);
/* Handle storing and loading of object indices */
if (cur_objndx_spndx != prev_objndx_spndx)
{
/* New object index page. Within this clause we return directly
* if something fails, object index mess-up
*/
if (nwritten > 0)
{
/* Store previous object index page, unless first pass */
finfo("objid=%04x store objndx %04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, prev_objndx_spndx,
nwritten);
if (prev_objndx_spndx == 0)
{
/* This is an update to object index header page */
objhdr->size = offset + nwritten;
if (offset == 0)
{
/* Was an empty object, update same page (size was
* 0xffffffff)
*/
ret = spiffs_page_index_check(fs, fobj,
cur_objndx_pgndx, 0);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: "
"%d\n",
ret);
return ret;
}
ret =
spiffs_cache_write(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs),
fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n",
ret);
return ret;
}
}
else
{
/* Was a nonempty object, update to new page */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx,
fs->work, 0,
offset + nwritten,
&new_objhdr_page);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: "
"%d\n",
ret);
return ret;
}
finfo("objid=%04x store new objhdr, "
"%04x:%04x, nwritten=%zu\n",
fobj->objid, new_objhdr_page, 0, nwritten);
}
}
else
{
/* this is an update to an object index page */
ret = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
prev_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: "
"%d\n", ret);
return ret;
}
ret = spiffs_cache_write(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs),
fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret);
return ret;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)
fs->work,
SPIFFS_EV_NDXUPD, fobj->objid,
objndx->phdr.spndx, cur_objndx_pgndx,
0);
/* Update length in object index header page */
ret = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, 0, 0,
offset + nwritten,
&new_objhdr_page);
if (ret < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret);
return ret;
}
finfo("objid=%04x store new size I %" PRIdOFF
" in objhdr, %04x:%04x, nwritten=%zu\n",
fobj->objid, offset + nwritten,
new_objhdr_page, 0,
nwritten);
}
fobj->size = offset + nwritten;
fobj->offset = offset + nwritten;
}
/* create or load new object index page */
if (cur_objndx_spndx == 0)
{
/* load object index header page, must always exist */
finfo("objid=%04x load objndxhdr page %04x:%04x\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs,
cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objhdr->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n", ret);
return ret;
}
}
else
{
int16_t len_objndx_spndx;
/* On subsequent passes, create a new object index page */
len_objndx_spndx =
SPIFFS_OBJNDX_ENTRY_SPNDX(fs, (fobj->size - 1) /
SPIFFS_DATA_PAGE_SIZE(fs));
if (nwritten > 0 || cur_objndx_spndx > len_objndx_spndx)
{
phdr.objid = fobj->objid | SPIFFS_OBJID_NDXFLAG;
phdr.spndx = cur_objndx_spndx;
phdr.flags =
0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX);
ret = spiffs_page_allocate_data(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
&phdr, 0, 0, 0, 1,
&cur_objndx_pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_allocate_data() failed: %d\n",
ret);
return ret;
}
/* Quick "load" of new object index page */
memset(fs->work, 0xff, SPIFFS_GEO_PAGE_SIZE(fs));
memcpy(fs->work, &phdr,
sizeof(struct spiffs_page_header_s));
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)
fs->work,
SPIFFS_EV_NDXNEW, fobj->objid,
cur_objndx_spndx, cur_objndx_pgndx, 0);
finfo("objid=%04x create objndx page, "
"%04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx,
nwritten);
}
else
{
int16_t pgndx;
/* On first pass, we load existing object index page */
finfo("objid=%04x find objndx spndx=%04x\n",
fobj->objid, cur_objndx_spndx);
if (fobj->objndx_spndx == cur_objndx_spndx)
{
pgndx = fobj->objndx_pgndx;
}
else
{
ret =
spiffs_objlu_find_id_and_span(fs,
fobj->objid |
SPIFFS_OBJID_NDXFLAG,
cur_objndx_spndx, 0,
&pgndx);
if (ret < 0)
{
ferr("ERROR: spiffs_objlu_find_id_and_span() "
"failed: %d\n",
ret);
return ret;
}
}
finfo("objid=%04x found object index at "
"page=%04x [fobj size=%" PRIuOFF "]\n",
fobj->objid, pgndx, fobj->size);
ret = spiffs_cache_read(fs,
SPIFFS_OP_T_OBJNDX |
SPIFFS_OP_C_READ,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, pgndx),
SPIFFS_GEO_PAGE_SIZE(fs),
fs->work);
if (ret < 0)
{
ferr("ERROR: spiffs_cache_read() failed: %d\n", ret);
return ret;
}
ret = spiffs_validate_objndx(&objhdr->phdr, fobj->objid,
cur_objndx_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_validate_objndx() failed: %d\n",
ret);
return ret;
}
cur_objndx_pgndx = pgndx;
}
fobj->objndx_pgndx = cur_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
fobj->offset = offset + nwritten;
fobj->size = offset + nwritten;
}
prev_objndx_spndx = cur_objndx_spndx;
}
/* Write data */
to_write = MIN(len - nwritten, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs);
if (page_offs == 0)
{
/* ATt beginning of a page, allocate and write a new page of data */
phdr.objid = fobj->objid & ~SPIFFS_OBJID_NDXFLAG;
phdr.spndx = data_spndx;
phdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); /* Finalize immediately */
ret = spiffs_page_allocate_data(fs,
fobj->objid &
~SPIFFS_OBJID_NDXFLAG,
&phdr, &data[nwritten], to_write,
page_offs, 1, &data_page);
finfo("objid=%04x store new data page, %04x:%04x "
"offset=%" PRId32 ", len=%zu nwritten=%zu\n",
fobj->objid, data_page, data_spndx, page_offs, to_write,
nwritten);
}
else
{
/* Append to existing page, fill out free data in existing page */
if (cur_objndx_spndx == 0)
{
/* Get data page from object index header page */
data_page =
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx];
}
else
{
/* Get data page from object index page */
data_page =
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)];
}
ret = spiffs_page_data_check(fs, fobj, data_page, data_spndx);
if (ret < 0)
{
ferr("ERROR: spiffs_page_data_check() failed: %d\n", ret);
return ret;
}
ret = spiffs_cache_write(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, data_page) +
sizeof(struct spiffs_page_header_s) +
page_offs,
to_write, &data[nwritten]);
finfo("objid=%04x store to existing data page, "
"%04x:%04x offset=%" PRId32 ", "
"len=%zu, nwritten=%zu\n",
fobj->objid, data_page, data_spndx, page_offs, to_write,
nwritten);
}
if (ret < 0)
{
break;
}
/* Update memory representation of object index page with new data
* page
*/
if (cur_objndx_spndx == 0)
{
/* Update object index header page */
((FAR int16_t *)((FAR uint8_t *)objhdr +
sizeof(struct spiffs_pgobj_ndxheader_s)))[data_spndx] =
data_page;
finfo("objid=%04x wrote page %04x to objhdr entry=%04x in mem\n",
fobj->objid, data_page, data_spndx);
objhdr->size = offset + nwritten;
}
else
{
/* Update object index page */
((FAR int16_t *)((FAR uint8_t *)objndx +
sizeof(struct spiffs_page_objndx_s)))
[SPIFFS_OBJNDX_ENTRY(fs, data_spndx)] = data_page;
finfo("objid=%04x wrote page=%04x to objndx entry %04x in mem\n",
fobj->objid, data_page,
(int16_t)SPIFFS_OBJNDX_ENTRY(fs, data_spndx));
}
/* Update internals */
page_offs = 0;
data_spndx++;
nwritten += to_write;
}
fobj->size = offset + nwritten;
fobj->offset = offset + nwritten;
fobj->objndx_pgndx = cur_objndx_pgndx;
fobj->objndx_spndx = cur_objndx_spndx;
/* Finalize updated object indices */
ret2 = OK;
if (cur_objndx_spndx != 0)
{
/* Wrote beyond object index header page. Write last modified object
* index page, unless object header index page
*/
finfo("objid=%04x store objndx page, %04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx, nwritten);
ret2 = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
cur_objndx_spndx);
if (ret2 < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n", ret2);
return ret2;
}
ret2 = spiffs_cache_write(fs, SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs, cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret2 < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret2);
return ret2;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)fs->work,
SPIFFS_EV_NDXUPD, fobj->objid,
objndx->phdr.spndx, cur_objndx_pgndx, 0);
/* Update size in object header index page */
ret2 = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, 0, 0,
offset + nwritten,
&new_objhdr_page);
finfo("objid=%04x store new size II %" PRIdOFF
" in objhdr, %04x:%04x, nwritten=%zu, ret=%d\n",
fobj->objid, offset + nwritten,
new_objhdr_page, 0, nwritten,
ret2);
if (ret2 < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret2);
return ret2;
}
}
else
{
/* Wrote within object index header page */
if (offset == 0)
{
/* Wrote to empty object - simply update size and write whole
* page
*/
objhdr->size = offset + nwritten;
finfo("objid=%04x store fresh objhdr page, "
"%04x:%04x, nwritten=%zu\n",
fobj->objid, cur_objndx_pgndx, cur_objndx_spndx, nwritten);
ret2 = spiffs_page_index_check(fs, fobj, cur_objndx_pgndx,
cur_objndx_spndx);
if (ret2 < 0)
{
ferr("ERROR: spiffs_page_index_check() failed: %d\n", ret2);
return ret2;
}
ret2 = spiffs_cache_write(fs,
SPIFFS_OP_T_OBJNDX | SPIFFS_OP_C_UPDT,
fobj->objid,
SPIFFS_PAGE_TO_PADDR(fs,
cur_objndx_pgndx),
SPIFFS_GEO_PAGE_SIZE(fs), fs->work);
if (ret2 < 0)
{
ferr("ERROR: spiffs_cache_write() failed: %d\n", ret2);
return ret2;
}
spiffs_fobj_event(fs,
(FAR struct spiffs_page_objndx_s *)fs->work,
SPIFFS_EV_NDXUPD_HDR, fobj->objid,
objhdr->phdr.spndx, cur_objndx_pgndx,
objhdr->size);
}
else
{
/* Modifying object index header page, update size and make new
* copy.
*/
ret2 = spiffs_fobj_update_ndxhdr(fs, fobj, fobj->objid,
fobj->objhdr_pgndx, fs->work, 0,
offset + nwritten,
&new_objhdr_page);
finfo("objid=%04x store modified objhdr page, %04x:%04x, "
"nwritten=%zu\n",
fobj->objid, new_objhdr_page, 0, nwritten);
if (ret2 < 0)
{
ferr("ERROR: spiffs_fobj_update_ndxhdr() failed: %d\n",
ret2);
return ret2;
}
}
}
return nwritten;
}